roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @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  * usage:
4913  * document.undoManager = new UndoManager(limit, document)
4914  *  
4915  *
4916  *  TOTALLY UNTESTED...
4917  *
4918  *  Documentation to be done....
4919  */
4920
4921
4922
4923 /**
4924 * @class Roo.lib.UndoManager
4925 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4926 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4927
4928 * Usage:
4929 <pre><code>
4930 document.undoManager = new UndoManager(limit, document)
4931  
4932 </code></pre>
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.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4945
4946 };
4947         
4948 Roo.lib.UndoManager.prototype = {
4949     
4950     
4951     stack : false,
4952     
4953     fireEvent : false,
4954     position : 0,
4955     length : 0,
4956     
4957     
4958      /**
4959      * To push and execute a transaction, the method undoManager.transact
4960      * must be called by passing a transaction object as the first argument, and a merge
4961      * flag as the second argument. A transaction object has the following properties:
4962      *
4963      * Usage:
4964 <pre><code>
4965 undoManager.transact({
4966     label: 'Typing',
4967     execute: function() { ... },
4968     undo: function() { ... },
4969     // redo same as execute
4970     redo: function() { this.execute(); }
4971 }, false);
4972
4973 // merge transaction
4974 undoManager.transact({
4975     label: 'Typing',
4976     execute: function() { ... },  // this will be run...
4977     undo: function() { ... }, // what to do when undo is run.
4978     // redo same as execute
4979     redo: function() { this.execute(); }
4980 }, true); 
4981 </code></pre> 
4982      *
4983      * 
4984      * @param {Object} transaction The transaction to add to the stack.
4985      * @return {String} The HTML fragment
4986      */
4987     
4988     
4989     transact : function (transaction, merge)
4990     {
4991         if (arguments.length < 2) {
4992             throw new TypeError('Not enough arguments to UndoManager.transact.');
4993         }
4994
4995         transaction.execute();
4996
4997         this.stack.splice(0, this.position);
4998         if (merge && this.length) {
4999             this.stack[0].push(transaction);
5000         } else {
5001             this.stack.unshift([transaction]);
5002         }
5003     
5004         this.position = 0;
5005
5006         if (limit && this.stack.length > limit) {
5007             this.length = this.stack.length = limit;
5008         } else {
5009             this.length = this.stack.length;
5010         }
5011
5012         if (this.fireEvent) {
5013             undoScopeHost.dispatchEvent(
5014                 new CustomEvent('DOMTransaction', {
5015                     detail: {
5016                         transactions: this.stack[0].slice()
5017                     },
5018                     bubbles: true,
5019                     cancelable: false
5020                 })
5021             );
5022         }
5023     },
5024
5025     undo : function ()
5026     {
5027         if (this.position < this.length) {
5028             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5029                 this.stack[this.position][i].undo();
5030             }
5031             this.position++;
5032
5033             if (this.fireEvent) {
5034                 undoScopeHost.dispatchEvent(
5035                     new CustomEvent('undo', {
5036                         detail: {
5037                             transactions: this.stack[this.position - 1].slice()
5038                         },
5039                         bubbles: true,
5040                         cancelable: false
5041                     })
5042                 );
5043             }
5044         }
5045     },
5046
5047     redo : function ()
5048     {
5049         if (this.position > 0) {
5050             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5051                 this.stack[this.position - 1][i].redo();
5052             }
5053             this.position--;
5054
5055             if (this.fireEvent) {
5056                 undoScopeHost.dispatchEvent(
5057                     new CustomEvent('redo', {
5058                         detail: {
5059                             transactions: this.stack[this.position].slice()
5060                         },
5061                         bubbles: true,
5062                         cancelable: false
5063                     })
5064                 );
5065             }
5066         }
5067     },
5068
5069     item : function (index)
5070     {
5071         if (index >= 0 && index < this.length) {
5072             return this.stack[index].slice();
5073         }
5074         return null;
5075     },
5076
5077     clearUndo : function () {
5078         this.stack.length = this.length = this.position;
5079     },
5080
5081     clearRedo : function () {
5082         this.stack.splice(0, this.position);
5083         this.position = 0;
5084         this.length = stack.length;
5085     }
5086 };
5087 /*
5088  * Based on:
5089  * Ext JS Library 1.1.1
5090  * Copyright(c) 2006-2007, Ext JS, LLC.
5091  *
5092  * Originally Released Under LGPL - original licence link has changed is not relivant.
5093  *
5094  * Fork - LGPL
5095  * <script type="text/javascript">
5096  */
5097
5098
5099 // nasty IE9 hack - what a pile of crap that is..
5100
5101  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5102     Range.prototype.createContextualFragment = function (html) {
5103         var doc = window.document;
5104         var container = doc.createElement("div");
5105         container.innerHTML = html;
5106         var frag = doc.createDocumentFragment(), n;
5107         while ((n = container.firstChild)) {
5108             frag.appendChild(n);
5109         }
5110         return frag;
5111     };
5112 }
5113
5114 /**
5115  * @class Roo.DomHelper
5116  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5117  * 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>.
5118  * @static
5119  */
5120 Roo.DomHelper = function(){
5121     var tempTableEl = null;
5122     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5123     var tableRe = /^table|tbody|tr|td$/i;
5124     var xmlns = {};
5125     // build as innerHTML where available
5126     /** @ignore */
5127     var createHtml = function(o){
5128         if(typeof o == 'string'){
5129             return o;
5130         }
5131         var b = "";
5132         if(!o.tag){
5133             o.tag = "div";
5134         }
5135         b += "<" + o.tag;
5136         for(var attr in o){
5137             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5138             if(attr == "style"){
5139                 var s = o["style"];
5140                 if(typeof s == "function"){
5141                     s = s.call();
5142                 }
5143                 if(typeof s == "string"){
5144                     b += ' style="' + s + '"';
5145                 }else if(typeof s == "object"){
5146                     b += ' style="';
5147                     for(var key in s){
5148                         if(typeof s[key] != "function"){
5149                             b += key + ":" + s[key] + ";";
5150                         }
5151                     }
5152                     b += '"';
5153                 }
5154             }else{
5155                 if(attr == "cls"){
5156                     b += ' class="' + o["cls"] + '"';
5157                 }else if(attr == "htmlFor"){
5158                     b += ' for="' + o["htmlFor"] + '"';
5159                 }else{
5160                     b += " " + attr + '="' + o[attr] + '"';
5161                 }
5162             }
5163         }
5164         if(emptyTags.test(o.tag)){
5165             b += "/>";
5166         }else{
5167             b += ">";
5168             var cn = o.children || o.cn;
5169             if(cn){
5170                 //http://bugs.kde.org/show_bug.cgi?id=71506
5171                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5172                     for(var i = 0, len = cn.length; i < len; i++) {
5173                         b += createHtml(cn[i], b);
5174                     }
5175                 }else{
5176                     b += createHtml(cn, b);
5177                 }
5178             }
5179             if(o.html){
5180                 b += o.html;
5181             }
5182             b += "</" + o.tag + ">";
5183         }
5184         return b;
5185     };
5186
5187     // build as dom
5188     /** @ignore */
5189     var createDom = function(o, parentNode){
5190          
5191         // defininition craeted..
5192         var ns = false;
5193         if (o.ns && o.ns != 'html') {
5194                
5195             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5196                 xmlns[o.ns] = o.xmlns;
5197                 ns = o.xmlns;
5198             }
5199             if (typeof(xmlns[o.ns]) == 'undefined') {
5200                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5201             }
5202             ns = xmlns[o.ns];
5203         }
5204         
5205         
5206         if (typeof(o) == 'string') {
5207             return parentNode.appendChild(document.createTextNode(o));
5208         }
5209         o.tag = o.tag || div;
5210         if (o.ns && Roo.isIE) {
5211             ns = false;
5212             o.tag = o.ns + ':' + o.tag;
5213             
5214         }
5215         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5216         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5217         for(var attr in o){
5218             
5219             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5220                     attr == "style" || typeof o[attr] == "function") { continue; }
5221                     
5222             if(attr=="cls" && Roo.isIE){
5223                 el.className = o["cls"];
5224             }else{
5225                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5226                 else { 
5227                     el[attr] = o[attr];
5228                 }
5229             }
5230         }
5231         Roo.DomHelper.applyStyles(el, o.style);
5232         var cn = o.children || o.cn;
5233         if(cn){
5234             //http://bugs.kde.org/show_bug.cgi?id=71506
5235              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5236                 for(var i = 0, len = cn.length; i < len; i++) {
5237                     createDom(cn[i], el);
5238                 }
5239             }else{
5240                 createDom(cn, el);
5241             }
5242         }
5243         if(o.html){
5244             el.innerHTML = o.html;
5245         }
5246         if(parentNode){
5247            parentNode.appendChild(el);
5248         }
5249         return el;
5250     };
5251
5252     var ieTable = function(depth, s, h, e){
5253         tempTableEl.innerHTML = [s, h, e].join('');
5254         var i = -1, el = tempTableEl;
5255         while(++i < depth && el.firstChild){
5256             el = el.firstChild;
5257         }
5258         return el;
5259     };
5260
5261     // kill repeat to save bytes
5262     var ts = '<table>',
5263         te = '</table>',
5264         tbs = ts+'<tbody>',
5265         tbe = '</tbody>'+te,
5266         trs = tbs + '<tr>',
5267         tre = '</tr>'+tbe;
5268
5269     /**
5270      * @ignore
5271      * Nasty code for IE's broken table implementation
5272      */
5273     var insertIntoTable = function(tag, where, el, html){
5274         if(!tempTableEl){
5275             tempTableEl = document.createElement('div');
5276         }
5277         var node;
5278         var before = null;
5279         if(tag == 'td'){
5280             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5281                 return;
5282             }
5283             if(where == 'beforebegin'){
5284                 before = el;
5285                 el = el.parentNode;
5286             } else{
5287                 before = el.nextSibling;
5288                 el = el.parentNode;
5289             }
5290             node = ieTable(4, trs, html, tre);
5291         }
5292         else if(tag == 'tr'){
5293             if(where == 'beforebegin'){
5294                 before = el;
5295                 el = el.parentNode;
5296                 node = ieTable(3, tbs, html, tbe);
5297             } else if(where == 'afterend'){
5298                 before = el.nextSibling;
5299                 el = el.parentNode;
5300                 node = ieTable(3, tbs, html, tbe);
5301             } else{ // INTO a TR
5302                 if(where == 'afterbegin'){
5303                     before = el.firstChild;
5304                 }
5305                 node = ieTable(4, trs, html, tre);
5306             }
5307         } else if(tag == 'tbody'){
5308             if(where == 'beforebegin'){
5309                 before = el;
5310                 el = el.parentNode;
5311                 node = ieTable(2, ts, html, te);
5312             } else if(where == 'afterend'){
5313                 before = el.nextSibling;
5314                 el = el.parentNode;
5315                 node = ieTable(2, ts, html, te);
5316             } else{
5317                 if(where == 'afterbegin'){
5318                     before = el.firstChild;
5319                 }
5320                 node = ieTable(3, tbs, html, tbe);
5321             }
5322         } else{ // TABLE
5323             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5324                 return;
5325             }
5326             if(where == 'afterbegin'){
5327                 before = el.firstChild;
5328             }
5329             node = ieTable(2, ts, html, te);
5330         }
5331         el.insertBefore(node, before);
5332         return node;
5333     };
5334     
5335     // this is a bit like the react update code...
5336     // 
5337     
5338     var updateNode = function(from, to)
5339     {
5340         // should we handle non-standard elements?
5341         Roo.log(["UpdateNode" , from, to]);
5342         if (from.nodeType != to.nodeType) {
5343             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5344             from.parentNode.replaceChild(to, from);
5345         }
5346         
5347         if (from.nodeType == 3) {
5348             // assume it's text?!
5349             if (from.data == to.data) {
5350                 return;
5351             }
5352             from.data = to.data;
5353             return;
5354         }
5355         
5356         // assume 'to' doesnt have '1/3 nodetypes!
5357         if (from.nodeType !=1 || from.tagName != to.tagName) {
5358             Roo.log(["ReplaceChild" , from, to ]);
5359             from.parentNode.replaceChild(to, from);
5360             return;
5361         }
5362         // compare attributes
5363         var ar = Array.from(from.attributes);
5364         for(var i = 0; i< ar.length;i++) {
5365             if (to.hasAttribute(ar[i].name)) {
5366                 continue;
5367             }
5368             if (ar[i].name == 'id') { // always keep ids?
5369                 continue;
5370             }
5371             from.removeAttribute(ar[i].name);
5372         }
5373         ar = to.attributes;
5374         for(var i = 0; i< ar.length;i++) {
5375             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5376                 continue;
5377             }
5378             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5379         }
5380         // children
5381         var far = Array.from(from.childNodes);
5382         var tar = Array.from(to.childNodes);
5383         // if the lengths are different.. then it's probably a editable content change, rather than
5384         // a change of the block definition..
5385         
5386         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5387          /*if (from.innerHTML == to.innerHTML) {
5388             return;
5389         }
5390         if (far.length != tar.length) {
5391             from.innerHTML = to.innerHTML;
5392             return;
5393         }
5394         */
5395         
5396         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5397             if (i >= far.length) {
5398                 from.appendChild(tar[i]);
5399                 Roo.log(["add", tar[i]]);
5400                 
5401             } else if ( i  >= tar.length) {
5402                 from.removeChild(far[i]);
5403                 Roo.log(["remove", far[i]]);
5404             } else {
5405                 
5406                 updateNode(far[i], tar[i]);
5407             }    
5408         }
5409         
5410         
5411         
5412         
5413     };
5414     
5415     
5416
5417     return {
5418         /** True to force the use of DOM instead of html fragments @type Boolean */
5419         useDom : false,
5420     
5421         /**
5422          * Returns the markup for the passed Element(s) config
5423          * @param {Object} o The Dom object spec (and children)
5424          * @return {String}
5425          */
5426         markup : function(o){
5427             return createHtml(o);
5428         },
5429     
5430         /**
5431          * Applies a style specification to an element
5432          * @param {String/HTMLElement} el The element to apply styles to
5433          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5434          * a function which returns such a specification.
5435          */
5436         applyStyles : function(el, styles){
5437             if(styles){
5438                el = Roo.fly(el);
5439                if(typeof styles == "string"){
5440                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5441                    var matches;
5442                    while ((matches = re.exec(styles)) != null){
5443                        el.setStyle(matches[1], matches[2]);
5444                    }
5445                }else if (typeof styles == "object"){
5446                    for (var style in styles){
5447                       el.setStyle(style, styles[style]);
5448                    }
5449                }else if (typeof styles == "function"){
5450                     Roo.DomHelper.applyStyles(el, styles.call());
5451                }
5452             }
5453         },
5454     
5455         /**
5456          * Inserts an HTML fragment into the Dom
5457          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5458          * @param {HTMLElement} el The context element
5459          * @param {String} html The HTML fragmenet
5460          * @return {HTMLElement} The new node
5461          */
5462         insertHtml : function(where, el, html){
5463             where = where.toLowerCase();
5464             if(el.insertAdjacentHTML){
5465                 if(tableRe.test(el.tagName)){
5466                     var rs;
5467                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5468                         return rs;
5469                     }
5470                 }
5471                 switch(where){
5472                     case "beforebegin":
5473                         el.insertAdjacentHTML('BeforeBegin', html);
5474                         return el.previousSibling;
5475                     case "afterbegin":
5476                         el.insertAdjacentHTML('AfterBegin', html);
5477                         return el.firstChild;
5478                     case "beforeend":
5479                         el.insertAdjacentHTML('BeforeEnd', html);
5480                         return el.lastChild;
5481                     case "afterend":
5482                         el.insertAdjacentHTML('AfterEnd', html);
5483                         return el.nextSibling;
5484                 }
5485                 throw 'Illegal insertion point -> "' + where + '"';
5486             }
5487             var range = el.ownerDocument.createRange();
5488             var frag;
5489             switch(where){
5490                  case "beforebegin":
5491                     range.setStartBefore(el);
5492                     frag = range.createContextualFragment(html);
5493                     el.parentNode.insertBefore(frag, el);
5494                     return el.previousSibling;
5495                  case "afterbegin":
5496                     if(el.firstChild){
5497                         range.setStartBefore(el.firstChild);
5498                         frag = range.createContextualFragment(html);
5499                         el.insertBefore(frag, el.firstChild);
5500                         return el.firstChild;
5501                     }else{
5502                         el.innerHTML = html;
5503                         return el.firstChild;
5504                     }
5505                 case "beforeend":
5506                     if(el.lastChild){
5507                         range.setStartAfter(el.lastChild);
5508                         frag = range.createContextualFragment(html);
5509                         el.appendChild(frag);
5510                         return el.lastChild;
5511                     }else{
5512                         el.innerHTML = html;
5513                         return el.lastChild;
5514                     }
5515                 case "afterend":
5516                     range.setStartAfter(el);
5517                     frag = range.createContextualFragment(html);
5518                     el.parentNode.insertBefore(frag, el.nextSibling);
5519                     return el.nextSibling;
5520                 }
5521                 throw 'Illegal insertion point -> "' + where + '"';
5522         },
5523     
5524         /**
5525          * Creates new Dom element(s) and inserts them before el
5526          * @param {String/HTMLElement/Element} el The context element
5527          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5528          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5529          * @return {HTMLElement/Roo.Element} The new node
5530          */
5531         insertBefore : function(el, o, returnElement){
5532             return this.doInsert(el, o, returnElement, "beforeBegin");
5533         },
5534     
5535         /**
5536          * Creates new Dom element(s) and inserts them after el
5537          * @param {String/HTMLElement/Element} el The context element
5538          * @param {Object} o The Dom object spec (and children)
5539          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5540          * @return {HTMLElement/Roo.Element} The new node
5541          */
5542         insertAfter : function(el, o, returnElement){
5543             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5544         },
5545     
5546         /**
5547          * Creates new Dom element(s) and inserts them as the first child of el
5548          * @param {String/HTMLElement/Element} el The context element
5549          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5550          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5551          * @return {HTMLElement/Roo.Element} The new node
5552          */
5553         insertFirst : function(el, o, returnElement){
5554             return this.doInsert(el, o, returnElement, "afterBegin");
5555         },
5556     
5557         // private
5558         doInsert : function(el, o, returnElement, pos, sibling){
5559             el = Roo.getDom(el);
5560             var newNode;
5561             if(this.useDom || o.ns){
5562                 newNode = createDom(o, null);
5563                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5564             }else{
5565                 var html = createHtml(o);
5566                 newNode = this.insertHtml(pos, el, html);
5567             }
5568             return returnElement ? Roo.get(newNode, true) : newNode;
5569         },
5570     
5571         /**
5572          * Creates new Dom element(s) and appends them to el
5573          * @param {String/HTMLElement/Element} el The context element
5574          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5575          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5576          * @return {HTMLElement/Roo.Element} The new node
5577          */
5578         append : function(el, o, returnElement){
5579             el = Roo.getDom(el);
5580             var newNode;
5581             if(this.useDom || o.ns){
5582                 newNode = createDom(o, null);
5583                 el.appendChild(newNode);
5584             }else{
5585                 var html = createHtml(o);
5586                 newNode = this.insertHtml("beforeEnd", el, html);
5587             }
5588             return returnElement ? Roo.get(newNode, true) : newNode;
5589         },
5590     
5591         /**
5592          * Creates new Dom element(s) and overwrites the contents of el with them
5593          * @param {String/HTMLElement/Element} el The context element
5594          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5595          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5596          * @return {HTMLElement/Roo.Element} The new node
5597          */
5598         overwrite : function(el, o, returnElement)
5599         {
5600             el = Roo.getDom(el);
5601             if (o.ns) {
5602               
5603                 while (el.childNodes.length) {
5604                     el.removeChild(el.firstChild);
5605                 }
5606                 createDom(o, el);
5607             } else {
5608                 el.innerHTML = createHtml(o);   
5609             }
5610             
5611             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5612         },
5613     
5614         /**
5615          * Creates a new Roo.DomHelper.Template from the Dom object spec
5616          * @param {Object} o The Dom object spec (and children)
5617          * @return {Roo.DomHelper.Template} The new template
5618          */
5619         createTemplate : function(o){
5620             var html = createHtml(o);
5621             return new Roo.Template(html);
5622         },
5623          /**
5624          * Updates the first element with the spec from the o (replacing if necessary)
5625          * This iterates through the children, and updates attributes / children etc..
5626          * @param {String/HTMLElement/Element} el The context element
5627          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5628          */
5629         
5630         update : function(el, o)
5631         {
5632             updateNode(Roo.getDom(el), createDom(o));
5633             
5634         }
5635         
5636         
5637     };
5638 }();
5639 /*
5640  * Based on:
5641  * Ext JS Library 1.1.1
5642  * Copyright(c) 2006-2007, Ext JS, LLC.
5643  *
5644  * Originally Released Under LGPL - original licence link has changed is not relivant.
5645  *
5646  * Fork - LGPL
5647  * <script type="text/javascript">
5648  */
5649  
5650 /**
5651 * @class Roo.Template
5652 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5653 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5654 * Usage:
5655 <pre><code>
5656 var t = new Roo.Template({
5657     html :  '&lt;div name="{id}"&gt;' + 
5658         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5659         '&lt;/div&gt;',
5660     myformat: function (value, allValues) {
5661         return 'XX' + value;
5662     }
5663 });
5664 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5665 </code></pre>
5666 * For more information see this blog post with examples:
5667 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5668      - Create Elements using DOM, HTML fragments and Templates</a>. 
5669 * @constructor
5670 * @param {Object} cfg - Configuration object.
5671 */
5672 Roo.Template = function(cfg){
5673     // BC!
5674     if(cfg instanceof Array){
5675         cfg = cfg.join("");
5676     }else if(arguments.length > 1){
5677         cfg = Array.prototype.join.call(arguments, "");
5678     }
5679     
5680     
5681     if (typeof(cfg) == 'object') {
5682         Roo.apply(this,cfg)
5683     } else {
5684         // bc
5685         this.html = cfg;
5686     }
5687     if (this.url) {
5688         this.load();
5689     }
5690     
5691 };
5692 Roo.Template.prototype = {
5693     
5694     /**
5695      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5696      */
5697     onLoad : false,
5698     
5699     
5700     /**
5701      * @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..
5702      *                    it should be fixed so that template is observable...
5703      */
5704     url : false,
5705     /**
5706      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5707      */
5708     html : '',
5709     
5710     
5711     compiled : false,
5712     loaded : false,
5713     /**
5714      * Returns an HTML fragment of this template with the specified values applied.
5715      * @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'})
5716      * @return {String} The HTML fragment
5717      */
5718     
5719    
5720     
5721     applyTemplate : function(values){
5722         //Roo.log(["applyTemplate", values]);
5723         try {
5724            
5725             if(this.compiled){
5726                 return this.compiled(values);
5727             }
5728             var useF = this.disableFormats !== true;
5729             var fm = Roo.util.Format, tpl = this;
5730             var fn = function(m, name, format, args){
5731                 if(format && useF){
5732                     if(format.substr(0, 5) == "this."){
5733                         return tpl.call(format.substr(5), values[name], values);
5734                     }else{
5735                         if(args){
5736                             // quoted values are required for strings in compiled templates, 
5737                             // but for non compiled we need to strip them
5738                             // quoted reversed for jsmin
5739                             var re = /^\s*['"](.*)["']\s*$/;
5740                             args = args.split(',');
5741                             for(var i = 0, len = args.length; i < len; i++){
5742                                 args[i] = args[i].replace(re, "$1");
5743                             }
5744                             args = [values[name]].concat(args);
5745                         }else{
5746                             args = [values[name]];
5747                         }
5748                         return fm[format].apply(fm, args);
5749                     }
5750                 }else{
5751                     return values[name] !== undefined ? values[name] : "";
5752                 }
5753             };
5754             return this.html.replace(this.re, fn);
5755         } catch (e) {
5756             Roo.log(e);
5757             throw e;
5758         }
5759          
5760     },
5761     
5762     loading : false,
5763       
5764     load : function ()
5765     {
5766          
5767         if (this.loading) {
5768             return;
5769         }
5770         var _t = this;
5771         
5772         this.loading = true;
5773         this.compiled = false;
5774         
5775         var cx = new Roo.data.Connection();
5776         cx.request({
5777             url : this.url,
5778             method : 'GET',
5779             success : function (response) {
5780                 _t.loading = false;
5781                 _t.url = false;
5782                 
5783                 _t.set(response.responseText,true);
5784                 _t.loaded = true;
5785                 if (_t.onLoad) {
5786                     _t.onLoad();
5787                 }
5788              },
5789             failure : function(response) {
5790                 Roo.log("Template failed to load from " + _t.url);
5791                 _t.loading = false;
5792             }
5793         });
5794     },
5795
5796     /**
5797      * Sets the HTML used as the template and optionally compiles it.
5798      * @param {String} html
5799      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5800      * @return {Roo.Template} this
5801      */
5802     set : function(html, compile){
5803         this.html = html;
5804         this.compiled = false;
5805         if(compile){
5806             this.compile();
5807         }
5808         return this;
5809     },
5810     
5811     /**
5812      * True to disable format functions (defaults to false)
5813      * @type Boolean
5814      */
5815     disableFormats : false,
5816     
5817     /**
5818     * The regular expression used to match template variables 
5819     * @type RegExp
5820     * @property 
5821     */
5822     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5823     
5824     /**
5825      * Compiles the template into an internal function, eliminating the RegEx overhead.
5826      * @return {Roo.Template} this
5827      */
5828     compile : function(){
5829         var fm = Roo.util.Format;
5830         var useF = this.disableFormats !== true;
5831         var sep = Roo.isGecko ? "+" : ",";
5832         var fn = function(m, name, format, args){
5833             if(format && useF){
5834                 args = args ? ',' + args : "";
5835                 if(format.substr(0, 5) != "this."){
5836                     format = "fm." + format + '(';
5837                 }else{
5838                     format = 'this.call("'+ format.substr(5) + '", ';
5839                     args = ", values";
5840                 }
5841             }else{
5842                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5843             }
5844             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5845         };
5846         var body;
5847         // branched to use + in gecko and [].join() in others
5848         if(Roo.isGecko){
5849             body = "this.compiled = function(values){ return '" +
5850                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5851                     "';};";
5852         }else{
5853             body = ["this.compiled = function(values){ return ['"];
5854             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5855             body.push("'].join('');};");
5856             body = body.join('');
5857         }
5858         /**
5859          * eval:var:values
5860          * eval:var:fm
5861          */
5862         eval(body);
5863         return this;
5864     },
5865     
5866     // private function used to call members
5867     call : function(fnName, value, allValues){
5868         return this[fnName](value, allValues);
5869     },
5870     
5871     /**
5872      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5873      * @param {String/HTMLElement/Roo.Element} el The context element
5874      * @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'})
5875      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5876      * @return {HTMLElement/Roo.Element} The new node or Element
5877      */
5878     insertFirst: function(el, values, returnElement){
5879         return this.doInsert('afterBegin', el, values, returnElement);
5880     },
5881
5882     /**
5883      * Applies the supplied values to the template and inserts the new node(s) before el.
5884      * @param {String/HTMLElement/Roo.Element} el The context element
5885      * @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'})
5886      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5887      * @return {HTMLElement/Roo.Element} The new node or Element
5888      */
5889     insertBefore: function(el, values, returnElement){
5890         return this.doInsert('beforeBegin', el, values, returnElement);
5891     },
5892
5893     /**
5894      * Applies the supplied values to the template and inserts the new node(s) after el.
5895      * @param {String/HTMLElement/Roo.Element} el The context element
5896      * @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'})
5897      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5898      * @return {HTMLElement/Roo.Element} The new node or Element
5899      */
5900     insertAfter : function(el, values, returnElement){
5901         return this.doInsert('afterEnd', el, values, returnElement);
5902     },
5903     
5904     /**
5905      * Applies the supplied values to the template and appends the new node(s) to el.
5906      * @param {String/HTMLElement/Roo.Element} el The context element
5907      * @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'})
5908      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5909      * @return {HTMLElement/Roo.Element} The new node or Element
5910      */
5911     append : function(el, values, returnElement){
5912         return this.doInsert('beforeEnd', el, values, returnElement);
5913     },
5914
5915     doInsert : function(where, el, values, returnEl){
5916         el = Roo.getDom(el);
5917         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5918         return returnEl ? Roo.get(newNode, true) : newNode;
5919     },
5920
5921     /**
5922      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5923      * @param {String/HTMLElement/Roo.Element} el The context element
5924      * @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'})
5925      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5926      * @return {HTMLElement/Roo.Element} The new node or Element
5927      */
5928     overwrite : function(el, values, returnElement){
5929         el = Roo.getDom(el);
5930         el.innerHTML = this.applyTemplate(values);
5931         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5932     }
5933 };
5934 /**
5935  * Alias for {@link #applyTemplate}
5936  * @method
5937  */
5938 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5939
5940 // backwards compat
5941 Roo.DomHelper.Template = Roo.Template;
5942
5943 /**
5944  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5945  * @param {String/HTMLElement} el A DOM element or its id
5946  * @returns {Roo.Template} The created template
5947  * @static
5948  */
5949 Roo.Template.from = function(el){
5950     el = Roo.getDom(el);
5951     return new Roo.Template(el.value || el.innerHTML);
5952 };/*
5953  * Based on:
5954  * Ext JS Library 1.1.1
5955  * Copyright(c) 2006-2007, Ext JS, LLC.
5956  *
5957  * Originally Released Under LGPL - original licence link has changed is not relivant.
5958  *
5959  * Fork - LGPL
5960  * <script type="text/javascript">
5961  */
5962  
5963
5964 /*
5965  * This is code is also distributed under MIT license for use
5966  * with jQuery and prototype JavaScript libraries.
5967  */
5968 /**
5969  * @class Roo.DomQuery
5970 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).
5971 <p>
5972 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>
5973
5974 <p>
5975 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.
5976 </p>
5977 <h4>Element Selectors:</h4>
5978 <ul class="list">
5979     <li> <b>*</b> any element</li>
5980     <li> <b>E</b> an element with the tag E</li>
5981     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5982     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5983     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5984     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5985 </ul>
5986 <h4>Attribute Selectors:</h4>
5987 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5988 <ul class="list">
5989     <li> <b>E[foo]</b> has an attribute "foo"</li>
5990     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5991     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5992     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5993     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5994     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5995     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5996 </ul>
5997 <h4>Pseudo Classes:</h4>
5998 <ul class="list">
5999     <li> <b>E:first-child</b> E is the first child of its parent</li>
6000     <li> <b>E:last-child</b> E is the last child of its parent</li>
6001     <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>
6002     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6003     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6004     <li> <b>E:only-child</b> E is the only child of its parent</li>
6005     <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>
6006     <li> <b>E:first</b> the first E in the resultset</li>
6007     <li> <b>E:last</b> the last E in the resultset</li>
6008     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6009     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6010     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6011     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6012     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6013     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6014     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6015     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6016     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6017 </ul>
6018 <h4>CSS Value Selectors:</h4>
6019 <ul class="list">
6020     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6021     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6022     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6023     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6024     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6025     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6026 </ul>
6027  * @static
6028  */
6029 Roo.DomQuery = function(){
6030     var cache = {}, simpleCache = {}, valueCache = {};
6031     var nonSpace = /\S/;
6032     var trimRe = /^\s+|\s+$/g;
6033     var tplRe = /\{(\d+)\}/g;
6034     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6035     var tagTokenRe = /^(#)?([\w-\*]+)/;
6036     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6037
6038     function child(p, index){
6039         var i = 0;
6040         var n = p.firstChild;
6041         while(n){
6042             if(n.nodeType == 1){
6043                if(++i == index){
6044                    return n;
6045                }
6046             }
6047             n = n.nextSibling;
6048         }
6049         return null;
6050     };
6051
6052     function next(n){
6053         while((n = n.nextSibling) && n.nodeType != 1);
6054         return n;
6055     };
6056
6057     function prev(n){
6058         while((n = n.previousSibling) && n.nodeType != 1);
6059         return n;
6060     };
6061
6062     function children(d){
6063         var n = d.firstChild, ni = -1;
6064             while(n){
6065                 var nx = n.nextSibling;
6066                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6067                     d.removeChild(n);
6068                 }else{
6069                     n.nodeIndex = ++ni;
6070                 }
6071                 n = nx;
6072             }
6073             return this;
6074         };
6075
6076     function byClassName(c, a, v){
6077         if(!v){
6078             return c;
6079         }
6080         var r = [], ri = -1, cn;
6081         for(var i = 0, ci; ci = c[i]; i++){
6082             
6083             
6084             if((' '+
6085                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6086                  +' ').indexOf(v) != -1){
6087                 r[++ri] = ci;
6088             }
6089         }
6090         return r;
6091     };
6092
6093     function attrValue(n, attr){
6094         if(!n.tagName && typeof n.length != "undefined"){
6095             n = n[0];
6096         }
6097         if(!n){
6098             return null;
6099         }
6100         if(attr == "for"){
6101             return n.htmlFor;
6102         }
6103         if(attr == "class" || attr == "className"){
6104             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6105         }
6106         return n.getAttribute(attr) || n[attr];
6107
6108     };
6109
6110     function getNodes(ns, mode, tagName){
6111         var result = [], ri = -1, cs;
6112         if(!ns){
6113             return result;
6114         }
6115         tagName = tagName || "*";
6116         if(typeof ns.getElementsByTagName != "undefined"){
6117             ns = [ns];
6118         }
6119         if(!mode){
6120             for(var i = 0, ni; ni = ns[i]; i++){
6121                 cs = ni.getElementsByTagName(tagName);
6122                 for(var j = 0, ci; ci = cs[j]; j++){
6123                     result[++ri] = ci;
6124                 }
6125             }
6126         }else if(mode == "/" || mode == ">"){
6127             var utag = tagName.toUpperCase();
6128             for(var i = 0, ni, cn; ni = ns[i]; i++){
6129                 cn = ni.children || ni.childNodes;
6130                 for(var j = 0, cj; cj = cn[j]; j++){
6131                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6132                         result[++ri] = cj;
6133                     }
6134                 }
6135             }
6136         }else if(mode == "+"){
6137             var utag = tagName.toUpperCase();
6138             for(var i = 0, n; n = ns[i]; i++){
6139                 while((n = n.nextSibling) && n.nodeType != 1);
6140                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6141                     result[++ri] = n;
6142                 }
6143             }
6144         }else if(mode == "~"){
6145             for(var i = 0, n; n = ns[i]; i++){
6146                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6147                 if(n){
6148                     result[++ri] = n;
6149                 }
6150             }
6151         }
6152         return result;
6153     };
6154
6155     function concat(a, b){
6156         if(b.slice){
6157             return a.concat(b);
6158         }
6159         for(var i = 0, l = b.length; i < l; i++){
6160             a[a.length] = b[i];
6161         }
6162         return a;
6163     }
6164
6165     function byTag(cs, tagName){
6166         if(cs.tagName || cs == document){
6167             cs = [cs];
6168         }
6169         if(!tagName){
6170             return cs;
6171         }
6172         var r = [], ri = -1;
6173         tagName = tagName.toLowerCase();
6174         for(var i = 0, ci; ci = cs[i]; i++){
6175             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6176                 r[++ri] = ci;
6177             }
6178         }
6179         return r;
6180     };
6181
6182     function byId(cs, attr, id){
6183         if(cs.tagName || cs == document){
6184             cs = [cs];
6185         }
6186         if(!id){
6187             return cs;
6188         }
6189         var r = [], ri = -1;
6190         for(var i = 0,ci; ci = cs[i]; i++){
6191             if(ci && ci.id == id){
6192                 r[++ri] = ci;
6193                 return r;
6194             }
6195         }
6196         return r;
6197     };
6198
6199     function byAttribute(cs, attr, value, op, custom){
6200         var r = [], ri = -1, st = custom=="{";
6201         var f = Roo.DomQuery.operators[op];
6202         for(var i = 0, ci; ci = cs[i]; i++){
6203             var a;
6204             if(st){
6205                 a = Roo.DomQuery.getStyle(ci, attr);
6206             }
6207             else if(attr == "class" || attr == "className"){
6208                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6209             }else if(attr == "for"){
6210                 a = ci.htmlFor;
6211             }else if(attr == "href"){
6212                 a = ci.getAttribute("href", 2);
6213             }else{
6214                 a = ci.getAttribute(attr);
6215             }
6216             if((f && f(a, value)) || (!f && a)){
6217                 r[++ri] = ci;
6218             }
6219         }
6220         return r;
6221     };
6222
6223     function byPseudo(cs, name, value){
6224         return Roo.DomQuery.pseudos[name](cs, value);
6225     };
6226
6227     // This is for IE MSXML which does not support expandos.
6228     // IE runs the same speed using setAttribute, however FF slows way down
6229     // and Safari completely fails so they need to continue to use expandos.
6230     var isIE = window.ActiveXObject ? true : false;
6231
6232     // this eval is stop the compressor from
6233     // renaming the variable to something shorter
6234     
6235     /** eval:var:batch */
6236     var batch = 30803; 
6237
6238     var key = 30803;
6239
6240     function nodupIEXml(cs){
6241         var d = ++key;
6242         cs[0].setAttribute("_nodup", d);
6243         var r = [cs[0]];
6244         for(var i = 1, len = cs.length; i < len; i++){
6245             var c = cs[i];
6246             if(!c.getAttribute("_nodup") != d){
6247                 c.setAttribute("_nodup", d);
6248                 r[r.length] = c;
6249             }
6250         }
6251         for(var i = 0, len = cs.length; i < len; i++){
6252             cs[i].removeAttribute("_nodup");
6253         }
6254         return r;
6255     }
6256
6257     function nodup(cs){
6258         if(!cs){
6259             return [];
6260         }
6261         var len = cs.length, c, i, r = cs, cj, ri = -1;
6262         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6263             return cs;
6264         }
6265         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6266             return nodupIEXml(cs);
6267         }
6268         var d = ++key;
6269         cs[0]._nodup = d;
6270         for(i = 1; c = cs[i]; i++){
6271             if(c._nodup != d){
6272                 c._nodup = d;
6273             }else{
6274                 r = [];
6275                 for(var j = 0; j < i; j++){
6276                     r[++ri] = cs[j];
6277                 }
6278                 for(j = i+1; cj = cs[j]; j++){
6279                     if(cj._nodup != d){
6280                         cj._nodup = d;
6281                         r[++ri] = cj;
6282                     }
6283                 }
6284                 return r;
6285             }
6286         }
6287         return r;
6288     }
6289
6290     function quickDiffIEXml(c1, c2){
6291         var d = ++key;
6292         for(var i = 0, len = c1.length; i < len; i++){
6293             c1[i].setAttribute("_qdiff", d);
6294         }
6295         var r = [];
6296         for(var i = 0, len = c2.length; i < len; i++){
6297             if(c2[i].getAttribute("_qdiff") != d){
6298                 r[r.length] = c2[i];
6299             }
6300         }
6301         for(var i = 0, len = c1.length; i < len; i++){
6302            c1[i].removeAttribute("_qdiff");
6303         }
6304         return r;
6305     }
6306
6307     function quickDiff(c1, c2){
6308         var len1 = c1.length;
6309         if(!len1){
6310             return c2;
6311         }
6312         if(isIE && c1[0].selectSingleNode){
6313             return quickDiffIEXml(c1, c2);
6314         }
6315         var d = ++key;
6316         for(var i = 0; i < len1; i++){
6317             c1[i]._qdiff = d;
6318         }
6319         var r = [];
6320         for(var i = 0, len = c2.length; i < len; i++){
6321             if(c2[i]._qdiff != d){
6322                 r[r.length] = c2[i];
6323             }
6324         }
6325         return r;
6326     }
6327
6328     function quickId(ns, mode, root, id){
6329         if(ns == root){
6330            var d = root.ownerDocument || root;
6331            return d.getElementById(id);
6332         }
6333         ns = getNodes(ns, mode, "*");
6334         return byId(ns, null, id);
6335     }
6336
6337     return {
6338         getStyle : function(el, name){
6339             return Roo.fly(el).getStyle(name);
6340         },
6341         /**
6342          * Compiles a selector/xpath query into a reusable function. The returned function
6343          * takes one parameter "root" (optional), which is the context node from where the query should start.
6344          * @param {String} selector The selector/xpath query
6345          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6346          * @return {Function}
6347          */
6348         compile : function(path, type){
6349             type = type || "select";
6350             
6351             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6352             var q = path, mode, lq;
6353             var tk = Roo.DomQuery.matchers;
6354             var tklen = tk.length;
6355             var mm;
6356
6357             // accept leading mode switch
6358             var lmode = q.match(modeRe);
6359             if(lmode && lmode[1]){
6360                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6361                 q = q.replace(lmode[1], "");
6362             }
6363             // strip leading slashes
6364             while(path.substr(0, 1)=="/"){
6365                 path = path.substr(1);
6366             }
6367
6368             while(q && lq != q){
6369                 lq = q;
6370                 var tm = q.match(tagTokenRe);
6371                 if(type == "select"){
6372                     if(tm){
6373                         if(tm[1] == "#"){
6374                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6375                         }else{
6376                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6377                         }
6378                         q = q.replace(tm[0], "");
6379                     }else if(q.substr(0, 1) != '@'){
6380                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6381                     }
6382                 }else{
6383                     if(tm){
6384                         if(tm[1] == "#"){
6385                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6386                         }else{
6387                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6388                         }
6389                         q = q.replace(tm[0], "");
6390                     }
6391                 }
6392                 while(!(mm = q.match(modeRe))){
6393                     var matched = false;
6394                     for(var j = 0; j < tklen; j++){
6395                         var t = tk[j];
6396                         var m = q.match(t.re);
6397                         if(m){
6398                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6399                                                     return m[i];
6400                                                 });
6401                             q = q.replace(m[0], "");
6402                             matched = true;
6403                             break;
6404                         }
6405                     }
6406                     // prevent infinite loop on bad selector
6407                     if(!matched){
6408                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6409                     }
6410                 }
6411                 if(mm[1]){
6412                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6413                     q = q.replace(mm[1], "");
6414                 }
6415             }
6416             fn[fn.length] = "return nodup(n);\n}";
6417             
6418              /** 
6419               * list of variables that need from compression as they are used by eval.
6420              *  eval:var:batch 
6421              *  eval:var:nodup
6422              *  eval:var:byTag
6423              *  eval:var:ById
6424              *  eval:var:getNodes
6425              *  eval:var:quickId
6426              *  eval:var:mode
6427              *  eval:var:root
6428              *  eval:var:n
6429              *  eval:var:byClassName
6430              *  eval:var:byPseudo
6431              *  eval:var:byAttribute
6432              *  eval:var:attrValue
6433              * 
6434              **/ 
6435             eval(fn.join(""));
6436             return f;
6437         },
6438
6439         /**
6440          * Selects a group of elements.
6441          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6442          * @param {Node} root (optional) The start of the query (defaults to document).
6443          * @return {Array}
6444          */
6445         select : function(path, root, type){
6446             if(!root || root == document){
6447                 root = document;
6448             }
6449             if(typeof root == "string"){
6450                 root = document.getElementById(root);
6451             }
6452             var paths = path.split(",");
6453             var results = [];
6454             for(var i = 0, len = paths.length; i < len; i++){
6455                 var p = paths[i].replace(trimRe, "");
6456                 if(!cache[p]){
6457                     cache[p] = Roo.DomQuery.compile(p);
6458                     if(!cache[p]){
6459                         throw p + " is not a valid selector";
6460                     }
6461                 }
6462                 var result = cache[p](root);
6463                 if(result && result != document){
6464                     results = results.concat(result);
6465                 }
6466             }
6467             if(paths.length > 1){
6468                 return nodup(results);
6469             }
6470             return results;
6471         },
6472
6473         /**
6474          * Selects a single element.
6475          * @param {String} selector The selector/xpath query
6476          * @param {Node} root (optional) The start of the query (defaults to document).
6477          * @return {Element}
6478          */
6479         selectNode : function(path, root){
6480             return Roo.DomQuery.select(path, root)[0];
6481         },
6482
6483         /**
6484          * Selects the value of a node, optionally replacing null with the defaultValue.
6485          * @param {String} selector The selector/xpath query
6486          * @param {Node} root (optional) The start of the query (defaults to document).
6487          * @param {String} defaultValue
6488          */
6489         selectValue : function(path, root, defaultValue){
6490             path = path.replace(trimRe, "");
6491             if(!valueCache[path]){
6492                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6493             }
6494             var n = valueCache[path](root);
6495             n = n[0] ? n[0] : n;
6496             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6497             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6498         },
6499
6500         /**
6501          * Selects the value of a node, parsing integers and floats.
6502          * @param {String} selector The selector/xpath query
6503          * @param {Node} root (optional) The start of the query (defaults to document).
6504          * @param {Number} defaultValue
6505          * @return {Number}
6506          */
6507         selectNumber : function(path, root, defaultValue){
6508             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6509             return parseFloat(v);
6510         },
6511
6512         /**
6513          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6514          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6515          * @param {String} selector The simple selector to test
6516          * @return {Boolean}
6517          */
6518         is : function(el, ss){
6519             if(typeof el == "string"){
6520                 el = document.getElementById(el);
6521             }
6522             var isArray = (el instanceof Array);
6523             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6524             return isArray ? (result.length == el.length) : (result.length > 0);
6525         },
6526
6527         /**
6528          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6529          * @param {Array} el An array of elements to filter
6530          * @param {String} selector The simple selector to test
6531          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6532          * the selector instead of the ones that match
6533          * @return {Array}
6534          */
6535         filter : function(els, ss, nonMatches){
6536             ss = ss.replace(trimRe, "");
6537             if(!simpleCache[ss]){
6538                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6539             }
6540             var result = simpleCache[ss](els);
6541             return nonMatches ? quickDiff(result, els) : result;
6542         },
6543
6544         /**
6545          * Collection of matching regular expressions and code snippets.
6546          */
6547         matchers : [{
6548                 re: /^\.([\w-]+)/,
6549                 select: 'n = byClassName(n, null, " {1} ");'
6550             }, {
6551                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6552                 select: 'n = byPseudo(n, "{1}", "{2}");'
6553             },{
6554                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6555                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6556             }, {
6557                 re: /^#([\w-]+)/,
6558                 select: 'n = byId(n, null, "{1}");'
6559             },{
6560                 re: /^@([\w-]+)/,
6561                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6562             }
6563         ],
6564
6565         /**
6566          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6567          * 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;.
6568          */
6569         operators : {
6570             "=" : function(a, v){
6571                 return a == v;
6572             },
6573             "!=" : function(a, v){
6574                 return a != v;
6575             },
6576             "^=" : function(a, v){
6577                 return a && a.substr(0, v.length) == v;
6578             },
6579             "$=" : function(a, v){
6580                 return a && a.substr(a.length-v.length) == v;
6581             },
6582             "*=" : function(a, v){
6583                 return a && a.indexOf(v) !== -1;
6584             },
6585             "%=" : function(a, v){
6586                 return (a % v) == 0;
6587             },
6588             "|=" : function(a, v){
6589                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6590             },
6591             "~=" : function(a, v){
6592                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6593             }
6594         },
6595
6596         /**
6597          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6598          * and the argument (if any) supplied in the selector.
6599          */
6600         pseudos : {
6601             "first-child" : function(c){
6602                 var r = [], ri = -1, n;
6603                 for(var i = 0, ci; ci = n = c[i]; i++){
6604                     while((n = n.previousSibling) && n.nodeType != 1);
6605                     if(!n){
6606                         r[++ri] = ci;
6607                     }
6608                 }
6609                 return r;
6610             },
6611
6612             "last-child" : function(c){
6613                 var r = [], ri = -1, n;
6614                 for(var i = 0, ci; ci = n = c[i]; i++){
6615                     while((n = n.nextSibling) && n.nodeType != 1);
6616                     if(!n){
6617                         r[++ri] = ci;
6618                     }
6619                 }
6620                 return r;
6621             },
6622
6623             "nth-child" : function(c, a) {
6624                 var r = [], ri = -1;
6625                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6626                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6627                 for(var i = 0, n; n = c[i]; i++){
6628                     var pn = n.parentNode;
6629                     if (batch != pn._batch) {
6630                         var j = 0;
6631                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6632                             if(cn.nodeType == 1){
6633                                cn.nodeIndex = ++j;
6634                             }
6635                         }
6636                         pn._batch = batch;
6637                     }
6638                     if (f == 1) {
6639                         if (l == 0 || n.nodeIndex == l){
6640                             r[++ri] = n;
6641                         }
6642                     } else if ((n.nodeIndex + l) % f == 0){
6643                         r[++ri] = n;
6644                     }
6645                 }
6646
6647                 return r;
6648             },
6649
6650             "only-child" : function(c){
6651                 var r = [], ri = -1;;
6652                 for(var i = 0, ci; ci = c[i]; i++){
6653                     if(!prev(ci) && !next(ci)){
6654                         r[++ri] = ci;
6655                     }
6656                 }
6657                 return r;
6658             },
6659
6660             "empty" : function(c){
6661                 var r = [], ri = -1;
6662                 for(var i = 0, ci; ci = c[i]; i++){
6663                     var cns = ci.childNodes, j = 0, cn, empty = true;
6664                     while(cn = cns[j]){
6665                         ++j;
6666                         if(cn.nodeType == 1 || cn.nodeType == 3){
6667                             empty = false;
6668                             break;
6669                         }
6670                     }
6671                     if(empty){
6672                         r[++ri] = ci;
6673                     }
6674                 }
6675                 return r;
6676             },
6677
6678             "contains" : function(c, v){
6679                 var r = [], ri = -1;
6680                 for(var i = 0, ci; ci = c[i]; i++){
6681                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6682                         r[++ri] = ci;
6683                     }
6684                 }
6685                 return r;
6686             },
6687
6688             "nodeValue" : function(c, v){
6689                 var r = [], ri = -1;
6690                 for(var i = 0, ci; ci = c[i]; i++){
6691                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6692                         r[++ri] = ci;
6693                     }
6694                 }
6695                 return r;
6696             },
6697
6698             "checked" : function(c){
6699                 var r = [], ri = -1;
6700                 for(var i = 0, ci; ci = c[i]; i++){
6701                     if(ci.checked == true){
6702                         r[++ri] = ci;
6703                     }
6704                 }
6705                 return r;
6706             },
6707
6708             "not" : function(c, ss){
6709                 return Roo.DomQuery.filter(c, ss, true);
6710             },
6711
6712             "odd" : function(c){
6713                 return this["nth-child"](c, "odd");
6714             },
6715
6716             "even" : function(c){
6717                 return this["nth-child"](c, "even");
6718             },
6719
6720             "nth" : function(c, a){
6721                 return c[a-1] || [];
6722             },
6723
6724             "first" : function(c){
6725                 return c[0] || [];
6726             },
6727
6728             "last" : function(c){
6729                 return c[c.length-1] || [];
6730             },
6731
6732             "has" : function(c, ss){
6733                 var s = Roo.DomQuery.select;
6734                 var r = [], ri = -1;
6735                 for(var i = 0, ci; ci = c[i]; i++){
6736                     if(s(ss, ci).length > 0){
6737                         r[++ri] = ci;
6738                     }
6739                 }
6740                 return r;
6741             },
6742
6743             "next" : function(c, ss){
6744                 var is = Roo.DomQuery.is;
6745                 var r = [], ri = -1;
6746                 for(var i = 0, ci; ci = c[i]; i++){
6747                     var n = next(ci);
6748                     if(n && is(n, ss)){
6749                         r[++ri] = ci;
6750                     }
6751                 }
6752                 return r;
6753             },
6754
6755             "prev" : function(c, ss){
6756                 var is = Roo.DomQuery.is;
6757                 var r = [], ri = -1;
6758                 for(var i = 0, ci; ci = c[i]; i++){
6759                     var n = prev(ci);
6760                     if(n && is(n, ss)){
6761                         r[++ri] = ci;
6762                     }
6763                 }
6764                 return r;
6765             }
6766         }
6767     };
6768 }();
6769
6770 /**
6771  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6772  * @param {String} path The selector/xpath query
6773  * @param {Node} root (optional) The start of the query (defaults to document).
6774  * @return {Array}
6775  * @member Roo
6776  * @method query
6777  */
6778 Roo.query = Roo.DomQuery.select;
6779 /*
6780  * Based on:
6781  * Ext JS Library 1.1.1
6782  * Copyright(c) 2006-2007, Ext JS, LLC.
6783  *
6784  * Originally Released Under LGPL - original licence link has changed is not relivant.
6785  *
6786  * Fork - LGPL
6787  * <script type="text/javascript">
6788  */
6789
6790 /**
6791  * @class Roo.util.Observable
6792  * Base class that provides a common interface for publishing events. Subclasses are expected to
6793  * to have a property "events" with all the events defined.<br>
6794  * For example:
6795  * <pre><code>
6796  Employee = function(name){
6797     this.name = name;
6798     this.addEvents({
6799         "fired" : true,
6800         "quit" : true
6801     });
6802  }
6803  Roo.extend(Employee, Roo.util.Observable);
6804 </code></pre>
6805  * @param {Object} config properties to use (incuding events / listeners)
6806  */
6807
6808 Roo.util.Observable = function(cfg){
6809     
6810     cfg = cfg|| {};
6811     this.addEvents(cfg.events || {});
6812     if (cfg.events) {
6813         delete cfg.events; // make sure
6814     }
6815      
6816     Roo.apply(this, cfg);
6817     
6818     if(this.listeners){
6819         this.on(this.listeners);
6820         delete this.listeners;
6821     }
6822 };
6823 Roo.util.Observable.prototype = {
6824     /** 
6825  * @cfg {Object} listeners  list of events and functions to call for this object, 
6826  * For example :
6827  * <pre><code>
6828     listeners :  { 
6829        'click' : function(e) {
6830            ..... 
6831         } ,
6832         .... 
6833     } 
6834   </code></pre>
6835  */
6836     
6837     
6838     /**
6839      * Fires the specified event with the passed parameters (minus the event name).
6840      * @param {String} eventName
6841      * @param {Object...} args Variable number of parameters are passed to handlers
6842      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6843      */
6844     fireEvent : function(){
6845         var ce = this.events[arguments[0].toLowerCase()];
6846         if(typeof ce == "object"){
6847             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6848         }else{
6849             return true;
6850         }
6851     },
6852
6853     // private
6854     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6855
6856     /**
6857      * Appends an event handler to this component
6858      * @param {String}   eventName The type of event to listen for
6859      * @param {Function} handler The method the event invokes
6860      * @param {Object}   scope (optional) The scope in which to execute the handler
6861      * function. The handler function's "this" context.
6862      * @param {Object}   options (optional) An object containing handler configuration
6863      * properties. This may contain any of the following properties:<ul>
6864      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6865      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6866      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6867      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6868      * by the specified number of milliseconds. If the event fires again within that time, the original
6869      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6870      * </ul><br>
6871      * <p>
6872      * <b>Combining Options</b><br>
6873      * Using the options argument, it is possible to combine different types of listeners:<br>
6874      * <br>
6875      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6876                 <pre><code>
6877                 el.on('click', this.onClick, this, {
6878                         single: true,
6879                 delay: 100,
6880                 forumId: 4
6881                 });
6882                 </code></pre>
6883      * <p>
6884      * <b>Attaching multiple handlers in 1 call</b><br>
6885      * The method also allows for a single argument to be passed which is a config object containing properties
6886      * which specify multiple handlers.
6887      * <pre><code>
6888                 el.on({
6889                         'click': {
6890                         fn: this.onClick,
6891                         scope: this,
6892                         delay: 100
6893                 }, 
6894                 'mouseover': {
6895                         fn: this.onMouseOver,
6896                         scope: this
6897                 },
6898                 'mouseout': {
6899                         fn: this.onMouseOut,
6900                         scope: this
6901                 }
6902                 });
6903                 </code></pre>
6904      * <p>
6905      * Or a shorthand syntax which passes the same scope object to all handlers:
6906         <pre><code>
6907                 el.on({
6908                         'click': this.onClick,
6909                 'mouseover': this.onMouseOver,
6910                 'mouseout': this.onMouseOut,
6911                 scope: this
6912                 });
6913                 </code></pre>
6914      */
6915     addListener : function(eventName, fn, scope, o){
6916         if(typeof eventName == "object"){
6917             o = eventName;
6918             for(var e in o){
6919                 if(this.filterOptRe.test(e)){
6920                     continue;
6921                 }
6922                 if(typeof o[e] == "function"){
6923                     // shared options
6924                     this.addListener(e, o[e], o.scope,  o);
6925                 }else{
6926                     // individual options
6927                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6928                 }
6929             }
6930             return;
6931         }
6932         o = (!o || typeof o == "boolean") ? {} : o;
6933         eventName = eventName.toLowerCase();
6934         var ce = this.events[eventName] || true;
6935         if(typeof ce == "boolean"){
6936             ce = new Roo.util.Event(this, eventName);
6937             this.events[eventName] = ce;
6938         }
6939         ce.addListener(fn, scope, o);
6940     },
6941
6942     /**
6943      * Removes a listener
6944      * @param {String}   eventName     The type of event to listen for
6945      * @param {Function} handler        The handler to remove
6946      * @param {Object}   scope  (optional) The scope (this object) for the handler
6947      */
6948     removeListener : function(eventName, fn, scope){
6949         var ce = this.events[eventName.toLowerCase()];
6950         if(typeof ce == "object"){
6951             ce.removeListener(fn, scope);
6952         }
6953     },
6954
6955     /**
6956      * Removes all listeners for this object
6957      */
6958     purgeListeners : function(){
6959         for(var evt in this.events){
6960             if(typeof this.events[evt] == "object"){
6961                  this.events[evt].clearListeners();
6962             }
6963         }
6964     },
6965
6966     relayEvents : function(o, events){
6967         var createHandler = function(ename){
6968             return function(){
6969                  
6970                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6971             };
6972         };
6973         for(var i = 0, len = events.length; i < len; i++){
6974             var ename = events[i];
6975             if(!this.events[ename]){
6976                 this.events[ename] = true;
6977             };
6978             o.on(ename, createHandler(ename), this);
6979         }
6980     },
6981
6982     /**
6983      * Used to define events on this Observable
6984      * @param {Object} object The object with the events defined
6985      */
6986     addEvents : function(o){
6987         if(!this.events){
6988             this.events = {};
6989         }
6990         Roo.applyIf(this.events, o);
6991     },
6992
6993     /**
6994      * Checks to see if this object has any listeners for a specified event
6995      * @param {String} eventName The name of the event to check for
6996      * @return {Boolean} True if the event is being listened for, else false
6997      */
6998     hasListener : function(eventName){
6999         var e = this.events[eventName];
7000         return typeof e == "object" && e.listeners.length > 0;
7001     }
7002 };
7003 /**
7004  * Appends an event handler to this element (shorthand for addListener)
7005  * @param {String}   eventName     The type of event to listen for
7006  * @param {Function} handler        The method the event invokes
7007  * @param {Object}   scope (optional) The scope in which to execute the handler
7008  * function. The handler function's "this" context.
7009  * @param {Object}   options  (optional)
7010  * @method
7011  */
7012 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7013 /**
7014  * Removes a listener (shorthand for removeListener)
7015  * @param {String}   eventName     The type of event to listen for
7016  * @param {Function} handler        The handler to remove
7017  * @param {Object}   scope  (optional) The scope (this object) for the handler
7018  * @method
7019  */
7020 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7021
7022 /**
7023  * Starts capture on the specified Observable. All events will be passed
7024  * to the supplied function with the event name + standard signature of the event
7025  * <b>before</b> the event is fired. If the supplied function returns false,
7026  * the event will not fire.
7027  * @param {Observable} o The Observable to capture
7028  * @param {Function} fn The function to call
7029  * @param {Object} scope (optional) The scope (this object) for the fn
7030  * @static
7031  */
7032 Roo.util.Observable.capture = function(o, fn, scope){
7033     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7034 };
7035
7036 /**
7037  * Removes <b>all</b> added captures from the Observable.
7038  * @param {Observable} o The Observable to release
7039  * @static
7040  */
7041 Roo.util.Observable.releaseCapture = function(o){
7042     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7043 };
7044
7045 (function(){
7046
7047     var createBuffered = function(h, o, scope){
7048         var task = new Roo.util.DelayedTask();
7049         return function(){
7050             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7051         };
7052     };
7053
7054     var createSingle = function(h, e, fn, scope){
7055         return function(){
7056             e.removeListener(fn, scope);
7057             return h.apply(scope, arguments);
7058         };
7059     };
7060
7061     var createDelayed = function(h, o, scope){
7062         return function(){
7063             var args = Array.prototype.slice.call(arguments, 0);
7064             setTimeout(function(){
7065                 h.apply(scope, args);
7066             }, o.delay || 10);
7067         };
7068     };
7069
7070     Roo.util.Event = function(obj, name){
7071         this.name = name;
7072         this.obj = obj;
7073         this.listeners = [];
7074     };
7075
7076     Roo.util.Event.prototype = {
7077         addListener : function(fn, scope, options){
7078             var o = options || {};
7079             scope = scope || this.obj;
7080             if(!this.isListening(fn, scope)){
7081                 var l = {fn: fn, scope: scope, options: o};
7082                 var h = fn;
7083                 if(o.delay){
7084                     h = createDelayed(h, o, scope);
7085                 }
7086                 if(o.single){
7087                     h = createSingle(h, this, fn, scope);
7088                 }
7089                 if(o.buffer){
7090                     h = createBuffered(h, o, scope);
7091                 }
7092                 l.fireFn = h;
7093                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7094                     this.listeners.push(l);
7095                 }else{
7096                     this.listeners = this.listeners.slice(0);
7097                     this.listeners.push(l);
7098                 }
7099             }
7100         },
7101
7102         findListener : function(fn, scope){
7103             scope = scope || this.obj;
7104             var ls = this.listeners;
7105             for(var i = 0, len = ls.length; i < len; i++){
7106                 var l = ls[i];
7107                 if(l.fn == fn && l.scope == scope){
7108                     return i;
7109                 }
7110             }
7111             return -1;
7112         },
7113
7114         isListening : function(fn, scope){
7115             return this.findListener(fn, scope) != -1;
7116         },
7117
7118         removeListener : function(fn, scope){
7119             var index;
7120             if((index = this.findListener(fn, scope)) != -1){
7121                 if(!this.firing){
7122                     this.listeners.splice(index, 1);
7123                 }else{
7124                     this.listeners = this.listeners.slice(0);
7125                     this.listeners.splice(index, 1);
7126                 }
7127                 return true;
7128             }
7129             return false;
7130         },
7131
7132         clearListeners : function(){
7133             this.listeners = [];
7134         },
7135
7136         fire : function(){
7137             var ls = this.listeners, scope, len = ls.length;
7138             if(len > 0){
7139                 this.firing = true;
7140                 var args = Array.prototype.slice.call(arguments, 0);                
7141                 for(var i = 0; i < len; i++){
7142                     var l = ls[i];
7143                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7144                         this.firing = false;
7145                         return false;
7146                     }
7147                 }
7148                 this.firing = false;
7149             }
7150             return true;
7151         }
7152     };
7153 })();/*
7154  * RooJS Library 
7155  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7156  *
7157  * Licence LGPL 
7158  *
7159  */
7160  
7161 /**
7162  * @class Roo.Document
7163  * @extends Roo.util.Observable
7164  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7165  * 
7166  * @param {Object} config the methods and properties of the 'base' class for the application.
7167  * 
7168  *  Generic Page handler - implement this to start your app..
7169  * 
7170  * eg.
7171  *  MyProject = new Roo.Document({
7172         events : {
7173             'load' : true // your events..
7174         },
7175         listeners : {
7176             'ready' : function() {
7177                 // fired on Roo.onReady()
7178             }
7179         }
7180  * 
7181  */
7182 Roo.Document = function(cfg) {
7183      
7184     this.addEvents({ 
7185         'ready' : true
7186     });
7187     Roo.util.Observable.call(this,cfg);
7188     
7189     var _this = this;
7190     
7191     Roo.onReady(function() {
7192         _this.fireEvent('ready');
7193     },null,false);
7194     
7195     
7196 }
7197
7198 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7199  * Based on:
7200  * Ext JS Library 1.1.1
7201  * Copyright(c) 2006-2007, Ext JS, LLC.
7202  *
7203  * Originally Released Under LGPL - original licence link has changed is not relivant.
7204  *
7205  * Fork - LGPL
7206  * <script type="text/javascript">
7207  */
7208
7209 /**
7210  * @class Roo.EventManager
7211  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7212  * several useful events directly.
7213  * See {@link Roo.EventObject} for more details on normalized event objects.
7214  * @static
7215  */
7216 Roo.EventManager = function(){
7217     var docReadyEvent, docReadyProcId, docReadyState = false;
7218     var resizeEvent, resizeTask, textEvent, textSize;
7219     var E = Roo.lib.Event;
7220     var D = Roo.lib.Dom;
7221
7222     
7223     
7224
7225     var fireDocReady = function(){
7226         if(!docReadyState){
7227             docReadyState = true;
7228             Roo.isReady = true;
7229             if(docReadyProcId){
7230                 clearInterval(docReadyProcId);
7231             }
7232             if(Roo.isGecko || Roo.isOpera) {
7233                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7234             }
7235             if(Roo.isIE){
7236                 var defer = document.getElementById("ie-deferred-loader");
7237                 if(defer){
7238                     defer.onreadystatechange = null;
7239                     defer.parentNode.removeChild(defer);
7240                 }
7241             }
7242             if(docReadyEvent){
7243                 docReadyEvent.fire();
7244                 docReadyEvent.clearListeners();
7245             }
7246         }
7247     };
7248     
7249     var initDocReady = function(){
7250         docReadyEvent = new Roo.util.Event();
7251         if(Roo.isGecko || Roo.isOpera) {
7252             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7253         }else if(Roo.isIE){
7254             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7255             var defer = document.getElementById("ie-deferred-loader");
7256             defer.onreadystatechange = function(){
7257                 if(this.readyState == "complete"){
7258                     fireDocReady();
7259                 }
7260             };
7261         }else if(Roo.isSafari){ 
7262             docReadyProcId = setInterval(function(){
7263                 var rs = document.readyState;
7264                 if(rs == "complete") {
7265                     fireDocReady();     
7266                  }
7267             }, 10);
7268         }
7269         // no matter what, make sure it fires on load
7270         E.on(window, "load", fireDocReady);
7271     };
7272
7273     var createBuffered = function(h, o){
7274         var task = new Roo.util.DelayedTask(h);
7275         return function(e){
7276             // create new event object impl so new events don't wipe out properties
7277             e = new Roo.EventObjectImpl(e);
7278             task.delay(o.buffer, h, null, [e]);
7279         };
7280     };
7281
7282     var createSingle = function(h, el, ename, fn){
7283         return function(e){
7284             Roo.EventManager.removeListener(el, ename, fn);
7285             h(e);
7286         };
7287     };
7288
7289     var createDelayed = function(h, o){
7290         return function(e){
7291             // create new event object impl so new events don't wipe out properties
7292             e = new Roo.EventObjectImpl(e);
7293             setTimeout(function(){
7294                 h(e);
7295             }, o.delay || 10);
7296         };
7297     };
7298     var transitionEndVal = false;
7299     
7300     var transitionEnd = function()
7301     {
7302         if (transitionEndVal) {
7303             return transitionEndVal;
7304         }
7305         var el = document.createElement('div');
7306
7307         var transEndEventNames = {
7308             WebkitTransition : 'webkitTransitionEnd',
7309             MozTransition    : 'transitionend',
7310             OTransition      : 'oTransitionEnd otransitionend',
7311             transition       : 'transitionend'
7312         };
7313     
7314         for (var name in transEndEventNames) {
7315             if (el.style[name] !== undefined) {
7316                 transitionEndVal = transEndEventNames[name];
7317                 return  transitionEndVal ;
7318             }
7319         }
7320     }
7321     
7322   
7323
7324     var listen = function(element, ename, opt, fn, scope)
7325     {
7326         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7327         fn = fn || o.fn; scope = scope || o.scope;
7328         var el = Roo.getDom(element);
7329         
7330         
7331         if(!el){
7332             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7333         }
7334         
7335         if (ename == 'transitionend') {
7336             ename = transitionEnd();
7337         }
7338         var h = function(e){
7339             e = Roo.EventObject.setEvent(e);
7340             var t;
7341             if(o.delegate){
7342                 t = e.getTarget(o.delegate, el);
7343                 if(!t){
7344                     return;
7345                 }
7346             }else{
7347                 t = e.target;
7348             }
7349             if(o.stopEvent === true){
7350                 e.stopEvent();
7351             }
7352             if(o.preventDefault === true){
7353                e.preventDefault();
7354             }
7355             if(o.stopPropagation === true){
7356                 e.stopPropagation();
7357             }
7358
7359             if(o.normalized === false){
7360                 e = e.browserEvent;
7361             }
7362
7363             fn.call(scope || el, e, t, o);
7364         };
7365         if(o.delay){
7366             h = createDelayed(h, o);
7367         }
7368         if(o.single){
7369             h = createSingle(h, el, ename, fn);
7370         }
7371         if(o.buffer){
7372             h = createBuffered(h, o);
7373         }
7374         
7375         fn._handlers = fn._handlers || [];
7376         
7377         
7378         fn._handlers.push([Roo.id(el), ename, h]);
7379         
7380         
7381          
7382         E.on(el, ename, h); // this adds the actuall listener to the object..
7383         
7384         
7385         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7386             el.addEventListener("DOMMouseScroll", h, false);
7387             E.on(window, 'unload', function(){
7388                 el.removeEventListener("DOMMouseScroll", h, false);
7389             });
7390         }
7391         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7392             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7393         }
7394         return h;
7395     };
7396
7397     var stopListening = function(el, ename, fn){
7398         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7399         if(hds){
7400             for(var i = 0, len = hds.length; i < len; i++){
7401                 var h = hds[i];
7402                 if(h[0] == id && h[1] == ename){
7403                     hd = h[2];
7404                     hds.splice(i, 1);
7405                     break;
7406                 }
7407             }
7408         }
7409         E.un(el, ename, hd);
7410         el = Roo.getDom(el);
7411         if(ename == "mousewheel" && el.addEventListener){
7412             el.removeEventListener("DOMMouseScroll", hd, false);
7413         }
7414         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7415             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7416         }
7417     };
7418
7419     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7420     
7421     var pub = {
7422         
7423         
7424         /** 
7425          * Fix for doc tools
7426          * @scope Roo.EventManager
7427          */
7428         
7429         
7430         /** 
7431          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7432          * object with a Roo.EventObject
7433          * @param {Function} fn        The method the event invokes
7434          * @param {Object}   scope    An object that becomes the scope of the handler
7435          * @param {boolean}  override If true, the obj passed in becomes
7436          *                             the execution scope of the listener
7437          * @return {Function} The wrapped function
7438          * @deprecated
7439          */
7440         wrap : function(fn, scope, override){
7441             return function(e){
7442                 Roo.EventObject.setEvent(e);
7443                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7444             };
7445         },
7446         
7447         /**
7448      * Appends an event handler to an element (shorthand for addListener)
7449      * @param {String/HTMLElement}   element        The html element or id to assign the
7450      * @param {String}   eventName The type of event to listen for
7451      * @param {Function} handler The method the event invokes
7452      * @param {Object}   scope (optional) The scope in which to execute the handler
7453      * function. The handler function's "this" context.
7454      * @param {Object}   options (optional) An object containing handler configuration
7455      * properties. This may contain any of the following properties:<ul>
7456      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7457      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7458      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7459      * <li>preventDefault {Boolean} True to prevent the default action</li>
7460      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7461      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7462      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7463      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7464      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7465      * by the specified number of milliseconds. If the event fires again within that time, the original
7466      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7467      * </ul><br>
7468      * <p>
7469      * <b>Combining Options</b><br>
7470      * Using the options argument, it is possible to combine different types of listeners:<br>
7471      * <br>
7472      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7473      * Code:<pre><code>
7474 el.on('click', this.onClick, this, {
7475     single: true,
7476     delay: 100,
7477     stopEvent : true,
7478     forumId: 4
7479 });</code></pre>
7480      * <p>
7481      * <b>Attaching multiple handlers in 1 call</b><br>
7482       * The method also allows for a single argument to be passed which is a config object containing properties
7483      * which specify multiple handlers.
7484      * <p>
7485      * Code:<pre><code>
7486 el.on({
7487     'click' : {
7488         fn: this.onClick
7489         scope: this,
7490         delay: 100
7491     },
7492     'mouseover' : {
7493         fn: this.onMouseOver
7494         scope: this
7495     },
7496     'mouseout' : {
7497         fn: this.onMouseOut
7498         scope: this
7499     }
7500 });</code></pre>
7501      * <p>
7502      * Or a shorthand syntax:<br>
7503      * Code:<pre><code>
7504 el.on({
7505     'click' : this.onClick,
7506     'mouseover' : this.onMouseOver,
7507     'mouseout' : this.onMouseOut
7508     scope: this
7509 });</code></pre>
7510      */
7511         addListener : function(element, eventName, fn, scope, options){
7512             if(typeof eventName == "object"){
7513                 var o = eventName;
7514                 for(var e in o){
7515                     if(propRe.test(e)){
7516                         continue;
7517                     }
7518                     if(typeof o[e] == "function"){
7519                         // shared options
7520                         listen(element, e, o, o[e], o.scope);
7521                     }else{
7522                         // individual options
7523                         listen(element, e, o[e]);
7524                     }
7525                 }
7526                 return;
7527             }
7528             return listen(element, eventName, options, fn, scope);
7529         },
7530         
7531         /**
7532          * Removes an event handler
7533          *
7534          * @param {String/HTMLElement}   element        The id or html element to remove the 
7535          *                             event from
7536          * @param {String}   eventName     The type of event
7537          * @param {Function} fn
7538          * @return {Boolean} True if a listener was actually removed
7539          */
7540         removeListener : function(element, eventName, fn){
7541             return stopListening(element, eventName, fn);
7542         },
7543         
7544         /**
7545          * Fires when the document is ready (before onload and before images are loaded). Can be 
7546          * accessed shorthanded Roo.onReady().
7547          * @param {Function} fn        The method the event invokes
7548          * @param {Object}   scope    An  object that becomes the scope of the handler
7549          * @param {boolean}  options
7550          */
7551         onDocumentReady : function(fn, scope, options){
7552             if(docReadyState){ // if it already fired
7553                 docReadyEvent.addListener(fn, scope, options);
7554                 docReadyEvent.fire();
7555                 docReadyEvent.clearListeners();
7556                 return;
7557             }
7558             if(!docReadyEvent){
7559                 initDocReady();
7560             }
7561             docReadyEvent.addListener(fn, scope, options);
7562         },
7563         
7564         /**
7565          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7566          * @param {Function} fn        The method the event invokes
7567          * @param {Object}   scope    An object that becomes the scope of the handler
7568          * @param {boolean}  options
7569          */
7570         onWindowResize : function(fn, scope, options)
7571         {
7572             if(!resizeEvent){
7573                 resizeEvent = new Roo.util.Event();
7574                 resizeTask = new Roo.util.DelayedTask(function(){
7575                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7576                 });
7577                 E.on(window, "resize", function()
7578                 {
7579                     if (Roo.isIE) {
7580                         resizeTask.delay(50);
7581                     } else {
7582                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7583                     }
7584                 });
7585             }
7586             resizeEvent.addListener(fn, scope, options);
7587         },
7588
7589         /**
7590          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7591          * @param {Function} fn        The method the event invokes
7592          * @param {Object}   scope    An object that becomes the scope of the handler
7593          * @param {boolean}  options
7594          */
7595         onTextResize : function(fn, scope, options){
7596             if(!textEvent){
7597                 textEvent = new Roo.util.Event();
7598                 var textEl = new Roo.Element(document.createElement('div'));
7599                 textEl.dom.className = 'x-text-resize';
7600                 textEl.dom.innerHTML = 'X';
7601                 textEl.appendTo(document.body);
7602                 textSize = textEl.dom.offsetHeight;
7603                 setInterval(function(){
7604                     if(textEl.dom.offsetHeight != textSize){
7605                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7606                     }
7607                 }, this.textResizeInterval);
7608             }
7609             textEvent.addListener(fn, scope, options);
7610         },
7611
7612         /**
7613          * Removes the passed window resize listener.
7614          * @param {Function} fn        The method the event invokes
7615          * @param {Object}   scope    The scope of handler
7616          */
7617         removeResizeListener : function(fn, scope){
7618             if(resizeEvent){
7619                 resizeEvent.removeListener(fn, scope);
7620             }
7621         },
7622
7623         // private
7624         fireResize : function(){
7625             if(resizeEvent){
7626                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7627             }   
7628         },
7629         /**
7630          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7631          */
7632         ieDeferSrc : false,
7633         /**
7634          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7635          */
7636         textResizeInterval : 50
7637     };
7638     
7639     /**
7640      * Fix for doc tools
7641      * @scopeAlias pub=Roo.EventManager
7642      */
7643     
7644      /**
7645      * Appends an event handler to an element (shorthand for addListener)
7646      * @param {String/HTMLElement}   element        The html element or id to assign the
7647      * @param {String}   eventName The type of event to listen for
7648      * @param {Function} handler The method the event invokes
7649      * @param {Object}   scope (optional) The scope in which to execute the handler
7650      * function. The handler function's "this" context.
7651      * @param {Object}   options (optional) An object containing handler configuration
7652      * properties. This may contain any of the following properties:<ul>
7653      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7654      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7655      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7656      * <li>preventDefault {Boolean} True to prevent the default action</li>
7657      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7658      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7659      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7660      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7661      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7662      * by the specified number of milliseconds. If the event fires again within that time, the original
7663      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7664      * </ul><br>
7665      * <p>
7666      * <b>Combining Options</b><br>
7667      * Using the options argument, it is possible to combine different types of listeners:<br>
7668      * <br>
7669      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7670      * Code:<pre><code>
7671 el.on('click', this.onClick, this, {
7672     single: true,
7673     delay: 100,
7674     stopEvent : true,
7675     forumId: 4
7676 });</code></pre>
7677      * <p>
7678      * <b>Attaching multiple handlers in 1 call</b><br>
7679       * The method also allows for a single argument to be passed which is a config object containing properties
7680      * which specify multiple handlers.
7681      * <p>
7682      * Code:<pre><code>
7683 el.on({
7684     'click' : {
7685         fn: this.onClick
7686         scope: this,
7687         delay: 100
7688     },
7689     'mouseover' : {
7690         fn: this.onMouseOver
7691         scope: this
7692     },
7693     'mouseout' : {
7694         fn: this.onMouseOut
7695         scope: this
7696     }
7697 });</code></pre>
7698      * <p>
7699      * Or a shorthand syntax:<br>
7700      * Code:<pre><code>
7701 el.on({
7702     'click' : this.onClick,
7703     'mouseover' : this.onMouseOver,
7704     'mouseout' : this.onMouseOut
7705     scope: this
7706 });</code></pre>
7707      */
7708     pub.on = pub.addListener;
7709     pub.un = pub.removeListener;
7710
7711     pub.stoppedMouseDownEvent = new Roo.util.Event();
7712     return pub;
7713 }();
7714 /**
7715   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7716   * @param {Function} fn        The method the event invokes
7717   * @param {Object}   scope    An  object that becomes the scope of the handler
7718   * @param {boolean}  override If true, the obj passed in becomes
7719   *                             the execution scope of the listener
7720   * @member Roo
7721   * @method onReady
7722  */
7723 Roo.onReady = Roo.EventManager.onDocumentReady;
7724
7725 Roo.onReady(function(){
7726     var bd = Roo.get(document.body);
7727     if(!bd){ return; }
7728
7729     var cls = [
7730             Roo.isIE ? "roo-ie"
7731             : Roo.isIE11 ? "roo-ie11"
7732             : Roo.isEdge ? "roo-edge"
7733             : Roo.isGecko ? "roo-gecko"
7734             : Roo.isOpera ? "roo-opera"
7735             : Roo.isSafari ? "roo-safari" : ""];
7736
7737     if(Roo.isMac){
7738         cls.push("roo-mac");
7739     }
7740     if(Roo.isLinux){
7741         cls.push("roo-linux");
7742     }
7743     if(Roo.isIOS){
7744         cls.push("roo-ios");
7745     }
7746     if(Roo.isTouch){
7747         cls.push("roo-touch");
7748     }
7749     if(Roo.isBorderBox){
7750         cls.push('roo-border-box');
7751     }
7752     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7753         var p = bd.dom.parentNode;
7754         if(p){
7755             p.className += ' roo-strict';
7756         }
7757     }
7758     bd.addClass(cls.join(' '));
7759 });
7760
7761 /**
7762  * @class Roo.EventObject
7763  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7764  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7765  * Example:
7766  * <pre><code>
7767  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7768     e.preventDefault();
7769     var target = e.getTarget();
7770     ...
7771  }
7772  var myDiv = Roo.get("myDiv");
7773  myDiv.on("click", handleClick);
7774  //or
7775  Roo.EventManager.on("myDiv", 'click', handleClick);
7776  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7777  </code></pre>
7778  * @static
7779  */
7780 Roo.EventObject = function(){
7781     
7782     var E = Roo.lib.Event;
7783     
7784     // safari keypress events for special keys return bad keycodes
7785     var safariKeys = {
7786         63234 : 37, // left
7787         63235 : 39, // right
7788         63232 : 38, // up
7789         63233 : 40, // down
7790         63276 : 33, // page up
7791         63277 : 34, // page down
7792         63272 : 46, // delete
7793         63273 : 36, // home
7794         63275 : 35  // end
7795     };
7796
7797     // normalize button clicks
7798     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7799                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7800
7801     Roo.EventObjectImpl = function(e){
7802         if(e){
7803             this.setEvent(e.browserEvent || e);
7804         }
7805     };
7806     Roo.EventObjectImpl.prototype = {
7807         /**
7808          * Used to fix doc tools.
7809          * @scope Roo.EventObject.prototype
7810          */
7811             
7812
7813         
7814         
7815         /** The normal browser event */
7816         browserEvent : null,
7817         /** The button pressed in a mouse event */
7818         button : -1,
7819         /** True if the shift key was down during the event */
7820         shiftKey : false,
7821         /** True if the control key was down during the event */
7822         ctrlKey : false,
7823         /** True if the alt key was down during the event */
7824         altKey : false,
7825
7826         /** Key constant 
7827         * @type Number */
7828         BACKSPACE : 8,
7829         /** Key constant 
7830         * @type Number */
7831         TAB : 9,
7832         /** Key constant 
7833         * @type Number */
7834         RETURN : 13,
7835         /** Key constant 
7836         * @type Number */
7837         ENTER : 13,
7838         /** Key constant 
7839         * @type Number */
7840         SHIFT : 16,
7841         /** Key constant 
7842         * @type Number */
7843         CONTROL : 17,
7844         /** Key constant 
7845         * @type Number */
7846         ESC : 27,
7847         /** Key constant 
7848         * @type Number */
7849         SPACE : 32,
7850         /** Key constant 
7851         * @type Number */
7852         PAGEUP : 33,
7853         /** Key constant 
7854         * @type Number */
7855         PAGEDOWN : 34,
7856         /** Key constant 
7857         * @type Number */
7858         END : 35,
7859         /** Key constant 
7860         * @type Number */
7861         HOME : 36,
7862         /** Key constant 
7863         * @type Number */
7864         LEFT : 37,
7865         /** Key constant 
7866         * @type Number */
7867         UP : 38,
7868         /** Key constant 
7869         * @type Number */
7870         RIGHT : 39,
7871         /** Key constant 
7872         * @type Number */
7873         DOWN : 40,
7874         /** Key constant 
7875         * @type Number */
7876         DELETE : 46,
7877         /** Key constant 
7878         * @type Number */
7879         F5 : 116,
7880
7881            /** @private */
7882         setEvent : function(e){
7883             if(e == this || (e && e.browserEvent)){ // already wrapped
7884                 return e;
7885             }
7886             this.browserEvent = e;
7887             if(e){
7888                 // normalize buttons
7889                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7890                 if(e.type == 'click' && this.button == -1){
7891                     this.button = 0;
7892                 }
7893                 this.type = e.type;
7894                 this.shiftKey = e.shiftKey;
7895                 // mac metaKey behaves like ctrlKey
7896                 this.ctrlKey = e.ctrlKey || e.metaKey;
7897                 this.altKey = e.altKey;
7898                 // in getKey these will be normalized for the mac
7899                 this.keyCode = e.keyCode;
7900                 // keyup warnings on firefox.
7901                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7902                 // cache the target for the delayed and or buffered events
7903                 this.target = E.getTarget(e);
7904                 // same for XY
7905                 this.xy = E.getXY(e);
7906             }else{
7907                 this.button = -1;
7908                 this.shiftKey = false;
7909                 this.ctrlKey = false;
7910                 this.altKey = false;
7911                 this.keyCode = 0;
7912                 this.charCode =0;
7913                 this.target = null;
7914                 this.xy = [0, 0];
7915             }
7916             return this;
7917         },
7918
7919         /**
7920          * Stop the event (preventDefault and stopPropagation)
7921          */
7922         stopEvent : function(){
7923             if(this.browserEvent){
7924                 if(this.browserEvent.type == 'mousedown'){
7925                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7926                 }
7927                 E.stopEvent(this.browserEvent);
7928             }
7929         },
7930
7931         /**
7932          * Prevents the browsers default handling of the event.
7933          */
7934         preventDefault : function(){
7935             if(this.browserEvent){
7936                 E.preventDefault(this.browserEvent);
7937             }
7938         },
7939
7940         /** @private */
7941         isNavKeyPress : function(){
7942             var k = this.keyCode;
7943             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7944             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7945         },
7946
7947         isSpecialKey : function(){
7948             var k = this.keyCode;
7949             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7950             (k == 16) || (k == 17) ||
7951             (k >= 18 && k <= 20) ||
7952             (k >= 33 && k <= 35) ||
7953             (k >= 36 && k <= 39) ||
7954             (k >= 44 && k <= 45);
7955         },
7956         /**
7957          * Cancels bubbling of the event.
7958          */
7959         stopPropagation : function(){
7960             if(this.browserEvent){
7961                 if(this.type == 'mousedown'){
7962                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7963                 }
7964                 E.stopPropagation(this.browserEvent);
7965             }
7966         },
7967
7968         /**
7969          * Gets the key code for the event.
7970          * @return {Number}
7971          */
7972         getCharCode : function(){
7973             return this.charCode || this.keyCode;
7974         },
7975
7976         /**
7977          * Returns a normalized keyCode for the event.
7978          * @return {Number} The key code
7979          */
7980         getKey : function(){
7981             var k = this.keyCode || this.charCode;
7982             return Roo.isSafari ? (safariKeys[k] || k) : k;
7983         },
7984
7985         /**
7986          * Gets the x coordinate of the event.
7987          * @return {Number}
7988          */
7989         getPageX : function(){
7990             return this.xy[0];
7991         },
7992
7993         /**
7994          * Gets the y coordinate of the event.
7995          * @return {Number}
7996          */
7997         getPageY : function(){
7998             return this.xy[1];
7999         },
8000
8001         /**
8002          * Gets the time of the event.
8003          * @return {Number}
8004          */
8005         getTime : function(){
8006             if(this.browserEvent){
8007                 return E.getTime(this.browserEvent);
8008             }
8009             return null;
8010         },
8011
8012         /**
8013          * Gets the page coordinates of the event.
8014          * @return {Array} The xy values like [x, y]
8015          */
8016         getXY : function(){
8017             return this.xy;
8018         },
8019
8020         /**
8021          * Gets the target for the event.
8022          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8023          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8024                 search as a number or element (defaults to 10 || document.body)
8025          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8026          * @return {HTMLelement}
8027          */
8028         getTarget : function(selector, maxDepth, returnEl){
8029             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8030         },
8031         /**
8032          * Gets the related target.
8033          * @return {HTMLElement}
8034          */
8035         getRelatedTarget : function(){
8036             if(this.browserEvent){
8037                 return E.getRelatedTarget(this.browserEvent);
8038             }
8039             return null;
8040         },
8041
8042         /**
8043          * Normalizes mouse wheel delta across browsers
8044          * @return {Number} The delta
8045          */
8046         getWheelDelta : function(){
8047             var e = this.browserEvent;
8048             var delta = 0;
8049             if(e.wheelDelta){ /* IE/Opera. */
8050                 delta = e.wheelDelta/120;
8051             }else if(e.detail){ /* Mozilla case. */
8052                 delta = -e.detail/3;
8053             }
8054             return delta;
8055         },
8056
8057         /**
8058          * Returns true if the control, meta, shift or alt key was pressed during this event.
8059          * @return {Boolean}
8060          */
8061         hasModifier : function(){
8062             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8063         },
8064
8065         /**
8066          * Returns true if the target of this event equals el or is a child of el
8067          * @param {String/HTMLElement/Element} el
8068          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8069          * @return {Boolean}
8070          */
8071         within : function(el, related){
8072             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8073             return t && Roo.fly(el).contains(t);
8074         },
8075
8076         getPoint : function(){
8077             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8078         }
8079     };
8080
8081     return new Roo.EventObjectImpl();
8082 }();
8083             
8084     /*
8085  * Based on:
8086  * Ext JS Library 1.1.1
8087  * Copyright(c) 2006-2007, Ext JS, LLC.
8088  *
8089  * Originally Released Under LGPL - original licence link has changed is not relivant.
8090  *
8091  * Fork - LGPL
8092  * <script type="text/javascript">
8093  */
8094
8095  
8096 // was in Composite Element!??!?!
8097  
8098 (function(){
8099     var D = Roo.lib.Dom;
8100     var E = Roo.lib.Event;
8101     var A = Roo.lib.Anim;
8102
8103     // local style camelizing for speed
8104     var propCache = {};
8105     var camelRe = /(-[a-z])/gi;
8106     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8107     var view = document.defaultView;
8108
8109 /**
8110  * @class Roo.Element
8111  * Represents an Element in the DOM.<br><br>
8112  * Usage:<br>
8113 <pre><code>
8114 var el = Roo.get("my-div");
8115
8116 // or with getEl
8117 var el = getEl("my-div");
8118
8119 // or with a DOM element
8120 var el = Roo.get(myDivElement);
8121 </code></pre>
8122  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8123  * each call instead of constructing a new one.<br><br>
8124  * <b>Animations</b><br />
8125  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8126  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8127 <pre>
8128 Option    Default   Description
8129 --------- --------  ---------------------------------------------
8130 duration  .35       The duration of the animation in seconds
8131 easing    easeOut   The YUI easing method
8132 callback  none      A function to execute when the anim completes
8133 scope     this      The scope (this) of the callback function
8134 </pre>
8135 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8136 * manipulate the animation. Here's an example:
8137 <pre><code>
8138 var el = Roo.get("my-div");
8139
8140 // no animation
8141 el.setWidth(100);
8142
8143 // default animation
8144 el.setWidth(100, true);
8145
8146 // animation with some options set
8147 el.setWidth(100, {
8148     duration: 1,
8149     callback: this.foo,
8150     scope: this
8151 });
8152
8153 // using the "anim" property to get the Anim object
8154 var opt = {
8155     duration: 1,
8156     callback: this.foo,
8157     scope: this
8158 };
8159 el.setWidth(100, opt);
8160 ...
8161 if(opt.anim.isAnimated()){
8162     opt.anim.stop();
8163 }
8164 </code></pre>
8165 * <b> Composite (Collections of) Elements</b><br />
8166  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8167  * @constructor Create a new Element directly.
8168  * @param {String/HTMLElement} element
8169  * @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).
8170  */
8171     Roo.Element = function(element, forceNew)
8172     {
8173         var dom = typeof element == "string" ?
8174                 document.getElementById(element) : element;
8175         
8176         this.listeners = {};
8177         
8178         if(!dom){ // invalid id/element
8179             return null;
8180         }
8181         var id = dom.id;
8182         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8183             return Roo.Element.cache[id];
8184         }
8185
8186         /**
8187          * The DOM element
8188          * @type HTMLElement
8189          */
8190         this.dom = dom;
8191
8192         /**
8193          * The DOM element ID
8194          * @type String
8195          */
8196         this.id = id || Roo.id(dom);
8197         
8198         return this; // assumed for cctor?
8199     };
8200
8201     var El = Roo.Element;
8202
8203     El.prototype = {
8204         /**
8205          * The element's default display mode  (defaults to "") 
8206          * @type String
8207          */
8208         originalDisplay : "",
8209
8210         
8211         // note this is overridden in BS version..
8212         visibilityMode : 1, 
8213         /**
8214          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8215          * @type String
8216          */
8217         defaultUnit : "px",
8218         
8219         /**
8220          * Sets the element's visibility mode. When setVisible() is called it
8221          * will use this to determine whether to set the visibility or the display property.
8222          * @param visMode Element.VISIBILITY or Element.DISPLAY
8223          * @return {Roo.Element} this
8224          */
8225         setVisibilityMode : function(visMode){
8226             this.visibilityMode = visMode;
8227             return this;
8228         },
8229         /**
8230          * Convenience method for setVisibilityMode(Element.DISPLAY)
8231          * @param {String} display (optional) What to set display to when visible
8232          * @return {Roo.Element} this
8233          */
8234         enableDisplayMode : function(display){
8235             this.setVisibilityMode(El.DISPLAY);
8236             if(typeof display != "undefined") { this.originalDisplay = display; }
8237             return this;
8238         },
8239
8240         /**
8241          * 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)
8242          * @param {String} selector The simple selector to test
8243          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8244                 search as a number or element (defaults to 10 || document.body)
8245          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8246          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8247          */
8248         findParent : function(simpleSelector, maxDepth, returnEl){
8249             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8250             maxDepth = maxDepth || 50;
8251             if(typeof maxDepth != "number"){
8252                 stopEl = Roo.getDom(maxDepth);
8253                 maxDepth = 10;
8254             }
8255             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8256                 if(dq.is(p, simpleSelector)){
8257                     return returnEl ? Roo.get(p) : p;
8258                 }
8259                 depth++;
8260                 p = p.parentNode;
8261             }
8262             return null;
8263         },
8264
8265
8266         /**
8267          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8268          * @param {String} selector The simple selector to test
8269          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8270                 search as a number or element (defaults to 10 || document.body)
8271          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8272          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8273          */
8274         findParentNode : function(simpleSelector, maxDepth, returnEl){
8275             var p = Roo.fly(this.dom.parentNode, '_internal');
8276             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8277         },
8278         
8279         /**
8280          * Looks at  the scrollable parent element
8281          */
8282         findScrollableParent : function()
8283         {
8284             var overflowRegex = /(auto|scroll)/;
8285             
8286             if(this.getStyle('position') === 'fixed'){
8287                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8288             }
8289             
8290             var excludeStaticParent = this.getStyle('position') === "absolute";
8291             
8292             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8293                 
8294                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8295                     continue;
8296                 }
8297                 
8298                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8299                     return parent;
8300                 }
8301                 
8302                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8303                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8304                 }
8305             }
8306             
8307             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8308         },
8309
8310         /**
8311          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8312          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8313          * @param {String} selector The simple selector to test
8314          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8315                 search as a number or element (defaults to 10 || document.body)
8316          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8317          */
8318         up : function(simpleSelector, maxDepth){
8319             return this.findParentNode(simpleSelector, maxDepth, true);
8320         },
8321
8322
8323
8324         /**
8325          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8326          * @param {String} selector The simple selector to test
8327          * @return {Boolean} True if this element matches the selector, else false
8328          */
8329         is : function(simpleSelector){
8330             return Roo.DomQuery.is(this.dom, simpleSelector);
8331         },
8332
8333         /**
8334          * Perform animation on this element.
8335          * @param {Object} args The YUI animation control args
8336          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8337          * @param {Function} onComplete (optional) Function to call when animation completes
8338          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8339          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8340          * @return {Roo.Element} this
8341          */
8342         animate : function(args, duration, onComplete, easing, animType){
8343             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8344             return this;
8345         },
8346
8347         /*
8348          * @private Internal animation call
8349          */
8350         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8351             animType = animType || 'run';
8352             opt = opt || {};
8353             var anim = Roo.lib.Anim[animType](
8354                 this.dom, args,
8355                 (opt.duration || defaultDur) || .35,
8356                 (opt.easing || defaultEase) || 'easeOut',
8357                 function(){
8358                     Roo.callback(cb, this);
8359                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8360                 },
8361                 this
8362             );
8363             opt.anim = anim;
8364             return anim;
8365         },
8366
8367         // private legacy anim prep
8368         preanim : function(a, i){
8369             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8370         },
8371
8372         /**
8373          * Removes worthless text nodes
8374          * @param {Boolean} forceReclean (optional) By default the element
8375          * keeps track if it has been cleaned already so
8376          * you can call this over and over. However, if you update the element and
8377          * need to force a reclean, you can pass true.
8378          */
8379         clean : function(forceReclean){
8380             if(this.isCleaned && forceReclean !== true){
8381                 return this;
8382             }
8383             var ns = /\S/;
8384             var d = this.dom, n = d.firstChild, ni = -1;
8385             while(n){
8386                 var nx = n.nextSibling;
8387                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8388                     d.removeChild(n);
8389                 }else{
8390                     n.nodeIndex = ++ni;
8391                 }
8392                 n = nx;
8393             }
8394             this.isCleaned = true;
8395             return this;
8396         },
8397
8398         // private
8399         calcOffsetsTo : function(el){
8400             el = Roo.get(el);
8401             var d = el.dom;
8402             var restorePos = false;
8403             if(el.getStyle('position') == 'static'){
8404                 el.position('relative');
8405                 restorePos = true;
8406             }
8407             var x = 0, y =0;
8408             var op = this.dom;
8409             while(op && op != d && op.tagName != 'HTML'){
8410                 x+= op.offsetLeft;
8411                 y+= op.offsetTop;
8412                 op = op.offsetParent;
8413             }
8414             if(restorePos){
8415                 el.position('static');
8416             }
8417             return [x, y];
8418         },
8419
8420         /**
8421          * Scrolls this element into view within the passed container.
8422          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8423          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8424          * @return {Roo.Element} this
8425          */
8426         scrollIntoView : function(container, hscroll){
8427             var c = Roo.getDom(container) || document.body;
8428             var el = this.dom;
8429
8430             var o = this.calcOffsetsTo(c),
8431                 l = o[0],
8432                 t = o[1],
8433                 b = t+el.offsetHeight,
8434                 r = l+el.offsetWidth;
8435
8436             var ch = c.clientHeight;
8437             var ct = parseInt(c.scrollTop, 10);
8438             var cl = parseInt(c.scrollLeft, 10);
8439             var cb = ct + ch;
8440             var cr = cl + c.clientWidth;
8441
8442             if(t < ct){
8443                 c.scrollTop = t;
8444             }else if(b > cb){
8445                 c.scrollTop = b-ch;
8446             }
8447
8448             if(hscroll !== false){
8449                 if(l < cl){
8450                     c.scrollLeft = l;
8451                 }else if(r > cr){
8452                     c.scrollLeft = r-c.clientWidth;
8453                 }
8454             }
8455             return this;
8456         },
8457
8458         // private
8459         scrollChildIntoView : function(child, hscroll){
8460             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8461         },
8462
8463         /**
8464          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8465          * the new height may not be available immediately.
8466          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8467          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8468          * @param {Function} onComplete (optional) Function to call when animation completes
8469          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8470          * @return {Roo.Element} this
8471          */
8472         autoHeight : function(animate, duration, onComplete, easing){
8473             var oldHeight = this.getHeight();
8474             this.clip();
8475             this.setHeight(1); // force clipping
8476             setTimeout(function(){
8477                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8478                 if(!animate){
8479                     this.setHeight(height);
8480                     this.unclip();
8481                     if(typeof onComplete == "function"){
8482                         onComplete();
8483                     }
8484                 }else{
8485                     this.setHeight(oldHeight); // restore original height
8486                     this.setHeight(height, animate, duration, function(){
8487                         this.unclip();
8488                         if(typeof onComplete == "function") { onComplete(); }
8489                     }.createDelegate(this), easing);
8490                 }
8491             }.createDelegate(this), 0);
8492             return this;
8493         },
8494
8495         /**
8496          * Returns true if this element is an ancestor of the passed element
8497          * @param {HTMLElement/String} el The element to check
8498          * @return {Boolean} True if this element is an ancestor of el, else false
8499          */
8500         contains : function(el){
8501             if(!el){return false;}
8502             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8503         },
8504
8505         /**
8506          * Checks whether the element is currently visible using both visibility and display properties.
8507          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8508          * @return {Boolean} True if the element is currently visible, else false
8509          */
8510         isVisible : function(deep) {
8511             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8512             if(deep !== true || !vis){
8513                 return vis;
8514             }
8515             var p = this.dom.parentNode;
8516             while(p && p.tagName.toLowerCase() != "body"){
8517                 if(!Roo.fly(p, '_isVisible').isVisible()){
8518                     return false;
8519                 }
8520                 p = p.parentNode;
8521             }
8522             return true;
8523         },
8524
8525         /**
8526          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8527          * @param {String} selector The CSS selector
8528          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8529          * @return {CompositeElement/CompositeElementLite} The composite element
8530          */
8531         select : function(selector, unique){
8532             return El.select(selector, unique, this.dom);
8533         },
8534
8535         /**
8536          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8537          * @param {String} selector The CSS selector
8538          * @return {Array} An array of the matched nodes
8539          */
8540         query : function(selector, unique){
8541             return Roo.DomQuery.select(selector, this.dom);
8542         },
8543
8544         /**
8545          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8546          * @param {String} selector The CSS selector
8547          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8548          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8549          */
8550         child : function(selector, returnDom){
8551             var n = Roo.DomQuery.selectNode(selector, this.dom);
8552             return returnDom ? n : Roo.get(n);
8553         },
8554
8555         /**
8556          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8557          * @param {String} selector The CSS selector
8558          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8559          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8560          */
8561         down : function(selector, returnDom){
8562             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8563             return returnDom ? n : Roo.get(n);
8564         },
8565
8566         /**
8567          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8568          * @param {String} group The group the DD object is member of
8569          * @param {Object} config The DD config object
8570          * @param {Object} overrides An object containing methods to override/implement on the DD object
8571          * @return {Roo.dd.DD} The DD object
8572          */
8573         initDD : function(group, config, overrides){
8574             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8575             return Roo.apply(dd, overrides);
8576         },
8577
8578         /**
8579          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8580          * @param {String} group The group the DDProxy object is member of
8581          * @param {Object} config The DDProxy config object
8582          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8583          * @return {Roo.dd.DDProxy} The DDProxy object
8584          */
8585         initDDProxy : function(group, config, overrides){
8586             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8587             return Roo.apply(dd, overrides);
8588         },
8589
8590         /**
8591          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8592          * @param {String} group The group the DDTarget object is member of
8593          * @param {Object} config The DDTarget config object
8594          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8595          * @return {Roo.dd.DDTarget} The DDTarget object
8596          */
8597         initDDTarget : function(group, config, overrides){
8598             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8599             return Roo.apply(dd, overrides);
8600         },
8601
8602         /**
8603          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8604          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8605          * @param {Boolean} visible Whether the element is visible
8606          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8607          * @return {Roo.Element} this
8608          */
8609          setVisible : function(visible, animate){
8610             if(!animate || !A){
8611                 if(this.visibilityMode == El.DISPLAY){
8612                     this.setDisplayed(visible);
8613                 }else{
8614                     this.fixDisplay();
8615                     this.dom.style.visibility = visible ? "visible" : "hidden";
8616                 }
8617             }else{
8618                 // closure for composites
8619                 var dom = this.dom;
8620                 var visMode = this.visibilityMode;
8621                 if(visible){
8622                     this.setOpacity(.01);
8623                     this.setVisible(true);
8624                 }
8625                 this.anim({opacity: { to: (visible?1:0) }},
8626                       this.preanim(arguments, 1),
8627                       null, .35, 'easeIn', function(){
8628                          if(!visible){
8629                              if(visMode == El.DISPLAY){
8630                                  dom.style.display = "none";
8631                              }else{
8632                                  dom.style.visibility = "hidden";
8633                              }
8634                              Roo.get(dom).setOpacity(1);
8635                          }
8636                      });
8637             }
8638             return this;
8639         },
8640
8641         /**
8642          * Returns true if display is not "none"
8643          * @return {Boolean}
8644          */
8645         isDisplayed : function() {
8646             return this.getStyle("display") != "none";
8647         },
8648
8649         /**
8650          * Toggles the element's visibility or display, depending on visibility mode.
8651          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8652          * @return {Roo.Element} this
8653          */
8654         toggle : function(animate){
8655             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8656             return this;
8657         },
8658
8659         /**
8660          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8661          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8662          * @return {Roo.Element} this
8663          */
8664         setDisplayed : function(value) {
8665             if(typeof value == "boolean"){
8666                value = value ? this.originalDisplay : "none";
8667             }
8668             this.setStyle("display", value);
8669             return this;
8670         },
8671
8672         /**
8673          * Tries to focus the element. Any exceptions are caught and ignored.
8674          * @return {Roo.Element} this
8675          */
8676         focus : function() {
8677             try{
8678                 this.dom.focus();
8679             }catch(e){}
8680             return this;
8681         },
8682
8683         /**
8684          * Tries to blur the element. Any exceptions are caught and ignored.
8685          * @return {Roo.Element} this
8686          */
8687         blur : function() {
8688             try{
8689                 this.dom.blur();
8690             }catch(e){}
8691             return this;
8692         },
8693
8694         /**
8695          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8696          * @param {String/Array} className The CSS class to add, or an array of classes
8697          * @return {Roo.Element} this
8698          */
8699         addClass : function(className){
8700             if(className instanceof Array){
8701                 for(var i = 0, len = className.length; i < len; i++) {
8702                     this.addClass(className[i]);
8703                 }
8704             }else{
8705                 if(className && !this.hasClass(className)){
8706                     if (this.dom instanceof SVGElement) {
8707                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8708                     } else {
8709                         this.dom.className = this.dom.className + " " + className;
8710                     }
8711                 }
8712             }
8713             return this;
8714         },
8715
8716         /**
8717          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8718          * @param {String/Array} className The CSS class to add, or an array of classes
8719          * @return {Roo.Element} this
8720          */
8721         radioClass : function(className){
8722             var siblings = this.dom.parentNode.childNodes;
8723             for(var i = 0; i < siblings.length; i++) {
8724                 var s = siblings[i];
8725                 if(s.nodeType == 1){
8726                     Roo.get(s).removeClass(className);
8727                 }
8728             }
8729             this.addClass(className);
8730             return this;
8731         },
8732
8733         /**
8734          * Removes one or more CSS classes from the element.
8735          * @param {String/Array} className The CSS class to remove, or an array of classes
8736          * @return {Roo.Element} this
8737          */
8738         removeClass : function(className){
8739             
8740             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8741             if(!className || !cn){
8742                 return this;
8743             }
8744             if(className instanceof Array){
8745                 for(var i = 0, len = className.length; i < len; i++) {
8746                     this.removeClass(className[i]);
8747                 }
8748             }else{
8749                 if(this.hasClass(className)){
8750                     var re = this.classReCache[className];
8751                     if (!re) {
8752                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8753                        this.classReCache[className] = re;
8754                     }
8755                     if (this.dom instanceof SVGElement) {
8756                         this.dom.className.baseVal = cn.replace(re, " ");
8757                     } else {
8758                         this.dom.className = cn.replace(re, " ");
8759                     }
8760                 }
8761             }
8762             return this;
8763         },
8764
8765         // private
8766         classReCache: {},
8767
8768         /**
8769          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8770          * @param {String} className The CSS class to toggle
8771          * @return {Roo.Element} this
8772          */
8773         toggleClass : function(className){
8774             if(this.hasClass(className)){
8775                 this.removeClass(className);
8776             }else{
8777                 this.addClass(className);
8778             }
8779             return this;
8780         },
8781
8782         /**
8783          * Checks if the specified CSS class exists on this element's DOM node.
8784          * @param {String} className The CSS class to check for
8785          * @return {Boolean} True if the class exists, else false
8786          */
8787         hasClass : function(className){
8788             if (this.dom instanceof SVGElement) {
8789                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8790             } 
8791             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8792         },
8793
8794         /**
8795          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8796          * @param {String} oldClassName The CSS class to replace
8797          * @param {String} newClassName The replacement CSS class
8798          * @return {Roo.Element} this
8799          */
8800         replaceClass : function(oldClassName, newClassName){
8801             this.removeClass(oldClassName);
8802             this.addClass(newClassName);
8803             return this;
8804         },
8805
8806         /**
8807          * Returns an object with properties matching the styles requested.
8808          * For example, el.getStyles('color', 'font-size', 'width') might return
8809          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8810          * @param {String} style1 A style name
8811          * @param {String} style2 A style name
8812          * @param {String} etc.
8813          * @return {Object} The style object
8814          */
8815         getStyles : function(){
8816             var a = arguments, len = a.length, r = {};
8817             for(var i = 0; i < len; i++){
8818                 r[a[i]] = this.getStyle(a[i]);
8819             }
8820             return r;
8821         },
8822
8823         /**
8824          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8825          * @param {String} property The style property whose value is returned.
8826          * @return {String} The current value of the style property for this element.
8827          */
8828         getStyle : function(){
8829             return view && view.getComputedStyle ?
8830                 function(prop){
8831                     var el = this.dom, v, cs, camel;
8832                     if(prop == 'float'){
8833                         prop = "cssFloat";
8834                     }
8835                     if(el.style && (v = el.style[prop])){
8836                         return v;
8837                     }
8838                     if(cs = view.getComputedStyle(el, "")){
8839                         if(!(camel = propCache[prop])){
8840                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8841                         }
8842                         return cs[camel];
8843                     }
8844                     return null;
8845                 } :
8846                 function(prop){
8847                     var el = this.dom, v, cs, camel;
8848                     if(prop == 'opacity'){
8849                         if(typeof el.style.filter == 'string'){
8850                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8851                             if(m){
8852                                 var fv = parseFloat(m[1]);
8853                                 if(!isNaN(fv)){
8854                                     return fv ? fv / 100 : 0;
8855                                 }
8856                             }
8857                         }
8858                         return 1;
8859                     }else if(prop == 'float'){
8860                         prop = "styleFloat";
8861                     }
8862                     if(!(camel = propCache[prop])){
8863                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8864                     }
8865                     if(v = el.style[camel]){
8866                         return v;
8867                     }
8868                     if(cs = el.currentStyle){
8869                         return cs[camel];
8870                     }
8871                     return null;
8872                 };
8873         }(),
8874
8875         /**
8876          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8877          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8878          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8879          * @return {Roo.Element} this
8880          */
8881         setStyle : function(prop, value){
8882             if(typeof prop == "string"){
8883                 
8884                 if (prop == 'float') {
8885                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8886                     return this;
8887                 }
8888                 
8889                 var camel;
8890                 if(!(camel = propCache[prop])){
8891                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8892                 }
8893                 
8894                 if(camel == 'opacity') {
8895                     this.setOpacity(value);
8896                 }else{
8897                     this.dom.style[camel] = value;
8898                 }
8899             }else{
8900                 for(var style in prop){
8901                     if(typeof prop[style] != "function"){
8902                        this.setStyle(style, prop[style]);
8903                     }
8904                 }
8905             }
8906             return this;
8907         },
8908
8909         /**
8910          * More flexible version of {@link #setStyle} for setting style properties.
8911          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8912          * a function which returns such a specification.
8913          * @return {Roo.Element} this
8914          */
8915         applyStyles : function(style){
8916             Roo.DomHelper.applyStyles(this.dom, style);
8917             return this;
8918         },
8919
8920         /**
8921           * 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).
8922           * @return {Number} The X position of the element
8923           */
8924         getX : function(){
8925             return D.getX(this.dom);
8926         },
8927
8928         /**
8929           * 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).
8930           * @return {Number} The Y position of the element
8931           */
8932         getY : function(){
8933             return D.getY(this.dom);
8934         },
8935
8936         /**
8937           * 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).
8938           * @return {Array} The XY position of the element
8939           */
8940         getXY : function(){
8941             return D.getXY(this.dom);
8942         },
8943
8944         /**
8945          * 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).
8946          * @param {Number} The X position of the element
8947          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8948          * @return {Roo.Element} this
8949          */
8950         setX : function(x, animate){
8951             if(!animate || !A){
8952                 D.setX(this.dom, x);
8953             }else{
8954                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8955             }
8956             return this;
8957         },
8958
8959         /**
8960          * 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).
8961          * @param {Number} The Y position of the element
8962          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8963          * @return {Roo.Element} this
8964          */
8965         setY : function(y, animate){
8966             if(!animate || !A){
8967                 D.setY(this.dom, y);
8968             }else{
8969                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8970             }
8971             return this;
8972         },
8973
8974         /**
8975          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8976          * @param {String} left The left CSS property value
8977          * @return {Roo.Element} this
8978          */
8979         setLeft : function(left){
8980             this.setStyle("left", this.addUnits(left));
8981             return this;
8982         },
8983
8984         /**
8985          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8986          * @param {String} top The top CSS property value
8987          * @return {Roo.Element} this
8988          */
8989         setTop : function(top){
8990             this.setStyle("top", this.addUnits(top));
8991             return this;
8992         },
8993
8994         /**
8995          * Sets the element's CSS right style.
8996          * @param {String} right The right CSS property value
8997          * @return {Roo.Element} this
8998          */
8999         setRight : function(right){
9000             this.setStyle("right", this.addUnits(right));
9001             return this;
9002         },
9003
9004         /**
9005          * Sets the element's CSS bottom style.
9006          * @param {String} bottom The bottom CSS property value
9007          * @return {Roo.Element} this
9008          */
9009         setBottom : function(bottom){
9010             this.setStyle("bottom", this.addUnits(bottom));
9011             return this;
9012         },
9013
9014         /**
9015          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9016          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9017          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9018          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9019          * @return {Roo.Element} this
9020          */
9021         setXY : function(pos, animate){
9022             if(!animate || !A){
9023                 D.setXY(this.dom, pos);
9024             }else{
9025                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9026             }
9027             return this;
9028         },
9029
9030         /**
9031          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9032          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9033          * @param {Number} x X value for new position (coordinates are page-based)
9034          * @param {Number} y Y value for new position (coordinates are page-based)
9035          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9036          * @return {Roo.Element} this
9037          */
9038         setLocation : function(x, y, animate){
9039             this.setXY([x, y], this.preanim(arguments, 2));
9040             return this;
9041         },
9042
9043         /**
9044          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9045          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9046          * @param {Number} x X value for new position (coordinates are page-based)
9047          * @param {Number} y Y value for new position (coordinates are page-based)
9048          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9049          * @return {Roo.Element} this
9050          */
9051         moveTo : function(x, y, animate){
9052             this.setXY([x, y], this.preanim(arguments, 2));
9053             return this;
9054         },
9055
9056         /**
9057          * Returns the region of the given element.
9058          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9059          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9060          */
9061         getRegion : function(){
9062             return D.getRegion(this.dom);
9063         },
9064
9065         /**
9066          * Returns the offset height of the element
9067          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9068          * @return {Number} The element's height
9069          */
9070         getHeight : function(contentHeight){
9071             var h = this.dom.offsetHeight || 0;
9072             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9073         },
9074
9075         /**
9076          * Returns the offset width of the element
9077          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9078          * @return {Number} The element's width
9079          */
9080         getWidth : function(contentWidth){
9081             var w = this.dom.offsetWidth || 0;
9082             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9083         },
9084
9085         /**
9086          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9087          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9088          * if a height has not been set using CSS.
9089          * @return {Number}
9090          */
9091         getComputedHeight : function(){
9092             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9093             if(!h){
9094                 h = parseInt(this.getStyle('height'), 10) || 0;
9095                 if(!this.isBorderBox()){
9096                     h += this.getFrameWidth('tb');
9097                 }
9098             }
9099             return h;
9100         },
9101
9102         /**
9103          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9104          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9105          * if a width has not been set using CSS.
9106          * @return {Number}
9107          */
9108         getComputedWidth : function(){
9109             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9110             if(!w){
9111                 w = parseInt(this.getStyle('width'), 10) || 0;
9112                 if(!this.isBorderBox()){
9113                     w += this.getFrameWidth('lr');
9114                 }
9115             }
9116             return w;
9117         },
9118
9119         /**
9120          * Returns the size of the element.
9121          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9122          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9123          */
9124         getSize : function(contentSize){
9125             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9126         },
9127
9128         /**
9129          * Returns the width and height of the viewport.
9130          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9131          */
9132         getViewSize : function(){
9133             var d = this.dom, doc = document, aw = 0, ah = 0;
9134             if(d == doc || d == doc.body){
9135                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9136             }else{
9137                 return {
9138                     width : d.clientWidth,
9139                     height: d.clientHeight
9140                 };
9141             }
9142         },
9143
9144         /**
9145          * Returns the value of the "value" attribute
9146          * @param {Boolean} asNumber true to parse the value as a number
9147          * @return {String/Number}
9148          */
9149         getValue : function(asNumber){
9150             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9151         },
9152
9153         // private
9154         adjustWidth : function(width){
9155             if(typeof width == "number"){
9156                 if(this.autoBoxAdjust && !this.isBorderBox()){
9157                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9158                 }
9159                 if(width < 0){
9160                     width = 0;
9161                 }
9162             }
9163             return width;
9164         },
9165
9166         // private
9167         adjustHeight : function(height){
9168             if(typeof height == "number"){
9169                if(this.autoBoxAdjust && !this.isBorderBox()){
9170                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9171                }
9172                if(height < 0){
9173                    height = 0;
9174                }
9175             }
9176             return height;
9177         },
9178
9179         /**
9180          * Set the width of the element
9181          * @param {Number} width The new width
9182          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9183          * @return {Roo.Element} this
9184          */
9185         setWidth : function(width, animate){
9186             width = this.adjustWidth(width);
9187             if(!animate || !A){
9188                 this.dom.style.width = this.addUnits(width);
9189             }else{
9190                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9191             }
9192             return this;
9193         },
9194
9195         /**
9196          * Set the height of the element
9197          * @param {Number} height The new height
9198          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9199          * @return {Roo.Element} this
9200          */
9201          setHeight : function(height, animate){
9202             height = this.adjustHeight(height);
9203             if(!animate || !A){
9204                 this.dom.style.height = this.addUnits(height);
9205             }else{
9206                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9207             }
9208             return this;
9209         },
9210
9211         /**
9212          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9213          * @param {Number} width The new width
9214          * @param {Number} height The new height
9215          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9216          * @return {Roo.Element} this
9217          */
9218          setSize : function(width, height, animate){
9219             if(typeof width == "object"){ // in case of object from getSize()
9220                 height = width.height; width = width.width;
9221             }
9222             width = this.adjustWidth(width); height = this.adjustHeight(height);
9223             if(!animate || !A){
9224                 this.dom.style.width = this.addUnits(width);
9225                 this.dom.style.height = this.addUnits(height);
9226             }else{
9227                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9228             }
9229             return this;
9230         },
9231
9232         /**
9233          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9234          * @param {Number} x X value for new position (coordinates are page-based)
9235          * @param {Number} y Y value for new position (coordinates are page-based)
9236          * @param {Number} width The new width
9237          * @param {Number} height The new height
9238          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9239          * @return {Roo.Element} this
9240          */
9241         setBounds : function(x, y, width, height, animate){
9242             if(!animate || !A){
9243                 this.setSize(width, height);
9244                 this.setLocation(x, y);
9245             }else{
9246                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9247                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9248                               this.preanim(arguments, 4), 'motion');
9249             }
9250             return this;
9251         },
9252
9253         /**
9254          * 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.
9255          * @param {Roo.lib.Region} region The region to fill
9256          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9257          * @return {Roo.Element} this
9258          */
9259         setRegion : function(region, animate){
9260             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9261             return this;
9262         },
9263
9264         /**
9265          * Appends an event handler
9266          *
9267          * @param {String}   eventName     The type of event to append
9268          * @param {Function} fn        The method the event invokes
9269          * @param {Object} scope       (optional) The scope (this object) of the fn
9270          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9271          */
9272         addListener : function(eventName, fn, scope, options)
9273         {
9274             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9275                 this.addListener('touchstart', this.onTapHandler, this);
9276             }
9277             
9278             // we need to handle a special case where dom element is a svg element.
9279             // in this case we do not actua
9280             if (!this.dom) {
9281                 return;
9282             }
9283             
9284             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9285                 if (typeof(this.listeners[eventName]) == 'undefined') {
9286                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9287                 }
9288                 this.listeners[eventName].addListener(fn, scope, options);
9289                 return;
9290             }
9291             
9292                 
9293             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9294             
9295             
9296         },
9297         tapedTwice : false,
9298         onTapHandler : function(event)
9299         {
9300             if(!this.tapedTwice) {
9301                 this.tapedTwice = true;
9302                 var s = this;
9303                 setTimeout( function() {
9304                     s.tapedTwice = false;
9305                 }, 300 );
9306                 return;
9307             }
9308             event.preventDefault();
9309             var revent = new MouseEvent('dblclick',  {
9310                 view: window,
9311                 bubbles: true,
9312                 cancelable: true
9313             });
9314              
9315             this.dom.dispatchEvent(revent);
9316             //action on double tap goes below
9317              
9318         }, 
9319  
9320         /**
9321          * Removes an event handler from this element
9322          * @param {String} eventName the type of event to remove
9323          * @param {Function} fn the method the event invokes
9324          * @param {Function} scope (needed for svg fake listeners)
9325          * @return {Roo.Element} this
9326          */
9327         removeListener : function(eventName, fn, scope){
9328             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9329             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9330                 return this;
9331             }
9332             this.listeners[eventName].removeListener(fn, scope);
9333             return this;
9334         },
9335
9336         /**
9337          * Removes all previous added listeners from this element
9338          * @return {Roo.Element} this
9339          */
9340         removeAllListeners : function(){
9341             E.purgeElement(this.dom);
9342             this.listeners = {};
9343             return this;
9344         },
9345
9346         relayEvent : function(eventName, observable){
9347             this.on(eventName, function(e){
9348                 observable.fireEvent(eventName, e);
9349             });
9350         },
9351
9352         
9353         /**
9354          * Set the opacity of the element
9355          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9356          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9357          * @return {Roo.Element} this
9358          */
9359          setOpacity : function(opacity, animate){
9360             if(!animate || !A){
9361                 var s = this.dom.style;
9362                 if(Roo.isIE){
9363                     s.zoom = 1;
9364                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9365                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9366                 }else{
9367                     s.opacity = opacity;
9368                 }
9369             }else{
9370                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9371             }
9372             return this;
9373         },
9374
9375         /**
9376          * Gets the left X coordinate
9377          * @param {Boolean} local True to get the local css position instead of page coordinate
9378          * @return {Number}
9379          */
9380         getLeft : function(local){
9381             if(!local){
9382                 return this.getX();
9383             }else{
9384                 return parseInt(this.getStyle("left"), 10) || 0;
9385             }
9386         },
9387
9388         /**
9389          * Gets the right X coordinate of the element (element X position + element width)
9390          * @param {Boolean} local True to get the local css position instead of page coordinate
9391          * @return {Number}
9392          */
9393         getRight : function(local){
9394             if(!local){
9395                 return this.getX() + this.getWidth();
9396             }else{
9397                 return (this.getLeft(true) + this.getWidth()) || 0;
9398             }
9399         },
9400
9401         /**
9402          * Gets the top Y coordinate
9403          * @param {Boolean} local True to get the local css position instead of page coordinate
9404          * @return {Number}
9405          */
9406         getTop : function(local) {
9407             if(!local){
9408                 return this.getY();
9409             }else{
9410                 return parseInt(this.getStyle("top"), 10) || 0;
9411             }
9412         },
9413
9414         /**
9415          * Gets the bottom Y coordinate of the element (element Y position + element height)
9416          * @param {Boolean} local True to get the local css position instead of page coordinate
9417          * @return {Number}
9418          */
9419         getBottom : function(local){
9420             if(!local){
9421                 return this.getY() + this.getHeight();
9422             }else{
9423                 return (this.getTop(true) + this.getHeight()) || 0;
9424             }
9425         },
9426
9427         /**
9428         * Initializes positioning on this element. If a desired position is not passed, it will make the
9429         * the element positioned relative IF it is not already positioned.
9430         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9431         * @param {Number} zIndex (optional) The zIndex to apply
9432         * @param {Number} x (optional) Set the page X position
9433         * @param {Number} y (optional) Set the page Y position
9434         */
9435         position : function(pos, zIndex, x, y){
9436             if(!pos){
9437                if(this.getStyle('position') == 'static'){
9438                    this.setStyle('position', 'relative');
9439                }
9440             }else{
9441                 this.setStyle("position", pos);
9442             }
9443             if(zIndex){
9444                 this.setStyle("z-index", zIndex);
9445             }
9446             if(x !== undefined && y !== undefined){
9447                 this.setXY([x, y]);
9448             }else if(x !== undefined){
9449                 this.setX(x);
9450             }else if(y !== undefined){
9451                 this.setY(y);
9452             }
9453         },
9454
9455         /**
9456         * Clear positioning back to the default when the document was loaded
9457         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9458         * @return {Roo.Element} this
9459          */
9460         clearPositioning : function(value){
9461             value = value ||'';
9462             this.setStyle({
9463                 "left": value,
9464                 "right": value,
9465                 "top": value,
9466                 "bottom": value,
9467                 "z-index": "",
9468                 "position" : "static"
9469             });
9470             return this;
9471         },
9472
9473         /**
9474         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9475         * snapshot before performing an update and then restoring the element.
9476         * @return {Object}
9477         */
9478         getPositioning : function(){
9479             var l = this.getStyle("left");
9480             var t = this.getStyle("top");
9481             return {
9482                 "position" : this.getStyle("position"),
9483                 "left" : l,
9484                 "right" : l ? "" : this.getStyle("right"),
9485                 "top" : t,
9486                 "bottom" : t ? "" : this.getStyle("bottom"),
9487                 "z-index" : this.getStyle("z-index")
9488             };
9489         },
9490
9491         /**
9492          * Gets the width of the border(s) for the specified side(s)
9493          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9494          * passing lr would get the border (l)eft width + the border (r)ight width.
9495          * @return {Number} The width of the sides passed added together
9496          */
9497         getBorderWidth : function(side){
9498             return this.addStyles(side, El.borders);
9499         },
9500
9501         /**
9502          * Gets the width of the padding(s) for the specified side(s)
9503          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9504          * passing lr would get the padding (l)eft + the padding (r)ight.
9505          * @return {Number} The padding of the sides passed added together
9506          */
9507         getPadding : function(side){
9508             return this.addStyles(side, El.paddings);
9509         },
9510
9511         /**
9512         * Set positioning with an object returned by getPositioning().
9513         * @param {Object} posCfg
9514         * @return {Roo.Element} this
9515          */
9516         setPositioning : function(pc){
9517             this.applyStyles(pc);
9518             if(pc.right == "auto"){
9519                 this.dom.style.right = "";
9520             }
9521             if(pc.bottom == "auto"){
9522                 this.dom.style.bottom = "";
9523             }
9524             return this;
9525         },
9526
9527         // private
9528         fixDisplay : function(){
9529             if(this.getStyle("display") == "none"){
9530                 this.setStyle("visibility", "hidden");
9531                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9532                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9533                     this.setStyle("display", "block");
9534                 }
9535             }
9536         },
9537
9538         /**
9539          * Quick set left and top adding default units
9540          * @param {String} left The left CSS property value
9541          * @param {String} top The top CSS property value
9542          * @return {Roo.Element} this
9543          */
9544          setLeftTop : function(left, top){
9545             this.dom.style.left = this.addUnits(left);
9546             this.dom.style.top = this.addUnits(top);
9547             return this;
9548         },
9549
9550         /**
9551          * Move this element relative to its current position.
9552          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9553          * @param {Number} distance How far to move the element in pixels
9554          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9555          * @return {Roo.Element} this
9556          */
9557          move : function(direction, distance, animate){
9558             var xy = this.getXY();
9559             direction = direction.toLowerCase();
9560             switch(direction){
9561                 case "l":
9562                 case "left":
9563                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9564                     break;
9565                case "r":
9566                case "right":
9567                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9568                     break;
9569                case "t":
9570                case "top":
9571                case "up":
9572                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9573                     break;
9574                case "b":
9575                case "bottom":
9576                case "down":
9577                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9578                     break;
9579             }
9580             return this;
9581         },
9582
9583         /**
9584          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9585          * @return {Roo.Element} this
9586          */
9587         clip : function(){
9588             if(!this.isClipped){
9589                this.isClipped = true;
9590                this.originalClip = {
9591                    "o": this.getStyle("overflow"),
9592                    "x": this.getStyle("overflow-x"),
9593                    "y": this.getStyle("overflow-y")
9594                };
9595                this.setStyle("overflow", "hidden");
9596                this.setStyle("overflow-x", "hidden");
9597                this.setStyle("overflow-y", "hidden");
9598             }
9599             return this;
9600         },
9601
9602         /**
9603          *  Return clipping (overflow) to original clipping before clip() was called
9604          * @return {Roo.Element} this
9605          */
9606         unclip : function(){
9607             if(this.isClipped){
9608                 this.isClipped = false;
9609                 var o = this.originalClip;
9610                 if(o.o){this.setStyle("overflow", o.o);}
9611                 if(o.x){this.setStyle("overflow-x", o.x);}
9612                 if(o.y){this.setStyle("overflow-y", o.y);}
9613             }
9614             return this;
9615         },
9616
9617
9618         /**
9619          * Gets the x,y coordinates specified by the anchor position on the element.
9620          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9621          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9622          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9623          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9624          * @return {Array} [x, y] An array containing the element's x and y coordinates
9625          */
9626         getAnchorXY : function(anchor, local, s){
9627             //Passing a different size is useful for pre-calculating anchors,
9628             //especially for anchored animations that change the el size.
9629
9630             var w, h, vp = false;
9631             if(!s){
9632                 var d = this.dom;
9633                 if(d == document.body || d == document){
9634                     vp = true;
9635                     w = D.getViewWidth(); h = D.getViewHeight();
9636                 }else{
9637                     w = this.getWidth(); h = this.getHeight();
9638                 }
9639             }else{
9640                 w = s.width;  h = s.height;
9641             }
9642             var x = 0, y = 0, r = Math.round;
9643             switch((anchor || "tl").toLowerCase()){
9644                 case "c":
9645                     x = r(w*.5);
9646                     y = r(h*.5);
9647                 break;
9648                 case "t":
9649                     x = r(w*.5);
9650                     y = 0;
9651                 break;
9652                 case "l":
9653                     x = 0;
9654                     y = r(h*.5);
9655                 break;
9656                 case "r":
9657                     x = w;
9658                     y = r(h*.5);
9659                 break;
9660                 case "b":
9661                     x = r(w*.5);
9662                     y = h;
9663                 break;
9664                 case "tl":
9665                     x = 0;
9666                     y = 0;
9667                 break;
9668                 case "bl":
9669                     x = 0;
9670                     y = h;
9671                 break;
9672                 case "br":
9673                     x = w;
9674                     y = h;
9675                 break;
9676                 case "tr":
9677                     x = w;
9678                     y = 0;
9679                 break;
9680             }
9681             if(local === true){
9682                 return [x, y];
9683             }
9684             if(vp){
9685                 var sc = this.getScroll();
9686                 return [x + sc.left, y + sc.top];
9687             }
9688             //Add the element's offset xy
9689             var o = this.getXY();
9690             return [x+o[0], y+o[1]];
9691         },
9692
9693         /**
9694          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9695          * supported position values.
9696          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9697          * @param {String} position The position to align to.
9698          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9699          * @return {Array} [x, y]
9700          */
9701         getAlignToXY : function(el, p, o)
9702         {
9703             el = Roo.get(el);
9704             var d = this.dom;
9705             if(!el.dom){
9706                 throw "Element.alignTo with an element that doesn't exist";
9707             }
9708             var c = false; //constrain to viewport
9709             var p1 = "", p2 = "";
9710             o = o || [0,0];
9711
9712             if(!p){
9713                 p = "tl-bl";
9714             }else if(p == "?"){
9715                 p = "tl-bl?";
9716             }else if(p.indexOf("-") == -1){
9717                 p = "tl-" + p;
9718             }
9719             p = p.toLowerCase();
9720             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9721             if(!m){
9722                throw "Element.alignTo with an invalid alignment " + p;
9723             }
9724             p1 = m[1]; p2 = m[2]; c = !!m[3];
9725
9726             //Subtract the aligned el's internal xy from the target's offset xy
9727             //plus custom offset to get the aligned el's new offset xy
9728             var a1 = this.getAnchorXY(p1, true);
9729             var a2 = el.getAnchorXY(p2, false);
9730             var x = a2[0] - a1[0] + o[0];
9731             var y = a2[1] - a1[1] + o[1];
9732             if(c){
9733                 //constrain the aligned el to viewport if necessary
9734                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9735                 // 5px of margin for ie
9736                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9737
9738                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9739                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9740                 //otherwise swap the aligned el to the opposite border of the target.
9741                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9742                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9743                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9744                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9745
9746                var doc = document;
9747                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9748                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9749
9750                if((x+w) > dw + scrollX){
9751                     x = swapX ? r.left-w : dw+scrollX-w;
9752                 }
9753                if(x < scrollX){
9754                    x = swapX ? r.right : scrollX;
9755                }
9756                if((y+h) > dh + scrollY){
9757                     y = swapY ? r.top-h : dh+scrollY-h;
9758                 }
9759                if (y < scrollY){
9760                    y = swapY ? r.bottom : scrollY;
9761                }
9762             }
9763             return [x,y];
9764         },
9765
9766         // private
9767         getConstrainToXY : function(){
9768             var os = {top:0, left:0, bottom:0, right: 0};
9769
9770             return function(el, local, offsets, proposedXY){
9771                 el = Roo.get(el);
9772                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9773
9774                 var vw, vh, vx = 0, vy = 0;
9775                 if(el.dom == document.body || el.dom == document){
9776                     vw = Roo.lib.Dom.getViewWidth();
9777                     vh = Roo.lib.Dom.getViewHeight();
9778                 }else{
9779                     vw = el.dom.clientWidth;
9780                     vh = el.dom.clientHeight;
9781                     if(!local){
9782                         var vxy = el.getXY();
9783                         vx = vxy[0];
9784                         vy = vxy[1];
9785                     }
9786                 }
9787
9788                 var s = el.getScroll();
9789
9790                 vx += offsets.left + s.left;
9791                 vy += offsets.top + s.top;
9792
9793                 vw -= offsets.right;
9794                 vh -= offsets.bottom;
9795
9796                 var vr = vx+vw;
9797                 var vb = vy+vh;
9798
9799                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9800                 var x = xy[0], y = xy[1];
9801                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9802
9803                 // only move it if it needs it
9804                 var moved = false;
9805
9806                 // first validate right/bottom
9807                 if((x + w) > vr){
9808                     x = vr - w;
9809                     moved = true;
9810                 }
9811                 if((y + h) > vb){
9812                     y = vb - h;
9813                     moved = true;
9814                 }
9815                 // then make sure top/left isn't negative
9816                 if(x < vx){
9817                     x = vx;
9818                     moved = true;
9819                 }
9820                 if(y < vy){
9821                     y = vy;
9822                     moved = true;
9823                 }
9824                 return moved ? [x, y] : false;
9825             };
9826         }(),
9827
9828         // private
9829         adjustForConstraints : function(xy, parent, offsets){
9830             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9831         },
9832
9833         /**
9834          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9835          * document it aligns it to the viewport.
9836          * The position parameter is optional, and can be specified in any one of the following formats:
9837          * <ul>
9838          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9839          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9840          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9841          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9842          *   <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
9843          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9844          * </ul>
9845          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9846          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9847          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9848          * that specified in order to enforce the viewport constraints.
9849          * Following are all of the supported anchor positions:
9850     <pre>
9851     Value  Description
9852     -----  -----------------------------
9853     tl     The top left corner (default)
9854     t      The center of the top edge
9855     tr     The top right corner
9856     l      The center of the left edge
9857     c      In the center of the element
9858     r      The center of the right edge
9859     bl     The bottom left corner
9860     b      The center of the bottom edge
9861     br     The bottom right corner
9862     </pre>
9863     Example Usage:
9864     <pre><code>
9865     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9866     el.alignTo("other-el");
9867
9868     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9869     el.alignTo("other-el", "tr?");
9870
9871     // align the bottom right corner of el with the center left edge of other-el
9872     el.alignTo("other-el", "br-l?");
9873
9874     // align the center of el with the bottom left corner of other-el and
9875     // adjust the x position by -6 pixels (and the y position by 0)
9876     el.alignTo("other-el", "c-bl", [-6, 0]);
9877     </code></pre>
9878          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9879          * @param {String} position The position to align to.
9880          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9881          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9882          * @return {Roo.Element} this
9883          */
9884         alignTo : function(element, position, offsets, animate){
9885             var xy = this.getAlignToXY(element, position, offsets);
9886             this.setXY(xy, this.preanim(arguments, 3));
9887             return this;
9888         },
9889
9890         /**
9891          * Anchors an element to another element and realigns it when the window is resized.
9892          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9893          * @param {String} position The position to align to.
9894          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9895          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9896          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9897          * is a number, it is used as the buffer delay (defaults to 50ms).
9898          * @param {Function} callback The function to call after the animation finishes
9899          * @return {Roo.Element} this
9900          */
9901         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9902             var action = function(){
9903                 this.alignTo(el, alignment, offsets, animate);
9904                 Roo.callback(callback, this);
9905             };
9906             Roo.EventManager.onWindowResize(action, this);
9907             var tm = typeof monitorScroll;
9908             if(tm != 'undefined'){
9909                 Roo.EventManager.on(window, 'scroll', action, this,
9910                     {buffer: tm == 'number' ? monitorScroll : 50});
9911             }
9912             action.call(this); // align immediately
9913             return this;
9914         },
9915         /**
9916          * Clears any opacity settings from this element. Required in some cases for IE.
9917          * @return {Roo.Element} this
9918          */
9919         clearOpacity : function(){
9920             if (window.ActiveXObject) {
9921                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9922                     this.dom.style.filter = "";
9923                 }
9924             } else {
9925                 this.dom.style.opacity = "";
9926                 this.dom.style["-moz-opacity"] = "";
9927                 this.dom.style["-khtml-opacity"] = "";
9928             }
9929             return this;
9930         },
9931
9932         /**
9933          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9934          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9935          * @return {Roo.Element} this
9936          */
9937         hide : function(animate){
9938             this.setVisible(false, this.preanim(arguments, 0));
9939             return this;
9940         },
9941
9942         /**
9943         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9944         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9945          * @return {Roo.Element} this
9946          */
9947         show : function(animate){
9948             this.setVisible(true, this.preanim(arguments, 0));
9949             return this;
9950         },
9951
9952         /**
9953          * @private Test if size has a unit, otherwise appends the default
9954          */
9955         addUnits : function(size){
9956             return Roo.Element.addUnits(size, this.defaultUnit);
9957         },
9958
9959         /**
9960          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9961          * @return {Roo.Element} this
9962          */
9963         beginMeasure : function(){
9964             var el = this.dom;
9965             if(el.offsetWidth || el.offsetHeight){
9966                 return this; // offsets work already
9967             }
9968             var changed = [];
9969             var p = this.dom, b = document.body; // start with this element
9970             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9971                 var pe = Roo.get(p);
9972                 if(pe.getStyle('display') == 'none'){
9973                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9974                     p.style.visibility = "hidden";
9975                     p.style.display = "block";
9976                 }
9977                 p = p.parentNode;
9978             }
9979             this._measureChanged = changed;
9980             return this;
9981
9982         },
9983
9984         /**
9985          * Restores displays to before beginMeasure was called
9986          * @return {Roo.Element} this
9987          */
9988         endMeasure : function(){
9989             var changed = this._measureChanged;
9990             if(changed){
9991                 for(var i = 0, len = changed.length; i < len; i++) {
9992                     var r = changed[i];
9993                     r.el.style.visibility = r.visibility;
9994                     r.el.style.display = "none";
9995                 }
9996                 this._measureChanged = null;
9997             }
9998             return this;
9999         },
10000
10001         /**
10002         * Update the innerHTML of this element, optionally searching for and processing scripts
10003         * @param {String} html The new HTML
10004         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10005         * @param {Function} callback For async script loading you can be noticed when the update completes
10006         * @return {Roo.Element} this
10007          */
10008         update : function(html, loadScripts, callback){
10009             if(typeof html == "undefined"){
10010                 html = "";
10011             }
10012             if(loadScripts !== true){
10013                 this.dom.innerHTML = html;
10014                 if(typeof callback == "function"){
10015                     callback();
10016                 }
10017                 return this;
10018             }
10019             var id = Roo.id();
10020             var dom = this.dom;
10021
10022             html += '<span id="' + id + '"></span>';
10023
10024             E.onAvailable(id, function(){
10025                 var hd = document.getElementsByTagName("head")[0];
10026                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10027                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10028                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10029
10030                 var match;
10031                 while(match = re.exec(html)){
10032                     var attrs = match[1];
10033                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10034                     if(srcMatch && srcMatch[2]){
10035                        var s = document.createElement("script");
10036                        s.src = srcMatch[2];
10037                        var typeMatch = attrs.match(typeRe);
10038                        if(typeMatch && typeMatch[2]){
10039                            s.type = typeMatch[2];
10040                        }
10041                        hd.appendChild(s);
10042                     }else if(match[2] && match[2].length > 0){
10043                         if(window.execScript) {
10044                            window.execScript(match[2]);
10045                         } else {
10046                             /**
10047                              * eval:var:id
10048                              * eval:var:dom
10049                              * eval:var:html
10050                              * 
10051                              */
10052                            window.eval(match[2]);
10053                         }
10054                     }
10055                 }
10056                 var el = document.getElementById(id);
10057                 if(el){el.parentNode.removeChild(el);}
10058                 if(typeof callback == "function"){
10059                     callback();
10060                 }
10061             });
10062             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10063             return this;
10064         },
10065
10066         /**
10067          * Direct access to the UpdateManager update() method (takes the same parameters).
10068          * @param {String/Function} url The url for this request or a function to call to get the url
10069          * @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}
10070          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10071          * @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.
10072          * @return {Roo.Element} this
10073          */
10074         load : function(){
10075             var um = this.getUpdateManager();
10076             um.update.apply(um, arguments);
10077             return this;
10078         },
10079
10080         /**
10081         * Gets this element's UpdateManager
10082         * @return {Roo.UpdateManager} The UpdateManager
10083         */
10084         getUpdateManager : function(){
10085             if(!this.updateManager){
10086                 this.updateManager = new Roo.UpdateManager(this);
10087             }
10088             return this.updateManager;
10089         },
10090
10091         /**
10092          * Disables text selection for this element (normalized across browsers)
10093          * @return {Roo.Element} this
10094          */
10095         unselectable : function(){
10096             this.dom.unselectable = "on";
10097             this.swallowEvent("selectstart", true);
10098             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10099             this.addClass("x-unselectable");
10100             return this;
10101         },
10102
10103         /**
10104         * Calculates the x, y to center this element on the screen
10105         * @return {Array} The x, y values [x, y]
10106         */
10107         getCenterXY : function(){
10108             return this.getAlignToXY(document, 'c-c');
10109         },
10110
10111         /**
10112         * Centers the Element in either the viewport, or another Element.
10113         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10114         */
10115         center : function(centerIn){
10116             this.alignTo(centerIn || document, 'c-c');
10117             return this;
10118         },
10119
10120         /**
10121          * Tests various css rules/browsers to determine if this element uses a border box
10122          * @return {Boolean}
10123          */
10124         isBorderBox : function(){
10125             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10126         },
10127
10128         /**
10129          * Return a box {x, y, width, height} that can be used to set another elements
10130          * size/location to match this element.
10131          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10132          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10133          * @return {Object} box An object in the format {x, y, width, height}
10134          */
10135         getBox : function(contentBox, local){
10136             var xy;
10137             if(!local){
10138                 xy = this.getXY();
10139             }else{
10140                 var left = parseInt(this.getStyle("left"), 10) || 0;
10141                 var top = parseInt(this.getStyle("top"), 10) || 0;
10142                 xy = [left, top];
10143             }
10144             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10145             if(!contentBox){
10146                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10147             }else{
10148                 var l = this.getBorderWidth("l")+this.getPadding("l");
10149                 var r = this.getBorderWidth("r")+this.getPadding("r");
10150                 var t = this.getBorderWidth("t")+this.getPadding("t");
10151                 var b = this.getBorderWidth("b")+this.getPadding("b");
10152                 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)};
10153             }
10154             bx.right = bx.x + bx.width;
10155             bx.bottom = bx.y + bx.height;
10156             return bx;
10157         },
10158
10159         /**
10160          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10161          for more information about the sides.
10162          * @param {String} sides
10163          * @return {Number}
10164          */
10165         getFrameWidth : function(sides, onlyContentBox){
10166             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10167         },
10168
10169         /**
10170          * 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.
10171          * @param {Object} box The box to fill {x, y, width, height}
10172          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10174          * @return {Roo.Element} this
10175          */
10176         setBox : function(box, adjust, animate){
10177             var w = box.width, h = box.height;
10178             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10179                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10180                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10181             }
10182             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10183             return this;
10184         },
10185
10186         /**
10187          * Forces the browser to repaint this element
10188          * @return {Roo.Element} this
10189          */
10190          repaint : function(){
10191             var dom = this.dom;
10192             this.addClass("x-repaint");
10193             setTimeout(function(){
10194                 Roo.get(dom).removeClass("x-repaint");
10195             }, 1);
10196             return this;
10197         },
10198
10199         /**
10200          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10201          * then it returns the calculated width of the sides (see getPadding)
10202          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10203          * @return {Object/Number}
10204          */
10205         getMargins : function(side){
10206             if(!side){
10207                 return {
10208                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10209                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10210                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10211                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10212                 };
10213             }else{
10214                 return this.addStyles(side, El.margins);
10215              }
10216         },
10217
10218         // private
10219         addStyles : function(sides, styles){
10220             var val = 0, v, w;
10221             for(var i = 0, len = sides.length; i < len; i++){
10222                 v = this.getStyle(styles[sides.charAt(i)]);
10223                 if(v){
10224                      w = parseInt(v, 10);
10225                      if(w){ val += w; }
10226                 }
10227             }
10228             return val;
10229         },
10230
10231         /**
10232          * Creates a proxy element of this element
10233          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10234          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10235          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10236          * @return {Roo.Element} The new proxy element
10237          */
10238         createProxy : function(config, renderTo, matchBox){
10239             if(renderTo){
10240                 renderTo = Roo.getDom(renderTo);
10241             }else{
10242                 renderTo = document.body;
10243             }
10244             config = typeof config == "object" ?
10245                 config : {tag : "div", cls: config};
10246             var proxy = Roo.DomHelper.append(renderTo, config, true);
10247             if(matchBox){
10248                proxy.setBox(this.getBox());
10249             }
10250             return proxy;
10251         },
10252
10253         /**
10254          * Puts a mask over this element to disable user interaction. Requires core.css.
10255          * This method can only be applied to elements which accept child nodes.
10256          * @param {String} msg (optional) A message to display in the mask
10257          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10258          * @return {Element} The mask  element
10259          */
10260         mask : function(msg, msgCls)
10261         {
10262             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10263                 this.setStyle("position", "relative");
10264             }
10265             if(!this._mask){
10266                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10267             }
10268             
10269             this.addClass("x-masked");
10270             this._mask.setDisplayed(true);
10271             
10272             // we wander
10273             var z = 0;
10274             var dom = this.dom;
10275             while (dom && dom.style) {
10276                 if (!isNaN(parseInt(dom.style.zIndex))) {
10277                     z = Math.max(z, parseInt(dom.style.zIndex));
10278                 }
10279                 dom = dom.parentNode;
10280             }
10281             // if we are masking the body - then it hides everything..
10282             if (this.dom == document.body) {
10283                 z = 1000000;
10284                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10285                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10286             }
10287            
10288             if(typeof msg == 'string'){
10289                 if(!this._maskMsg){
10290                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10291                         cls: "roo-el-mask-msg", 
10292                         cn: [
10293                             {
10294                                 tag: 'i',
10295                                 cls: 'fa fa-spinner fa-spin'
10296                             },
10297                             {
10298                                 tag: 'div'
10299                             }   
10300                         ]
10301                     }, true);
10302                 }
10303                 var mm = this._maskMsg;
10304                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10305                 if (mm.dom.lastChild) { // weird IE issue?
10306                     mm.dom.lastChild.innerHTML = msg;
10307                 }
10308                 mm.setDisplayed(true);
10309                 mm.center(this);
10310                 mm.setStyle('z-index', z + 102);
10311             }
10312             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10313                 this._mask.setHeight(this.getHeight());
10314             }
10315             this._mask.setStyle('z-index', z + 100);
10316             
10317             return this._mask;
10318         },
10319
10320         /**
10321          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10322          * it is cached for reuse.
10323          */
10324         unmask : function(removeEl){
10325             if(this._mask){
10326                 if(removeEl === true){
10327                     this._mask.remove();
10328                     delete this._mask;
10329                     if(this._maskMsg){
10330                         this._maskMsg.remove();
10331                         delete this._maskMsg;
10332                     }
10333                 }else{
10334                     this._mask.setDisplayed(false);
10335                     if(this._maskMsg){
10336                         this._maskMsg.setDisplayed(false);
10337                     }
10338                 }
10339             }
10340             this.removeClass("x-masked");
10341         },
10342
10343         /**
10344          * Returns true if this element is masked
10345          * @return {Boolean}
10346          */
10347         isMasked : function(){
10348             return this._mask && this._mask.isVisible();
10349         },
10350
10351         /**
10352          * Creates an iframe shim for this element to keep selects and other windowed objects from
10353          * showing through.
10354          * @return {Roo.Element} The new shim element
10355          */
10356         createShim : function(){
10357             var el = document.createElement('iframe');
10358             el.frameBorder = 'no';
10359             el.className = 'roo-shim';
10360             if(Roo.isIE && Roo.isSecure){
10361                 el.src = Roo.SSL_SECURE_URL;
10362             }
10363             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10364             shim.autoBoxAdjust = false;
10365             return shim;
10366         },
10367
10368         /**
10369          * Removes this element from the DOM and deletes it from the cache
10370          */
10371         remove : function(){
10372             if(this.dom.parentNode){
10373                 this.dom.parentNode.removeChild(this.dom);
10374             }
10375             delete El.cache[this.dom.id];
10376         },
10377
10378         /**
10379          * Sets up event handlers to add and remove a css class when the mouse is over this element
10380          * @param {String} className
10381          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10382          * mouseout events for children elements
10383          * @return {Roo.Element} this
10384          */
10385         addClassOnOver : function(className, preventFlicker){
10386             this.on("mouseover", function(){
10387                 Roo.fly(this, '_internal').addClass(className);
10388             }, this.dom);
10389             var removeFn = function(e){
10390                 if(preventFlicker !== true || !e.within(this, true)){
10391                     Roo.fly(this, '_internal').removeClass(className);
10392                 }
10393             };
10394             this.on("mouseout", removeFn, this.dom);
10395             return this;
10396         },
10397
10398         /**
10399          * Sets up event handlers to add and remove a css class when this element has the focus
10400          * @param {String} className
10401          * @return {Roo.Element} this
10402          */
10403         addClassOnFocus : function(className){
10404             this.on("focus", function(){
10405                 Roo.fly(this, '_internal').addClass(className);
10406             }, this.dom);
10407             this.on("blur", function(){
10408                 Roo.fly(this, '_internal').removeClass(className);
10409             }, this.dom);
10410             return this;
10411         },
10412         /**
10413          * 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)
10414          * @param {String} className
10415          * @return {Roo.Element} this
10416          */
10417         addClassOnClick : function(className){
10418             var dom = this.dom;
10419             this.on("mousedown", function(){
10420                 Roo.fly(dom, '_internal').addClass(className);
10421                 var d = Roo.get(document);
10422                 var fn = function(){
10423                     Roo.fly(dom, '_internal').removeClass(className);
10424                     d.removeListener("mouseup", fn);
10425                 };
10426                 d.on("mouseup", fn);
10427             });
10428             return this;
10429         },
10430
10431         /**
10432          * Stops the specified event from bubbling and optionally prevents the default action
10433          * @param {String} eventName
10434          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10435          * @return {Roo.Element} this
10436          */
10437         swallowEvent : function(eventName, preventDefault){
10438             var fn = function(e){
10439                 e.stopPropagation();
10440                 if(preventDefault){
10441                     e.preventDefault();
10442                 }
10443             };
10444             if(eventName instanceof Array){
10445                 for(var i = 0, len = eventName.length; i < len; i++){
10446                      this.on(eventName[i], fn);
10447                 }
10448                 return this;
10449             }
10450             this.on(eventName, fn);
10451             return this;
10452         },
10453
10454         /**
10455          * @private
10456          */
10457         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10458
10459         /**
10460          * Sizes this element to its parent element's dimensions performing
10461          * neccessary box adjustments.
10462          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10463          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10464          * @return {Roo.Element} this
10465          */
10466         fitToParent : function(monitorResize, targetParent) {
10467           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10468           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10469           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10470             return this;
10471           }
10472           var p = Roo.get(targetParent || this.dom.parentNode);
10473           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10474           if (monitorResize === true) {
10475             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10476             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10477           }
10478           return this;
10479         },
10480
10481         /**
10482          * Gets the next sibling, skipping text nodes
10483          * @return {HTMLElement} The next sibling or null
10484          */
10485         getNextSibling : function(){
10486             var n = this.dom.nextSibling;
10487             while(n && n.nodeType != 1){
10488                 n = n.nextSibling;
10489             }
10490             return n;
10491         },
10492
10493         /**
10494          * Gets the previous sibling, skipping text nodes
10495          * @return {HTMLElement} The previous sibling or null
10496          */
10497         getPrevSibling : function(){
10498             var n = this.dom.previousSibling;
10499             while(n && n.nodeType != 1){
10500                 n = n.previousSibling;
10501             }
10502             return n;
10503         },
10504
10505
10506         /**
10507          * Appends the passed element(s) to this element
10508          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10509          * @return {Roo.Element} this
10510          */
10511         appendChild: function(el){
10512             el = Roo.get(el);
10513             el.appendTo(this);
10514             return this;
10515         },
10516
10517         /**
10518          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10519          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10520          * automatically generated with the specified attributes.
10521          * @param {HTMLElement} insertBefore (optional) a child element of this element
10522          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10523          * @return {Roo.Element} The new child element
10524          */
10525         createChild: function(config, insertBefore, returnDom){
10526             config = config || {tag:'div'};
10527             if(insertBefore){
10528                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10529             }
10530             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10531         },
10532
10533         /**
10534          * Appends this element to the passed element
10535          * @param {String/HTMLElement/Element} el The new parent element
10536          * @return {Roo.Element} this
10537          */
10538         appendTo: function(el){
10539             el = Roo.getDom(el);
10540             el.appendChild(this.dom);
10541             return this;
10542         },
10543
10544         /**
10545          * Inserts this element before the passed element in the DOM
10546          * @param {String/HTMLElement/Element} el The element to insert before
10547          * @return {Roo.Element} this
10548          */
10549         insertBefore: function(el){
10550             el = Roo.getDom(el);
10551             el.parentNode.insertBefore(this.dom, el);
10552             return this;
10553         },
10554
10555         /**
10556          * Inserts this element after the passed element in the DOM
10557          * @param {String/HTMLElement/Element} el The element to insert after
10558          * @return {Roo.Element} this
10559          */
10560         insertAfter: function(el){
10561             el = Roo.getDom(el);
10562             el.parentNode.insertBefore(this.dom, el.nextSibling);
10563             return this;
10564         },
10565
10566         /**
10567          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10568          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10569          * @return {Roo.Element} The new child
10570          */
10571         insertFirst: function(el, returnDom){
10572             el = el || {};
10573             if(typeof el == 'object' && !el.nodeType){ // dh config
10574                 return this.createChild(el, this.dom.firstChild, returnDom);
10575             }else{
10576                 el = Roo.getDom(el);
10577                 this.dom.insertBefore(el, this.dom.firstChild);
10578                 return !returnDom ? Roo.get(el) : el;
10579             }
10580         },
10581
10582         /**
10583          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10584          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10585          * @param {String} where (optional) 'before' or 'after' defaults to before
10586          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10587          * @return {Roo.Element} the inserted Element
10588          */
10589         insertSibling: function(el, where, returnDom){
10590             where = where ? where.toLowerCase() : 'before';
10591             el = el || {};
10592             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10593
10594             if(typeof el == 'object' && !el.nodeType){ // dh config
10595                 if(where == 'after' && !this.dom.nextSibling){
10596                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10597                 }else{
10598                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10599                 }
10600
10601             }else{
10602                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10603                             where == 'before' ? this.dom : this.dom.nextSibling);
10604                 if(!returnDom){
10605                     rt = Roo.get(rt);
10606                 }
10607             }
10608             return rt;
10609         },
10610
10611         /**
10612          * Creates and wraps this element with another element
10613          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10614          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10615          * @return {HTMLElement/Element} The newly created wrapper element
10616          */
10617         wrap: function(config, returnDom){
10618             if(!config){
10619                 config = {tag: "div"};
10620             }
10621             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10622             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10623             return newEl;
10624         },
10625
10626         /**
10627          * Replaces the passed element with this element
10628          * @param {String/HTMLElement/Element} el The element to replace
10629          * @return {Roo.Element} this
10630          */
10631         replace: function(el){
10632             el = Roo.get(el);
10633             this.insertBefore(el);
10634             el.remove();
10635             return this;
10636         },
10637
10638         /**
10639          * Inserts an html fragment into this element
10640          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10641          * @param {String} html The HTML fragment
10642          * @param {Boolean} returnEl True to return an Roo.Element
10643          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10644          */
10645         insertHtml : function(where, html, returnEl){
10646             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10647             return returnEl ? Roo.get(el) : el;
10648         },
10649
10650         /**
10651          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10652          * @param {Object} o The object with the attributes
10653          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10654          * @return {Roo.Element} this
10655          */
10656         set : function(o, useSet){
10657             var el = this.dom;
10658             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10659             for(var attr in o){
10660                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10661                 if(attr=="cls"){
10662                     el.className = o["cls"];
10663                 }else{
10664                     if(useSet) {
10665                         el.setAttribute(attr, o[attr]);
10666                     } else {
10667                         el[attr] = o[attr];
10668                     }
10669                 }
10670             }
10671             if(o.style){
10672                 Roo.DomHelper.applyStyles(el, o.style);
10673             }
10674             return this;
10675         },
10676
10677         /**
10678          * Convenience method for constructing a KeyMap
10679          * @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:
10680          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10681          * @param {Function} fn The function to call
10682          * @param {Object} scope (optional) The scope of the function
10683          * @return {Roo.KeyMap} The KeyMap created
10684          */
10685         addKeyListener : function(key, fn, scope){
10686             var config;
10687             if(typeof key != "object" || key instanceof Array){
10688                 config = {
10689                     key: key,
10690                     fn: fn,
10691                     scope: scope
10692                 };
10693             }else{
10694                 config = {
10695                     key : key.key,
10696                     shift : key.shift,
10697                     ctrl : key.ctrl,
10698                     alt : key.alt,
10699                     fn: fn,
10700                     scope: scope
10701                 };
10702             }
10703             return new Roo.KeyMap(this, config);
10704         },
10705
10706         /**
10707          * Creates a KeyMap for this element
10708          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10709          * @return {Roo.KeyMap} The KeyMap created
10710          */
10711         addKeyMap : function(config){
10712             return new Roo.KeyMap(this, config);
10713         },
10714
10715         /**
10716          * Returns true if this element is scrollable.
10717          * @return {Boolean}
10718          */
10719          isScrollable : function(){
10720             var dom = this.dom;
10721             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10722         },
10723
10724         /**
10725          * 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().
10726          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10727          * @param {Number} value The new scroll value
10728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10729          * @return {Element} this
10730          */
10731
10732         scrollTo : function(side, value, animate){
10733             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10734             if(!animate || !A){
10735                 this.dom[prop] = value;
10736             }else{
10737                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10738                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10739             }
10740             return this;
10741         },
10742
10743         /**
10744          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10745          * within this element's scrollable range.
10746          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10747          * @param {Number} distance How far to scroll the element in pixels
10748          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10749          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10750          * was scrolled as far as it could go.
10751          */
10752          scroll : function(direction, distance, animate){
10753              if(!this.isScrollable()){
10754                  return;
10755              }
10756              var el = this.dom;
10757              var l = el.scrollLeft, t = el.scrollTop;
10758              var w = el.scrollWidth, h = el.scrollHeight;
10759              var cw = el.clientWidth, ch = el.clientHeight;
10760              direction = direction.toLowerCase();
10761              var scrolled = false;
10762              var a = this.preanim(arguments, 2);
10763              switch(direction){
10764                  case "l":
10765                  case "left":
10766                      if(w - l > cw){
10767                          var v = Math.min(l + distance, w-cw);
10768                          this.scrollTo("left", v, a);
10769                          scrolled = true;
10770                      }
10771                      break;
10772                 case "r":
10773                 case "right":
10774                      if(l > 0){
10775                          var v = Math.max(l - distance, 0);
10776                          this.scrollTo("left", v, a);
10777                          scrolled = true;
10778                      }
10779                      break;
10780                 case "t":
10781                 case "top":
10782                 case "up":
10783                      if(t > 0){
10784                          var v = Math.max(t - distance, 0);
10785                          this.scrollTo("top", v, a);
10786                          scrolled = true;
10787                      }
10788                      break;
10789                 case "b":
10790                 case "bottom":
10791                 case "down":
10792                      if(h - t > ch){
10793                          var v = Math.min(t + distance, h-ch);
10794                          this.scrollTo("top", v, a);
10795                          scrolled = true;
10796                      }
10797                      break;
10798              }
10799              return scrolled;
10800         },
10801
10802         /**
10803          * Translates the passed page coordinates into left/top css values for this element
10804          * @param {Number/Array} x The page x or an array containing [x, y]
10805          * @param {Number} y The page y
10806          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10807          */
10808         translatePoints : function(x, y){
10809             if(typeof x == 'object' || x instanceof Array){
10810                 y = x[1]; x = x[0];
10811             }
10812             var p = this.getStyle('position');
10813             var o = this.getXY();
10814
10815             var l = parseInt(this.getStyle('left'), 10);
10816             var t = parseInt(this.getStyle('top'), 10);
10817
10818             if(isNaN(l)){
10819                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10820             }
10821             if(isNaN(t)){
10822                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10823             }
10824
10825             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10826         },
10827
10828         /**
10829          * Returns the current scroll position of the element.
10830          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10831          */
10832         getScroll : function(){
10833             var d = this.dom, doc = document;
10834             if(d == doc || d == doc.body){
10835                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10836                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10837                 return {left: l, top: t};
10838             }else{
10839                 return {left: d.scrollLeft, top: d.scrollTop};
10840             }
10841         },
10842
10843         /**
10844          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10845          * are convert to standard 6 digit hex color.
10846          * @param {String} attr The css attribute
10847          * @param {String} defaultValue The default value to use when a valid color isn't found
10848          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10849          * YUI color anims.
10850          */
10851         getColor : function(attr, defaultValue, prefix){
10852             var v = this.getStyle(attr);
10853             if(!v || v == "transparent" || v == "inherit") {
10854                 return defaultValue;
10855             }
10856             var color = typeof prefix == "undefined" ? "#" : prefix;
10857             if(v.substr(0, 4) == "rgb("){
10858                 var rvs = v.slice(4, v.length -1).split(",");
10859                 for(var i = 0; i < 3; i++){
10860                     var h = parseInt(rvs[i]).toString(16);
10861                     if(h < 16){
10862                         h = "0" + h;
10863                     }
10864                     color += h;
10865                 }
10866             } else {
10867                 if(v.substr(0, 1) == "#"){
10868                     if(v.length == 4) {
10869                         for(var i = 1; i < 4; i++){
10870                             var c = v.charAt(i);
10871                             color +=  c + c;
10872                         }
10873                     }else if(v.length == 7){
10874                         color += v.substr(1);
10875                     }
10876                 }
10877             }
10878             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10879         },
10880
10881         /**
10882          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10883          * gradient background, rounded corners and a 4-way shadow.
10884          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10885          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10886          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10887          * @return {Roo.Element} this
10888          */
10889         boxWrap : function(cls){
10890             cls = cls || 'x-box';
10891             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10892             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10893             return el;
10894         },
10895
10896         /**
10897          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10898          * @param {String} namespace The namespace in which to look for the attribute
10899          * @param {String} name The attribute name
10900          * @return {String} The attribute value
10901          */
10902         getAttributeNS : Roo.isIE ? function(ns, name){
10903             var d = this.dom;
10904             var type = typeof d[ns+":"+name];
10905             if(type != 'undefined' && type != 'unknown'){
10906                 return d[ns+":"+name];
10907             }
10908             return d[name];
10909         } : function(ns, name){
10910             var d = this.dom;
10911             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10912         },
10913         
10914         
10915         /**
10916          * Sets or Returns the value the dom attribute value
10917          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10918          * @param {String} value (optional) The value to set the attribute to
10919          * @return {String} The attribute value
10920          */
10921         attr : function(name){
10922             if (arguments.length > 1) {
10923                 this.dom.setAttribute(name, arguments[1]);
10924                 return arguments[1];
10925             }
10926             if (typeof(name) == 'object') {
10927                 for(var i in name) {
10928                     this.attr(i, name[i]);
10929                 }
10930                 return name;
10931             }
10932             
10933             
10934             if (!this.dom.hasAttribute(name)) {
10935                 return undefined;
10936             }
10937             return this.dom.getAttribute(name);
10938         }
10939         
10940         
10941         
10942     };
10943
10944     var ep = El.prototype;
10945
10946     /**
10947      * Appends an event handler (Shorthand for addListener)
10948      * @param {String}   eventName     The type of event to append
10949      * @param {Function} fn        The method the event invokes
10950      * @param {Object} scope       (optional) The scope (this object) of the fn
10951      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10952      * @method
10953      */
10954     ep.on = ep.addListener;
10955         // backwards compat
10956     ep.mon = ep.addListener;
10957
10958     /**
10959      * Removes an event handler from this element (shorthand for removeListener)
10960      * @param {String} eventName the type of event to remove
10961      * @param {Function} fn the method the event invokes
10962      * @return {Roo.Element} this
10963      * @method
10964      */
10965     ep.un = ep.removeListener;
10966
10967     /**
10968      * true to automatically adjust width and height settings for box-model issues (default to true)
10969      */
10970     ep.autoBoxAdjust = true;
10971
10972     // private
10973     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10974
10975     // private
10976     El.addUnits = function(v, defaultUnit){
10977         if(v === "" || v == "auto"){
10978             return v;
10979         }
10980         if(v === undefined){
10981             return '';
10982         }
10983         if(typeof v == "number" || !El.unitPattern.test(v)){
10984             return v + (defaultUnit || 'px');
10985         }
10986         return v;
10987     };
10988
10989     // special markup used throughout Roo when box wrapping elements
10990     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>';
10991     /**
10992      * Visibility mode constant - Use visibility to hide element
10993      * @static
10994      * @type Number
10995      */
10996     El.VISIBILITY = 1;
10997     /**
10998      * Visibility mode constant - Use display to hide element
10999      * @static
11000      * @type Number
11001      */
11002     El.DISPLAY = 2;
11003
11004     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11005     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11006     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11007
11008
11009
11010     /**
11011      * @private
11012      */
11013     El.cache = {};
11014
11015     var docEl;
11016
11017     /**
11018      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11019      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11020      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11021      * @return {Element} The Element object
11022      * @static
11023      */
11024     El.get = function(el){
11025         var ex, elm, id;
11026         if(!el){ return null; }
11027         if(typeof el == "string"){ // element id
11028             if(!(elm = document.getElementById(el))){
11029                 return null;
11030             }
11031             if(ex = El.cache[el]){
11032                 ex.dom = elm;
11033             }else{
11034                 ex = El.cache[el] = new El(elm);
11035             }
11036             return ex;
11037         }else if(el.tagName){ // dom element
11038             if(!(id = el.id)){
11039                 id = Roo.id(el);
11040             }
11041             if(ex = El.cache[id]){
11042                 ex.dom = el;
11043             }else{
11044                 ex = El.cache[id] = new El(el);
11045             }
11046             return ex;
11047         }else if(el instanceof El){
11048             if(el != docEl){
11049                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11050                                                               // catch case where it hasn't been appended
11051                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11052             }
11053             return el;
11054         }else if(el.isComposite){
11055             return el;
11056         }else if(el instanceof Array){
11057             return El.select(el);
11058         }else if(el == document){
11059             // create a bogus element object representing the document object
11060             if(!docEl){
11061                 var f = function(){};
11062                 f.prototype = El.prototype;
11063                 docEl = new f();
11064                 docEl.dom = document;
11065             }
11066             return docEl;
11067         }
11068         return null;
11069     };
11070
11071     // private
11072     El.uncache = function(el){
11073         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11074             if(a[i]){
11075                 delete El.cache[a[i].id || a[i]];
11076             }
11077         }
11078     };
11079
11080     // private
11081     // Garbage collection - uncache elements/purge listeners on orphaned elements
11082     // so we don't hold a reference and cause the browser to retain them
11083     El.garbageCollect = function(){
11084         if(!Roo.enableGarbageCollector){
11085             clearInterval(El.collectorThread);
11086             return;
11087         }
11088         for(var eid in El.cache){
11089             var el = El.cache[eid], d = el.dom;
11090             // -------------------------------------------------------
11091             // Determining what is garbage:
11092             // -------------------------------------------------------
11093             // !d
11094             // dom node is null, definitely garbage
11095             // -------------------------------------------------------
11096             // !d.parentNode
11097             // no parentNode == direct orphan, definitely garbage
11098             // -------------------------------------------------------
11099             // !d.offsetParent && !document.getElementById(eid)
11100             // display none elements have no offsetParent so we will
11101             // also try to look it up by it's id. However, check
11102             // offsetParent first so we don't do unneeded lookups.
11103             // This enables collection of elements that are not orphans
11104             // directly, but somewhere up the line they have an orphan
11105             // parent.
11106             // -------------------------------------------------------
11107             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11108                 delete El.cache[eid];
11109                 if(d && Roo.enableListenerCollection){
11110                     E.purgeElement(d);
11111                 }
11112             }
11113         }
11114     }
11115     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11116
11117
11118     // dom is optional
11119     El.Flyweight = function(dom){
11120         this.dom = dom;
11121     };
11122     El.Flyweight.prototype = El.prototype;
11123
11124     El._flyweights = {};
11125     /**
11126      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11127      * the dom node can be overwritten by other code.
11128      * @param {String/HTMLElement} el The dom node or id
11129      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11130      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11131      * @static
11132      * @return {Element} The shared Element object
11133      */
11134     El.fly = function(el, named){
11135         named = named || '_global';
11136         el = Roo.getDom(el);
11137         if(!el){
11138             return null;
11139         }
11140         if(!El._flyweights[named]){
11141             El._flyweights[named] = new El.Flyweight();
11142         }
11143         El._flyweights[named].dom = el;
11144         return El._flyweights[named];
11145     };
11146
11147     /**
11148      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11149      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11150      * Shorthand of {@link Roo.Element#get}
11151      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11152      * @return {Element} The Element object
11153      * @member Roo
11154      * @method get
11155      */
11156     Roo.get = El.get;
11157     /**
11158      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11159      * the dom node can be overwritten by other code.
11160      * Shorthand of {@link Roo.Element#fly}
11161      * @param {String/HTMLElement} el The dom node or id
11162      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11163      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11164      * @static
11165      * @return {Element} The shared Element object
11166      * @member Roo
11167      * @method fly
11168      */
11169     Roo.fly = El.fly;
11170
11171     // speedy lookup for elements never to box adjust
11172     var noBoxAdjust = Roo.isStrict ? {
11173         select:1
11174     } : {
11175         input:1, select:1, textarea:1
11176     };
11177     if(Roo.isIE || Roo.isGecko){
11178         noBoxAdjust['button'] = 1;
11179     }
11180
11181
11182     Roo.EventManager.on(window, 'unload', function(){
11183         delete El.cache;
11184         delete El._flyweights;
11185     });
11186 })();
11187
11188
11189
11190
11191 if(Roo.DomQuery){
11192     Roo.Element.selectorFunction = Roo.DomQuery.select;
11193 }
11194
11195 Roo.Element.select = function(selector, unique, root){
11196     var els;
11197     if(typeof selector == "string"){
11198         els = Roo.Element.selectorFunction(selector, root);
11199     }else if(selector.length !== undefined){
11200         els = selector;
11201     }else{
11202         throw "Invalid selector";
11203     }
11204     if(unique === true){
11205         return new Roo.CompositeElement(els);
11206     }else{
11207         return new Roo.CompositeElementLite(els);
11208     }
11209 };
11210 /**
11211  * Selects elements based on the passed CSS selector to enable working on them as 1.
11212  * @param {String/Array} selector The CSS selector or an array of elements
11213  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11214  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11215  * @return {CompositeElementLite/CompositeElement}
11216  * @member Roo
11217  * @method select
11218  */
11219 Roo.select = Roo.Element.select;
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234 /*
11235  * Based on:
11236  * Ext JS Library 1.1.1
11237  * Copyright(c) 2006-2007, Ext JS, LLC.
11238  *
11239  * Originally Released Under LGPL - original licence link has changed is not relivant.
11240  *
11241  * Fork - LGPL
11242  * <script type="text/javascript">
11243  */
11244
11245
11246
11247 //Notifies Element that fx methods are available
11248 Roo.enableFx = true;
11249
11250 /**
11251  * @class Roo.Fx
11252  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11253  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11254  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11255  * Element effects to work.</p><br/>
11256  *
11257  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11258  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11259  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11260  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11261  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11262  * expected results and should be done with care.</p><br/>
11263  *
11264  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11265  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11266 <pre>
11267 Value  Description
11268 -----  -----------------------------
11269 tl     The top left corner
11270 t      The center of the top edge
11271 tr     The top right corner
11272 l      The center of the left edge
11273 r      The center of the right edge
11274 bl     The bottom left corner
11275 b      The center of the bottom edge
11276 br     The bottom right corner
11277 </pre>
11278  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11279  * below are common options that can be passed to any Fx method.</b>
11280  * @cfg {Function} callback A function called when the effect is finished
11281  * @cfg {Object} scope The scope of the effect function
11282  * @cfg {String} easing A valid Easing value for the effect
11283  * @cfg {String} afterCls A css class to apply after the effect
11284  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11285  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11286  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11287  * effects that end with the element being visually hidden, ignored otherwise)
11288  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11289  * a function which returns such a specification that will be applied to the Element after the effect finishes
11290  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11291  * @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
11292  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11293  */
11294 Roo.Fx = {
11295         /**
11296          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11297          * origin for the slide effect.  This function automatically handles wrapping the element with
11298          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11299          * Usage:
11300          *<pre><code>
11301 // default: slide the element in from the top
11302 el.slideIn();
11303
11304 // custom: slide the element in from the right with a 2-second duration
11305 el.slideIn('r', { duration: 2 });
11306
11307 // common config options shown with default values
11308 el.slideIn('t', {
11309     easing: 'easeOut',
11310     duration: .5
11311 });
11312 </code></pre>
11313          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11314          * @param {Object} options (optional) Object literal with any of the Fx config options
11315          * @return {Roo.Element} The Element
11316          */
11317     slideIn : function(anchor, o){
11318         var el = this.getFxEl();
11319         o = o || {};
11320
11321         el.queueFx(o, function(){
11322
11323             anchor = anchor || "t";
11324
11325             // fix display to visibility
11326             this.fixDisplay();
11327
11328             // restore values after effect
11329             var r = this.getFxRestore();
11330             var b = this.getBox();
11331             // fixed size for slide
11332             this.setSize(b);
11333
11334             // wrap if needed
11335             var wrap = this.fxWrap(r.pos, o, "hidden");
11336
11337             var st = this.dom.style;
11338             st.visibility = "visible";
11339             st.position = "absolute";
11340
11341             // clear out temp styles after slide and unwrap
11342             var after = function(){
11343                 el.fxUnwrap(wrap, r.pos, o);
11344                 st.width = r.width;
11345                 st.height = r.height;
11346                 el.afterFx(o);
11347             };
11348             // time to calc the positions
11349             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11350
11351             switch(anchor.toLowerCase()){
11352                 case "t":
11353                     wrap.setSize(b.width, 0);
11354                     st.left = st.bottom = "0";
11355                     a = {height: bh};
11356                 break;
11357                 case "l":
11358                     wrap.setSize(0, b.height);
11359                     st.right = st.top = "0";
11360                     a = {width: bw};
11361                 break;
11362                 case "r":
11363                     wrap.setSize(0, b.height);
11364                     wrap.setX(b.right);
11365                     st.left = st.top = "0";
11366                     a = {width: bw, points: pt};
11367                 break;
11368                 case "b":
11369                     wrap.setSize(b.width, 0);
11370                     wrap.setY(b.bottom);
11371                     st.left = st.top = "0";
11372                     a = {height: bh, points: pt};
11373                 break;
11374                 case "tl":
11375                     wrap.setSize(0, 0);
11376                     st.right = st.bottom = "0";
11377                     a = {width: bw, height: bh};
11378                 break;
11379                 case "bl":
11380                     wrap.setSize(0, 0);
11381                     wrap.setY(b.y+b.height);
11382                     st.right = st.top = "0";
11383                     a = {width: bw, height: bh, points: pt};
11384                 break;
11385                 case "br":
11386                     wrap.setSize(0, 0);
11387                     wrap.setXY([b.right, b.bottom]);
11388                     st.left = st.top = "0";
11389                     a = {width: bw, height: bh, points: pt};
11390                 break;
11391                 case "tr":
11392                     wrap.setSize(0, 0);
11393                     wrap.setX(b.x+b.width);
11394                     st.left = st.bottom = "0";
11395                     a = {width: bw, height: bh, points: pt};
11396                 break;
11397             }
11398             this.dom.style.visibility = "visible";
11399             wrap.show();
11400
11401             arguments.callee.anim = wrap.fxanim(a,
11402                 o,
11403                 'motion',
11404                 .5,
11405                 'easeOut', after);
11406         });
11407         return this;
11408     },
11409     
11410         /**
11411          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11412          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11413          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11414          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11415          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11416          * Usage:
11417          *<pre><code>
11418 // default: slide the element out to the top
11419 el.slideOut();
11420
11421 // custom: slide the element out to the right with a 2-second duration
11422 el.slideOut('r', { duration: 2 });
11423
11424 // common config options shown with default values
11425 el.slideOut('t', {
11426     easing: 'easeOut',
11427     duration: .5,
11428     remove: false,
11429     useDisplay: false
11430 });
11431 </code></pre>
11432          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11433          * @param {Object} options (optional) Object literal with any of the Fx config options
11434          * @return {Roo.Element} The Element
11435          */
11436     slideOut : function(anchor, o){
11437         var el = this.getFxEl();
11438         o = o || {};
11439
11440         el.queueFx(o, function(){
11441
11442             anchor = anchor || "t";
11443
11444             // restore values after effect
11445             var r = this.getFxRestore();
11446             
11447             var b = this.getBox();
11448             // fixed size for slide
11449             this.setSize(b);
11450
11451             // wrap if needed
11452             var wrap = this.fxWrap(r.pos, o, "visible");
11453
11454             var st = this.dom.style;
11455             st.visibility = "visible";
11456             st.position = "absolute";
11457
11458             wrap.setSize(b);
11459
11460             var after = function(){
11461                 if(o.useDisplay){
11462                     el.setDisplayed(false);
11463                 }else{
11464                     el.hide();
11465                 }
11466
11467                 el.fxUnwrap(wrap, r.pos, o);
11468
11469                 st.width = r.width;
11470                 st.height = r.height;
11471
11472                 el.afterFx(o);
11473             };
11474
11475             var a, zero = {to: 0};
11476             switch(anchor.toLowerCase()){
11477                 case "t":
11478                     st.left = st.bottom = "0";
11479                     a = {height: zero};
11480                 break;
11481                 case "l":
11482                     st.right = st.top = "0";
11483                     a = {width: zero};
11484                 break;
11485                 case "r":
11486                     st.left = st.top = "0";
11487                     a = {width: zero, points: {to:[b.right, b.y]}};
11488                 break;
11489                 case "b":
11490                     st.left = st.top = "0";
11491                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11492                 break;
11493                 case "tl":
11494                     st.right = st.bottom = "0";
11495                     a = {width: zero, height: zero};
11496                 break;
11497                 case "bl":
11498                     st.right = st.top = "0";
11499                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11500                 break;
11501                 case "br":
11502                     st.left = st.top = "0";
11503                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11504                 break;
11505                 case "tr":
11506                     st.left = st.bottom = "0";
11507                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11508                 break;
11509             }
11510
11511             arguments.callee.anim = wrap.fxanim(a,
11512                 o,
11513                 'motion',
11514                 .5,
11515                 "easeOut", after);
11516         });
11517         return this;
11518     },
11519
11520         /**
11521          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11522          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11523          * The element must be removed from the DOM using the 'remove' config option if desired.
11524          * Usage:
11525          *<pre><code>
11526 // default
11527 el.puff();
11528
11529 // common config options shown with default values
11530 el.puff({
11531     easing: 'easeOut',
11532     duration: .5,
11533     remove: false,
11534     useDisplay: false
11535 });
11536 </code></pre>
11537          * @param {Object} options (optional) Object literal with any of the Fx config options
11538          * @return {Roo.Element} The Element
11539          */
11540     puff : function(o){
11541         var el = this.getFxEl();
11542         o = o || {};
11543
11544         el.queueFx(o, function(){
11545             this.clearOpacity();
11546             this.show();
11547
11548             // restore values after effect
11549             var r = this.getFxRestore();
11550             var st = this.dom.style;
11551
11552             var after = function(){
11553                 if(o.useDisplay){
11554                     el.setDisplayed(false);
11555                 }else{
11556                     el.hide();
11557                 }
11558
11559                 el.clearOpacity();
11560
11561                 el.setPositioning(r.pos);
11562                 st.width = r.width;
11563                 st.height = r.height;
11564                 st.fontSize = '';
11565                 el.afterFx(o);
11566             };
11567
11568             var width = this.getWidth();
11569             var height = this.getHeight();
11570
11571             arguments.callee.anim = this.fxanim({
11572                     width : {to: this.adjustWidth(width * 2)},
11573                     height : {to: this.adjustHeight(height * 2)},
11574                     points : {by: [-(width * .5), -(height * .5)]},
11575                     opacity : {to: 0},
11576                     fontSize: {to:200, unit: "%"}
11577                 },
11578                 o,
11579                 'motion',
11580                 .5,
11581                 "easeOut", after);
11582         });
11583         return this;
11584     },
11585
11586         /**
11587          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11588          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11589          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11590          * Usage:
11591          *<pre><code>
11592 // default
11593 el.switchOff();
11594
11595 // all config options shown with default values
11596 el.switchOff({
11597     easing: 'easeIn',
11598     duration: .3,
11599     remove: false,
11600     useDisplay: false
11601 });
11602 </code></pre>
11603          * @param {Object} options (optional) Object literal with any of the Fx config options
11604          * @return {Roo.Element} The Element
11605          */
11606     switchOff : function(o){
11607         var el = this.getFxEl();
11608         o = o || {};
11609
11610         el.queueFx(o, function(){
11611             this.clearOpacity();
11612             this.clip();
11613
11614             // restore values after effect
11615             var r = this.getFxRestore();
11616             var st = this.dom.style;
11617
11618             var after = function(){
11619                 if(o.useDisplay){
11620                     el.setDisplayed(false);
11621                 }else{
11622                     el.hide();
11623                 }
11624
11625                 el.clearOpacity();
11626                 el.setPositioning(r.pos);
11627                 st.width = r.width;
11628                 st.height = r.height;
11629
11630                 el.afterFx(o);
11631             };
11632
11633             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11634                 this.clearOpacity();
11635                 (function(){
11636                     this.fxanim({
11637                         height:{to:1},
11638                         points:{by:[0, this.getHeight() * .5]}
11639                     }, o, 'motion', 0.3, 'easeIn', after);
11640                 }).defer(100, this);
11641             });
11642         });
11643         return this;
11644     },
11645
11646     /**
11647      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11648      * changed using the "attr" config option) and then fading back to the original color. If no original
11649      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11650      * Usage:
11651 <pre><code>
11652 // default: highlight background to yellow
11653 el.highlight();
11654
11655 // custom: highlight foreground text to blue for 2 seconds
11656 el.highlight("0000ff", { attr: 'color', duration: 2 });
11657
11658 // common config options shown with default values
11659 el.highlight("ffff9c", {
11660     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11661     endColor: (current color) or "ffffff",
11662     easing: 'easeIn',
11663     duration: 1
11664 });
11665 </code></pre>
11666      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11667      * @param {Object} options (optional) Object literal with any of the Fx config options
11668      * @return {Roo.Element} The Element
11669      */ 
11670     highlight : function(color, o){
11671         var el = this.getFxEl();
11672         o = o || {};
11673
11674         el.queueFx(o, function(){
11675             color = color || "ffff9c";
11676             attr = o.attr || "backgroundColor";
11677
11678             this.clearOpacity();
11679             this.show();
11680
11681             var origColor = this.getColor(attr);
11682             var restoreColor = this.dom.style[attr];
11683             endColor = (o.endColor || origColor) || "ffffff";
11684
11685             var after = function(){
11686                 el.dom.style[attr] = restoreColor;
11687                 el.afterFx(o);
11688             };
11689
11690             var a = {};
11691             a[attr] = {from: color, to: endColor};
11692             arguments.callee.anim = this.fxanim(a,
11693                 o,
11694                 'color',
11695                 1,
11696                 'easeIn', after);
11697         });
11698         return this;
11699     },
11700
11701    /**
11702     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11703     * Usage:
11704 <pre><code>
11705 // default: a single light blue ripple
11706 el.frame();
11707
11708 // custom: 3 red ripples lasting 3 seconds total
11709 el.frame("ff0000", 3, { duration: 3 });
11710
11711 // common config options shown with default values
11712 el.frame("C3DAF9", 1, {
11713     duration: 1 //duration of entire animation (not each individual ripple)
11714     // Note: Easing is not configurable and will be ignored if included
11715 });
11716 </code></pre>
11717     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11718     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11719     * @param {Object} options (optional) Object literal with any of the Fx config options
11720     * @return {Roo.Element} The Element
11721     */
11722     frame : function(color, count, o){
11723         var el = this.getFxEl();
11724         o = o || {};
11725
11726         el.queueFx(o, function(){
11727             color = color || "#C3DAF9";
11728             if(color.length == 6){
11729                 color = "#" + color;
11730             }
11731             count = count || 1;
11732             duration = o.duration || 1;
11733             this.show();
11734
11735             var b = this.getBox();
11736             var animFn = function(){
11737                 var proxy = this.createProxy({
11738
11739                      style:{
11740                         visbility:"hidden",
11741                         position:"absolute",
11742                         "z-index":"35000", // yee haw
11743                         border:"0px solid " + color
11744                      }
11745                   });
11746                 var scale = Roo.isBorderBox ? 2 : 1;
11747                 proxy.animate({
11748                     top:{from:b.y, to:b.y - 20},
11749                     left:{from:b.x, to:b.x - 20},
11750                     borderWidth:{from:0, to:10},
11751                     opacity:{from:1, to:0},
11752                     height:{from:b.height, to:(b.height + (20*scale))},
11753                     width:{from:b.width, to:(b.width + (20*scale))}
11754                 }, duration, function(){
11755                     proxy.remove();
11756                 });
11757                 if(--count > 0){
11758                      animFn.defer((duration/2)*1000, this);
11759                 }else{
11760                     el.afterFx(o);
11761                 }
11762             };
11763             animFn.call(this);
11764         });
11765         return this;
11766     },
11767
11768    /**
11769     * Creates a pause before any subsequent queued effects begin.  If there are
11770     * no effects queued after the pause it will have no effect.
11771     * Usage:
11772 <pre><code>
11773 el.pause(1);
11774 </code></pre>
11775     * @param {Number} seconds The length of time to pause (in seconds)
11776     * @return {Roo.Element} The Element
11777     */
11778     pause : function(seconds){
11779         var el = this.getFxEl();
11780         var o = {};
11781
11782         el.queueFx(o, function(){
11783             setTimeout(function(){
11784                 el.afterFx(o);
11785             }, seconds * 1000);
11786         });
11787         return this;
11788     },
11789
11790    /**
11791     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11792     * using the "endOpacity" config option.
11793     * Usage:
11794 <pre><code>
11795 // default: fade in from opacity 0 to 100%
11796 el.fadeIn();
11797
11798 // custom: fade in from opacity 0 to 75% over 2 seconds
11799 el.fadeIn({ endOpacity: .75, duration: 2});
11800
11801 // common config options shown with default values
11802 el.fadeIn({
11803     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11804     easing: 'easeOut',
11805     duration: .5
11806 });
11807 </code></pre>
11808     * @param {Object} options (optional) Object literal with any of the Fx config options
11809     * @return {Roo.Element} The Element
11810     */
11811     fadeIn : function(o){
11812         var el = this.getFxEl();
11813         o = o || {};
11814         el.queueFx(o, function(){
11815             this.setOpacity(0);
11816             this.fixDisplay();
11817             this.dom.style.visibility = 'visible';
11818             var to = o.endOpacity || 1;
11819             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11820                 o, null, .5, "easeOut", function(){
11821                 if(to == 1){
11822                     this.clearOpacity();
11823                 }
11824                 el.afterFx(o);
11825             });
11826         });
11827         return this;
11828     },
11829
11830    /**
11831     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11832     * using the "endOpacity" config option.
11833     * Usage:
11834 <pre><code>
11835 // default: fade out from the element's current opacity to 0
11836 el.fadeOut();
11837
11838 // custom: fade out from the element's current opacity to 25% over 2 seconds
11839 el.fadeOut({ endOpacity: .25, duration: 2});
11840
11841 // common config options shown with default values
11842 el.fadeOut({
11843     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11844     easing: 'easeOut',
11845     duration: .5
11846     remove: false,
11847     useDisplay: false
11848 });
11849 </code></pre>
11850     * @param {Object} options (optional) Object literal with any of the Fx config options
11851     * @return {Roo.Element} The Element
11852     */
11853     fadeOut : function(o){
11854         var el = this.getFxEl();
11855         o = o || {};
11856         el.queueFx(o, function(){
11857             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11858                 o, null, .5, "easeOut", function(){
11859                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11860                      this.dom.style.display = "none";
11861                 }else{
11862                      this.dom.style.visibility = "hidden";
11863                 }
11864                 this.clearOpacity();
11865                 el.afterFx(o);
11866             });
11867         });
11868         return this;
11869     },
11870
11871    /**
11872     * Animates the transition of an element's dimensions from a starting height/width
11873     * to an ending height/width.
11874     * Usage:
11875 <pre><code>
11876 // change height and width to 100x100 pixels
11877 el.scale(100, 100);
11878
11879 // common config options shown with default values.  The height and width will default to
11880 // the element's existing values if passed as null.
11881 el.scale(
11882     [element's width],
11883     [element's height], {
11884     easing: 'easeOut',
11885     duration: .35
11886 });
11887 </code></pre>
11888     * @param {Number} width  The new width (pass undefined to keep the original width)
11889     * @param {Number} height  The new height (pass undefined to keep the original height)
11890     * @param {Object} options (optional) Object literal with any of the Fx config options
11891     * @return {Roo.Element} The Element
11892     */
11893     scale : function(w, h, o){
11894         this.shift(Roo.apply({}, o, {
11895             width: w,
11896             height: h
11897         }));
11898         return this;
11899     },
11900
11901    /**
11902     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11903     * Any of these properties not specified in the config object will not be changed.  This effect 
11904     * requires that at least one new dimension, position or opacity setting must be passed in on
11905     * the config object in order for the function to have any effect.
11906     * Usage:
11907 <pre><code>
11908 // slide the element horizontally to x position 200 while changing the height and opacity
11909 el.shift({ x: 200, height: 50, opacity: .8 });
11910
11911 // common config options shown with default values.
11912 el.shift({
11913     width: [element's width],
11914     height: [element's height],
11915     x: [element's x position],
11916     y: [element's y position],
11917     opacity: [element's opacity],
11918     easing: 'easeOut',
11919     duration: .35
11920 });
11921 </code></pre>
11922     * @param {Object} options  Object literal with any of the Fx config options
11923     * @return {Roo.Element} The Element
11924     */
11925     shift : function(o){
11926         var el = this.getFxEl();
11927         o = o || {};
11928         el.queueFx(o, function(){
11929             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11930             if(w !== undefined){
11931                 a.width = {to: this.adjustWidth(w)};
11932             }
11933             if(h !== undefined){
11934                 a.height = {to: this.adjustHeight(h)};
11935             }
11936             if(x !== undefined || y !== undefined){
11937                 a.points = {to: [
11938                     x !== undefined ? x : this.getX(),
11939                     y !== undefined ? y : this.getY()
11940                 ]};
11941             }
11942             if(op !== undefined){
11943                 a.opacity = {to: op};
11944             }
11945             if(o.xy !== undefined){
11946                 a.points = {to: o.xy};
11947             }
11948             arguments.callee.anim = this.fxanim(a,
11949                 o, 'motion', .35, "easeOut", function(){
11950                 el.afterFx(o);
11951             });
11952         });
11953         return this;
11954     },
11955
11956         /**
11957          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11958          * ending point of the effect.
11959          * Usage:
11960          *<pre><code>
11961 // default: slide the element downward while fading out
11962 el.ghost();
11963
11964 // custom: slide the element out to the right with a 2-second duration
11965 el.ghost('r', { duration: 2 });
11966
11967 // common config options shown with default values
11968 el.ghost('b', {
11969     easing: 'easeOut',
11970     duration: .5
11971     remove: false,
11972     useDisplay: false
11973 });
11974 </code></pre>
11975          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11976          * @param {Object} options (optional) Object literal with any of the Fx config options
11977          * @return {Roo.Element} The Element
11978          */
11979     ghost : function(anchor, o){
11980         var el = this.getFxEl();
11981         o = o || {};
11982
11983         el.queueFx(o, function(){
11984             anchor = anchor || "b";
11985
11986             // restore values after effect
11987             var r = this.getFxRestore();
11988             var w = this.getWidth(),
11989                 h = this.getHeight();
11990
11991             var st = this.dom.style;
11992
11993             var after = function(){
11994                 if(o.useDisplay){
11995                     el.setDisplayed(false);
11996                 }else{
11997                     el.hide();
11998                 }
11999
12000                 el.clearOpacity();
12001                 el.setPositioning(r.pos);
12002                 st.width = r.width;
12003                 st.height = r.height;
12004
12005                 el.afterFx(o);
12006             };
12007
12008             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12009             switch(anchor.toLowerCase()){
12010                 case "t":
12011                     pt.by = [0, -h];
12012                 break;
12013                 case "l":
12014                     pt.by = [-w, 0];
12015                 break;
12016                 case "r":
12017                     pt.by = [w, 0];
12018                 break;
12019                 case "b":
12020                     pt.by = [0, h];
12021                 break;
12022                 case "tl":
12023                     pt.by = [-w, -h];
12024                 break;
12025                 case "bl":
12026                     pt.by = [-w, h];
12027                 break;
12028                 case "br":
12029                     pt.by = [w, h];
12030                 break;
12031                 case "tr":
12032                     pt.by = [w, -h];
12033                 break;
12034             }
12035
12036             arguments.callee.anim = this.fxanim(a,
12037                 o,
12038                 'motion',
12039                 .5,
12040                 "easeOut", after);
12041         });
12042         return this;
12043     },
12044
12045         /**
12046          * Ensures that all effects queued after syncFx is called on the element are
12047          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12048          * @return {Roo.Element} The Element
12049          */
12050     syncFx : function(){
12051         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12052             block : false,
12053             concurrent : true,
12054             stopFx : false
12055         });
12056         return this;
12057     },
12058
12059         /**
12060          * Ensures that all effects queued after sequenceFx is called on the element are
12061          * run in sequence.  This is the opposite of {@link #syncFx}.
12062          * @return {Roo.Element} The Element
12063          */
12064     sequenceFx : function(){
12065         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12066             block : false,
12067             concurrent : false,
12068             stopFx : false
12069         });
12070         return this;
12071     },
12072
12073         /* @private */
12074     nextFx : function(){
12075         var ef = this.fxQueue[0];
12076         if(ef){
12077             ef.call(this);
12078         }
12079     },
12080
12081         /**
12082          * Returns true if the element has any effects actively running or queued, else returns false.
12083          * @return {Boolean} True if element has active effects, else false
12084          */
12085     hasActiveFx : function(){
12086         return this.fxQueue && this.fxQueue[0];
12087     },
12088
12089         /**
12090          * Stops any running effects and clears the element's internal effects queue if it contains
12091          * any additional effects that haven't started yet.
12092          * @return {Roo.Element} The Element
12093          */
12094     stopFx : function(){
12095         if(this.hasActiveFx()){
12096             var cur = this.fxQueue[0];
12097             if(cur && cur.anim && cur.anim.isAnimated()){
12098                 this.fxQueue = [cur]; // clear out others
12099                 cur.anim.stop(true);
12100             }
12101         }
12102         return this;
12103     },
12104
12105         /* @private */
12106     beforeFx : function(o){
12107         if(this.hasActiveFx() && !o.concurrent){
12108            if(o.stopFx){
12109                this.stopFx();
12110                return true;
12111            }
12112            return false;
12113         }
12114         return true;
12115     },
12116
12117         /**
12118          * Returns true if the element is currently blocking so that no other effect can be queued
12119          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12120          * used to ensure that an effect initiated by a user action runs to completion prior to the
12121          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12122          * @return {Boolean} True if blocking, else false
12123          */
12124     hasFxBlock : function(){
12125         var q = this.fxQueue;
12126         return q && q[0] && q[0].block;
12127     },
12128
12129         /* @private */
12130     queueFx : function(o, fn){
12131         if(!this.fxQueue){
12132             this.fxQueue = [];
12133         }
12134         if(!this.hasFxBlock()){
12135             Roo.applyIf(o, this.fxDefaults);
12136             if(!o.concurrent){
12137                 var run = this.beforeFx(o);
12138                 fn.block = o.block;
12139                 this.fxQueue.push(fn);
12140                 if(run){
12141                     this.nextFx();
12142                 }
12143             }else{
12144                 fn.call(this);
12145             }
12146         }
12147         return this;
12148     },
12149
12150         /* @private */
12151     fxWrap : function(pos, o, vis){
12152         var wrap;
12153         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12154             var wrapXY;
12155             if(o.fixPosition){
12156                 wrapXY = this.getXY();
12157             }
12158             var div = document.createElement("div");
12159             div.style.visibility = vis;
12160             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12161             wrap.setPositioning(pos);
12162             if(wrap.getStyle("position") == "static"){
12163                 wrap.position("relative");
12164             }
12165             this.clearPositioning('auto');
12166             wrap.clip();
12167             wrap.dom.appendChild(this.dom);
12168             if(wrapXY){
12169                 wrap.setXY(wrapXY);
12170             }
12171         }
12172         return wrap;
12173     },
12174
12175         /* @private */
12176     fxUnwrap : function(wrap, pos, o){
12177         this.clearPositioning();
12178         this.setPositioning(pos);
12179         if(!o.wrap){
12180             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12181             wrap.remove();
12182         }
12183     },
12184
12185         /* @private */
12186     getFxRestore : function(){
12187         var st = this.dom.style;
12188         return {pos: this.getPositioning(), width: st.width, height : st.height};
12189     },
12190
12191         /* @private */
12192     afterFx : function(o){
12193         if(o.afterStyle){
12194             this.applyStyles(o.afterStyle);
12195         }
12196         if(o.afterCls){
12197             this.addClass(o.afterCls);
12198         }
12199         if(o.remove === true){
12200             this.remove();
12201         }
12202         Roo.callback(o.callback, o.scope, [this]);
12203         if(!o.concurrent){
12204             this.fxQueue.shift();
12205             this.nextFx();
12206         }
12207     },
12208
12209         /* @private */
12210     getFxEl : function(){ // support for composite element fx
12211         return Roo.get(this.dom);
12212     },
12213
12214         /* @private */
12215     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12216         animType = animType || 'run';
12217         opt = opt || {};
12218         var anim = Roo.lib.Anim[animType](
12219             this.dom, args,
12220             (opt.duration || defaultDur) || .35,
12221             (opt.easing || defaultEase) || 'easeOut',
12222             function(){
12223                 Roo.callback(cb, this);
12224             },
12225             this
12226         );
12227         opt.anim = anim;
12228         return anim;
12229     }
12230 };
12231
12232 // backwords compat
12233 Roo.Fx.resize = Roo.Fx.scale;
12234
12235 //When included, Roo.Fx is automatically applied to Element so that all basic
12236 //effects are available directly via the Element API
12237 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12238  * Based on:
12239  * Ext JS Library 1.1.1
12240  * Copyright(c) 2006-2007, Ext JS, LLC.
12241  *
12242  * Originally Released Under LGPL - original licence link has changed is not relivant.
12243  *
12244  * Fork - LGPL
12245  * <script type="text/javascript">
12246  */
12247
12248
12249 /**
12250  * @class Roo.CompositeElement
12251  * Standard composite class. Creates a Roo.Element for every element in the collection.
12252  * <br><br>
12253  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12254  * actions will be performed on all the elements in this collection.</b>
12255  * <br><br>
12256  * All methods return <i>this</i> and can be chained.
12257  <pre><code>
12258  var els = Roo.select("#some-el div.some-class", true);
12259  // or select directly from an existing element
12260  var el = Roo.get('some-el');
12261  el.select('div.some-class', true);
12262
12263  els.setWidth(100); // all elements become 100 width
12264  els.hide(true); // all elements fade out and hide
12265  // or
12266  els.setWidth(100).hide(true);
12267  </code></pre>
12268  */
12269 Roo.CompositeElement = function(els){
12270     this.elements = [];
12271     this.addElements(els);
12272 };
12273 Roo.CompositeElement.prototype = {
12274     isComposite: true,
12275     addElements : function(els){
12276         if(!els) {
12277             return this;
12278         }
12279         if(typeof els == "string"){
12280             els = Roo.Element.selectorFunction(els);
12281         }
12282         var yels = this.elements;
12283         var index = yels.length-1;
12284         for(var i = 0, len = els.length; i < len; i++) {
12285                 yels[++index] = Roo.get(els[i]);
12286         }
12287         return this;
12288     },
12289
12290     /**
12291     * Clears this composite and adds the elements returned by the passed selector.
12292     * @param {String/Array} els A string CSS selector, an array of elements or an element
12293     * @return {CompositeElement} this
12294     */
12295     fill : function(els){
12296         this.elements = [];
12297         this.add(els);
12298         return this;
12299     },
12300
12301     /**
12302     * Filters this composite to only elements that match the passed selector.
12303     * @param {String} selector A string CSS selector
12304     * @param {Boolean} inverse return inverse filter (not matches)
12305     * @return {CompositeElement} this
12306     */
12307     filter : function(selector, inverse){
12308         var els = [];
12309         inverse = inverse || false;
12310         this.each(function(el){
12311             var match = inverse ? !el.is(selector) : el.is(selector);
12312             if(match){
12313                 els[els.length] = el.dom;
12314             }
12315         });
12316         this.fill(els);
12317         return this;
12318     },
12319
12320     invoke : function(fn, args){
12321         var els = this.elements;
12322         for(var i = 0, len = els.length; i < len; i++) {
12323                 Roo.Element.prototype[fn].apply(els[i], args);
12324         }
12325         return this;
12326     },
12327     /**
12328     * Adds elements to this composite.
12329     * @param {String/Array} els A string CSS selector, an array of elements or an element
12330     * @return {CompositeElement} this
12331     */
12332     add : function(els){
12333         if(typeof els == "string"){
12334             this.addElements(Roo.Element.selectorFunction(els));
12335         }else if(els.length !== undefined){
12336             this.addElements(els);
12337         }else{
12338             this.addElements([els]);
12339         }
12340         return this;
12341     },
12342     /**
12343     * Calls the passed function passing (el, this, index) for each element in this composite.
12344     * @param {Function} fn The function to call
12345     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12346     * @return {CompositeElement} this
12347     */
12348     each : function(fn, scope){
12349         var els = this.elements;
12350         for(var i = 0, len = els.length; i < len; i++){
12351             if(fn.call(scope || els[i], els[i], this, i) === false) {
12352                 break;
12353             }
12354         }
12355         return this;
12356     },
12357
12358     /**
12359      * Returns the Element object at the specified index
12360      * @param {Number} index
12361      * @return {Roo.Element}
12362      */
12363     item : function(index){
12364         return this.elements[index] || null;
12365     },
12366
12367     /**
12368      * Returns the first Element
12369      * @return {Roo.Element}
12370      */
12371     first : function(){
12372         return this.item(0);
12373     },
12374
12375     /**
12376      * Returns the last Element
12377      * @return {Roo.Element}
12378      */
12379     last : function(){
12380         return this.item(this.elements.length-1);
12381     },
12382
12383     /**
12384      * Returns the number of elements in this composite
12385      * @return Number
12386      */
12387     getCount : function(){
12388         return this.elements.length;
12389     },
12390
12391     /**
12392      * Returns true if this composite contains the passed element
12393      * @return Boolean
12394      */
12395     contains : function(el){
12396         return this.indexOf(el) !== -1;
12397     },
12398
12399     /**
12400      * Returns true if this composite contains the passed element
12401      * @return Boolean
12402      */
12403     indexOf : function(el){
12404         return this.elements.indexOf(Roo.get(el));
12405     },
12406
12407
12408     /**
12409     * Removes the specified element(s).
12410     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12411     * or an array of any of those.
12412     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12413     * @return {CompositeElement} this
12414     */
12415     removeElement : function(el, removeDom){
12416         if(el instanceof Array){
12417             for(var i = 0, len = el.length; i < len; i++){
12418                 this.removeElement(el[i]);
12419             }
12420             return this;
12421         }
12422         var index = typeof el == 'number' ? el : this.indexOf(el);
12423         if(index !== -1){
12424             if(removeDom){
12425                 var d = this.elements[index];
12426                 if(d.dom){
12427                     d.remove();
12428                 }else{
12429                     d.parentNode.removeChild(d);
12430                 }
12431             }
12432             this.elements.splice(index, 1);
12433         }
12434         return this;
12435     },
12436
12437     /**
12438     * Replaces the specified element with the passed element.
12439     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12440     * to replace.
12441     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12442     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12443     * @return {CompositeElement} this
12444     */
12445     replaceElement : function(el, replacement, domReplace){
12446         var index = typeof el == 'number' ? el : this.indexOf(el);
12447         if(index !== -1){
12448             if(domReplace){
12449                 this.elements[index].replaceWith(replacement);
12450             }else{
12451                 this.elements.splice(index, 1, Roo.get(replacement))
12452             }
12453         }
12454         return this;
12455     },
12456
12457     /**
12458      * Removes all elements.
12459      */
12460     clear : function(){
12461         this.elements = [];
12462     }
12463 };
12464 (function(){
12465     Roo.CompositeElement.createCall = function(proto, fnName){
12466         if(!proto[fnName]){
12467             proto[fnName] = function(){
12468                 return this.invoke(fnName, arguments);
12469             };
12470         }
12471     };
12472     for(var fnName in Roo.Element.prototype){
12473         if(typeof Roo.Element.prototype[fnName] == "function"){
12474             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12475         }
12476     };
12477 })();
12478 /*
12479  * Based on:
12480  * Ext JS Library 1.1.1
12481  * Copyright(c) 2006-2007, Ext JS, LLC.
12482  *
12483  * Originally Released Under LGPL - original licence link has changed is not relivant.
12484  *
12485  * Fork - LGPL
12486  * <script type="text/javascript">
12487  */
12488
12489 /**
12490  * @class Roo.CompositeElementLite
12491  * @extends Roo.CompositeElement
12492  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12493  <pre><code>
12494  var els = Roo.select("#some-el div.some-class");
12495  // or select directly from an existing element
12496  var el = Roo.get('some-el');
12497  el.select('div.some-class');
12498
12499  els.setWidth(100); // all elements become 100 width
12500  els.hide(true); // all elements fade out and hide
12501  // or
12502  els.setWidth(100).hide(true);
12503  </code></pre><br><br>
12504  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12505  * actions will be performed on all the elements in this collection.</b>
12506  */
12507 Roo.CompositeElementLite = function(els){
12508     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12509     this.el = new Roo.Element.Flyweight();
12510 };
12511 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12512     addElements : function(els){
12513         if(els){
12514             if(els instanceof Array){
12515                 this.elements = this.elements.concat(els);
12516             }else{
12517                 var yels = this.elements;
12518                 var index = yels.length-1;
12519                 for(var i = 0, len = els.length; i < len; i++) {
12520                     yels[++index] = els[i];
12521                 }
12522             }
12523         }
12524         return this;
12525     },
12526     invoke : function(fn, args){
12527         var els = this.elements;
12528         var el = this.el;
12529         for(var i = 0, len = els.length; i < len; i++) {
12530             el.dom = els[i];
12531                 Roo.Element.prototype[fn].apply(el, args);
12532         }
12533         return this;
12534     },
12535     /**
12536      * Returns a flyweight Element of the dom element object at the specified index
12537      * @param {Number} index
12538      * @return {Roo.Element}
12539      */
12540     item : function(index){
12541         if(!this.elements[index]){
12542             return null;
12543         }
12544         this.el.dom = this.elements[index];
12545         return this.el;
12546     },
12547
12548     // fixes scope with flyweight
12549     addListener : function(eventName, handler, scope, opt){
12550         var els = this.elements;
12551         for(var i = 0, len = els.length; i < len; i++) {
12552             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12553         }
12554         return this;
12555     },
12556
12557     /**
12558     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12559     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12560     * a reference to the dom node, use el.dom.</b>
12561     * @param {Function} fn The function to call
12562     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12563     * @return {CompositeElement} this
12564     */
12565     each : function(fn, scope){
12566         var els = this.elements;
12567         var el = this.el;
12568         for(var i = 0, len = els.length; i < len; i++){
12569             el.dom = els[i];
12570                 if(fn.call(scope || el, el, this, i) === false){
12571                 break;
12572             }
12573         }
12574         return this;
12575     },
12576
12577     indexOf : function(el){
12578         return this.elements.indexOf(Roo.getDom(el));
12579     },
12580
12581     replaceElement : function(el, replacement, domReplace){
12582         var index = typeof el == 'number' ? el : this.indexOf(el);
12583         if(index !== -1){
12584             replacement = Roo.getDom(replacement);
12585             if(domReplace){
12586                 var d = this.elements[index];
12587                 d.parentNode.insertBefore(replacement, d);
12588                 d.parentNode.removeChild(d);
12589             }
12590             this.elements.splice(index, 1, replacement);
12591         }
12592         return this;
12593     }
12594 });
12595 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12596
12597 /*
12598  * Based on:
12599  * Ext JS Library 1.1.1
12600  * Copyright(c) 2006-2007, Ext JS, LLC.
12601  *
12602  * Originally Released Under LGPL - original licence link has changed is not relivant.
12603  *
12604  * Fork - LGPL
12605  * <script type="text/javascript">
12606  */
12607
12608  
12609
12610 /**
12611  * @class Roo.data.Connection
12612  * @extends Roo.util.Observable
12613  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12614  * either to a configured URL, or to a URL specified at request time. 
12615  * 
12616  * Requests made by this class are asynchronous, and will return immediately. No data from
12617  * the server will be available to the statement immediately following the {@link #request} call.
12618  * To process returned data, use a callback in the request options object, or an event listener.
12619  * 
12620  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12621  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12622  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12623  * property and, if present, the IFRAME's XML document as the responseXML property.
12624  * 
12625  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12626  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12627  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12628  * standard DOM methods.
12629  * @constructor
12630  * @param {Object} config a configuration object.
12631  */
12632 Roo.data.Connection = function(config){
12633     Roo.apply(this, config);
12634     this.addEvents({
12635         /**
12636          * @event beforerequest
12637          * Fires before a network request is made to retrieve a data object.
12638          * @param {Connection} conn This Connection object.
12639          * @param {Object} options The options config object passed to the {@link #request} method.
12640          */
12641         "beforerequest" : true,
12642         /**
12643          * @event requestcomplete
12644          * Fires if the request was successfully completed.
12645          * @param {Connection} conn This Connection object.
12646          * @param {Object} response The XHR object containing the response data.
12647          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12648          * @param {Object} options The options config object passed to the {@link #request} method.
12649          */
12650         "requestcomplete" : true,
12651         /**
12652          * @event requestexception
12653          * Fires if an error HTTP status was returned from the server.
12654          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12655          * @param {Connection} conn This Connection object.
12656          * @param {Object} response The XHR object containing the response data.
12657          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12658          * @param {Object} options The options config object passed to the {@link #request} method.
12659          */
12660         "requestexception" : true
12661     });
12662     Roo.data.Connection.superclass.constructor.call(this);
12663 };
12664
12665 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12666     /**
12667      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12668      */
12669     /**
12670      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12671      * extra parameters to each request made by this object. (defaults to undefined)
12672      */
12673     /**
12674      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12675      *  to each request made by this object. (defaults to undefined)
12676      */
12677     /**
12678      * @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)
12679      */
12680     /**
12681      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12682      */
12683     timeout : 30000,
12684     /**
12685      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12686      * @type Boolean
12687      */
12688     autoAbort:false,
12689
12690     /**
12691      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12692      * @type Boolean
12693      */
12694     disableCaching: true,
12695
12696     /**
12697      * Sends an HTTP request to a remote server.
12698      * @param {Object} options An object which may contain the following properties:<ul>
12699      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12700      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12701      * request, a url encoded string or a function to call to get either.</li>
12702      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12703      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12704      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12705      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12706      * <li>options {Object} The parameter to the request call.</li>
12707      * <li>success {Boolean} True if the request succeeded.</li>
12708      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12709      * </ul></li>
12710      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12711      * The callback is passed the following parameters:<ul>
12712      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12713      * <li>options {Object} The parameter to the request call.</li>
12714      * </ul></li>
12715      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12716      * The callback is passed the following parameters:<ul>
12717      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12718      * <li>options {Object} The parameter to the request call.</li>
12719      * </ul></li>
12720      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12721      * for the callback function. Defaults to the browser window.</li>
12722      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12723      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12724      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12725      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12726      * params for the post data. Any params will be appended to the URL.</li>
12727      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12728      * </ul>
12729      * @return {Number} transactionId
12730      */
12731     request : function(o){
12732         if(this.fireEvent("beforerequest", this, o) !== false){
12733             var p = o.params;
12734
12735             if(typeof p == "function"){
12736                 p = p.call(o.scope||window, o);
12737             }
12738             if(typeof p == "object"){
12739                 p = Roo.urlEncode(o.params);
12740             }
12741             if(this.extraParams){
12742                 var extras = Roo.urlEncode(this.extraParams);
12743                 p = p ? (p + '&' + extras) : extras;
12744             }
12745
12746             var url = o.url || this.url;
12747             if(typeof url == 'function'){
12748                 url = url.call(o.scope||window, o);
12749             }
12750
12751             if(o.form){
12752                 var form = Roo.getDom(o.form);
12753                 url = url || form.action;
12754
12755                 var enctype = form.getAttribute("enctype");
12756                 
12757                 if (o.formData) {
12758                     return this.doFormDataUpload(o, url);
12759                 }
12760                 
12761                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12762                     return this.doFormUpload(o, p, url);
12763                 }
12764                 var f = Roo.lib.Ajax.serializeForm(form);
12765                 p = p ? (p + '&' + f) : f;
12766             }
12767             
12768             if (!o.form && o.formData) {
12769                 o.formData = o.formData === true ? new FormData() : o.formData;
12770                 for (var k in o.params) {
12771                     o.formData.append(k,o.params[k]);
12772                 }
12773                     
12774                 return this.doFormDataUpload(o, url);
12775             }
12776             
12777
12778             var hs = o.headers;
12779             if(this.defaultHeaders){
12780                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12781                 if(!o.headers){
12782                     o.headers = hs;
12783                 }
12784             }
12785
12786             var cb = {
12787                 success: this.handleResponse,
12788                 failure: this.handleFailure,
12789                 scope: this,
12790                 argument: {options: o},
12791                 timeout : o.timeout || this.timeout
12792             };
12793
12794             var method = o.method||this.method||(p ? "POST" : "GET");
12795
12796             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12797                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12798             }
12799
12800             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12801                 if(o.autoAbort){
12802                     this.abort();
12803                 }
12804             }else if(this.autoAbort !== false){
12805                 this.abort();
12806             }
12807
12808             if((method == 'GET' && p) || o.xmlData){
12809                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12810                 p = '';
12811             }
12812             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12813             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12814             Roo.lib.Ajax.useDefaultHeader == true;
12815             return this.transId;
12816         }else{
12817             Roo.callback(o.callback, o.scope, [o, null, null]);
12818             return null;
12819         }
12820     },
12821
12822     /**
12823      * Determine whether this object has a request outstanding.
12824      * @param {Number} transactionId (Optional) defaults to the last transaction
12825      * @return {Boolean} True if there is an outstanding request.
12826      */
12827     isLoading : function(transId){
12828         if(transId){
12829             return Roo.lib.Ajax.isCallInProgress(transId);
12830         }else{
12831             return this.transId ? true : false;
12832         }
12833     },
12834
12835     /**
12836      * Aborts any outstanding request.
12837      * @param {Number} transactionId (Optional) defaults to the last transaction
12838      */
12839     abort : function(transId){
12840         if(transId || this.isLoading()){
12841             Roo.lib.Ajax.abort(transId || this.transId);
12842         }
12843     },
12844
12845     // private
12846     handleResponse : function(response){
12847         this.transId = false;
12848         var options = response.argument.options;
12849         response.argument = options ? options.argument : null;
12850         this.fireEvent("requestcomplete", this, response, options);
12851         Roo.callback(options.success, options.scope, [response, options]);
12852         Roo.callback(options.callback, options.scope, [options, true, response]);
12853     },
12854
12855     // private
12856     handleFailure : function(response, e){
12857         this.transId = false;
12858         var options = response.argument.options;
12859         response.argument = options ? options.argument : null;
12860         this.fireEvent("requestexception", this, response, options, e);
12861         Roo.callback(options.failure, options.scope, [response, options]);
12862         Roo.callback(options.callback, options.scope, [options, false, response]);
12863     },
12864
12865     // private
12866     doFormUpload : function(o, ps, url){
12867         var id = Roo.id();
12868         var frame = document.createElement('iframe');
12869         frame.id = id;
12870         frame.name = id;
12871         frame.className = 'x-hidden';
12872         if(Roo.isIE){
12873             frame.src = Roo.SSL_SECURE_URL;
12874         }
12875         document.body.appendChild(frame);
12876
12877         if(Roo.isIE){
12878            document.frames[id].name = id;
12879         }
12880
12881         var form = Roo.getDom(o.form);
12882         form.target = id;
12883         form.method = 'POST';
12884         form.enctype = form.encoding = 'multipart/form-data';
12885         if(url){
12886             form.action = url;
12887         }
12888
12889         var hiddens, hd;
12890         if(ps){ // add dynamic params
12891             hiddens = [];
12892             ps = Roo.urlDecode(ps, false);
12893             for(var k in ps){
12894                 if(ps.hasOwnProperty(k)){
12895                     hd = document.createElement('input');
12896                     hd.type = 'hidden';
12897                     hd.name = k;
12898                     hd.value = ps[k];
12899                     form.appendChild(hd);
12900                     hiddens.push(hd);
12901                 }
12902             }
12903         }
12904
12905         function cb(){
12906             var r = {  // bogus response object
12907                 responseText : '',
12908                 responseXML : null
12909             };
12910
12911             r.argument = o ? o.argument : null;
12912
12913             try { //
12914                 var doc;
12915                 if(Roo.isIE){
12916                     doc = frame.contentWindow.document;
12917                 }else {
12918                     doc = (frame.contentDocument || window.frames[id].document);
12919                 }
12920                 if(doc && doc.body){
12921                     r.responseText = doc.body.innerHTML;
12922                 }
12923                 if(doc && doc.XMLDocument){
12924                     r.responseXML = doc.XMLDocument;
12925                 }else {
12926                     r.responseXML = doc;
12927                 }
12928             }
12929             catch(e) {
12930                 // ignore
12931             }
12932
12933             Roo.EventManager.removeListener(frame, 'load', cb, this);
12934
12935             this.fireEvent("requestcomplete", this, r, o);
12936             Roo.callback(o.success, o.scope, [r, o]);
12937             Roo.callback(o.callback, o.scope, [o, true, r]);
12938
12939             setTimeout(function(){document.body.removeChild(frame);}, 100);
12940         }
12941
12942         Roo.EventManager.on(frame, 'load', cb, this);
12943         form.submit();
12944
12945         if(hiddens){ // remove dynamic params
12946             for(var i = 0, len = hiddens.length; i < len; i++){
12947                 form.removeChild(hiddens[i]);
12948             }
12949         }
12950     },
12951     // this is a 'formdata version???'
12952     
12953     
12954     doFormDataUpload : function(o,  url)
12955     {
12956         var formData;
12957         if (o.form) {
12958             var form =  Roo.getDom(o.form);
12959             form.enctype = form.encoding = 'multipart/form-data';
12960             formData = o.formData === true ? new FormData(form) : o.formData;
12961         } else {
12962             formData = o.formData === true ? new FormData() : o.formData;
12963         }
12964         
12965       
12966         var cb = {
12967             success: this.handleResponse,
12968             failure: this.handleFailure,
12969             scope: this,
12970             argument: {options: o},
12971             timeout : o.timeout || this.timeout
12972         };
12973  
12974         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12975             if(o.autoAbort){
12976                 this.abort();
12977             }
12978         }else if(this.autoAbort !== false){
12979             this.abort();
12980         }
12981
12982         //Roo.lib.Ajax.defaultPostHeader = null;
12983         Roo.lib.Ajax.useDefaultHeader = false;
12984         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12985         Roo.lib.Ajax.useDefaultHeader = true;
12986  
12987          
12988     }
12989     
12990 });
12991 /*
12992  * Based on:
12993  * Ext JS Library 1.1.1
12994  * Copyright(c) 2006-2007, Ext JS, LLC.
12995  *
12996  * Originally Released Under LGPL - original licence link has changed is not relivant.
12997  *
12998  * Fork - LGPL
12999  * <script type="text/javascript">
13000  */
13001  
13002 /**
13003  * Global Ajax request class.
13004  * 
13005  * @class Roo.Ajax
13006  * @extends Roo.data.Connection
13007  * @static
13008  * 
13009  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13010  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13011  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13012  * @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)
13013  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13014  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13015  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13016  */
13017 Roo.Ajax = new Roo.data.Connection({
13018     // fix up the docs
13019     /**
13020      * @scope Roo.Ajax
13021      * @type {Boolear} 
13022      */
13023     autoAbort : false,
13024
13025     /**
13026      * Serialize the passed form into a url encoded string
13027      * @scope Roo.Ajax
13028      * @param {String/HTMLElement} form
13029      * @return {String}
13030      */
13031     serializeForm : function(form){
13032         return Roo.lib.Ajax.serializeForm(form);
13033     }
13034 });/*
13035  * Based on:
13036  * Ext JS Library 1.1.1
13037  * Copyright(c) 2006-2007, Ext JS, LLC.
13038  *
13039  * Originally Released Under LGPL - original licence link has changed is not relivant.
13040  *
13041  * Fork - LGPL
13042  * <script type="text/javascript">
13043  */
13044
13045  
13046 /**
13047  * @class Roo.UpdateManager
13048  * @extends Roo.util.Observable
13049  * Provides AJAX-style update for Element object.<br><br>
13050  * Usage:<br>
13051  * <pre><code>
13052  * // Get it from a Roo.Element object
13053  * var el = Roo.get("foo");
13054  * var mgr = el.getUpdateManager();
13055  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13056  * ...
13057  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13058  * <br>
13059  * // or directly (returns the same UpdateManager instance)
13060  * var mgr = new Roo.UpdateManager("myElementId");
13061  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13062  * mgr.on("update", myFcnNeedsToKnow);
13063  * <br>
13064    // short handed call directly from the element object
13065    Roo.get("foo").load({
13066         url: "bar.php",
13067         scripts:true,
13068         params: "for=bar",
13069         text: "Loading Foo..."
13070    });
13071  * </code></pre>
13072  * @constructor
13073  * Create new UpdateManager directly.
13074  * @param {String/HTMLElement/Roo.Element} el The element to update
13075  * @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).
13076  */
13077 Roo.UpdateManager = function(el, forceNew){
13078     el = Roo.get(el);
13079     if(!forceNew && el.updateManager){
13080         return el.updateManager;
13081     }
13082     /**
13083      * The Element object
13084      * @type Roo.Element
13085      */
13086     this.el = el;
13087     /**
13088      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13089      * @type String
13090      */
13091     this.defaultUrl = null;
13092
13093     this.addEvents({
13094         /**
13095          * @event beforeupdate
13096          * Fired before an update is made, return false from your handler and the update is cancelled.
13097          * @param {Roo.Element} el
13098          * @param {String/Object/Function} url
13099          * @param {String/Object} params
13100          */
13101         "beforeupdate": true,
13102         /**
13103          * @event update
13104          * Fired after successful update is made.
13105          * @param {Roo.Element} el
13106          * @param {Object} oResponseObject The response Object
13107          */
13108         "update": true,
13109         /**
13110          * @event failure
13111          * Fired on update failure.
13112          * @param {Roo.Element} el
13113          * @param {Object} oResponseObject The response Object
13114          */
13115         "failure": true
13116     });
13117     var d = Roo.UpdateManager.defaults;
13118     /**
13119      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13120      * @type String
13121      */
13122     this.sslBlankUrl = d.sslBlankUrl;
13123     /**
13124      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13125      * @type Boolean
13126      */
13127     this.disableCaching = d.disableCaching;
13128     /**
13129      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13130      * @type String
13131      */
13132     this.indicatorText = d.indicatorText;
13133     /**
13134      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13135      * @type String
13136      */
13137     this.showLoadIndicator = d.showLoadIndicator;
13138     /**
13139      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13140      * @type Number
13141      */
13142     this.timeout = d.timeout;
13143
13144     /**
13145      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13146      * @type Boolean
13147      */
13148     this.loadScripts = d.loadScripts;
13149
13150     /**
13151      * Transaction object of current executing transaction
13152      */
13153     this.transaction = null;
13154
13155     /**
13156      * @private
13157      */
13158     this.autoRefreshProcId = null;
13159     /**
13160      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13161      * @type Function
13162      */
13163     this.refreshDelegate = this.refresh.createDelegate(this);
13164     /**
13165      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13166      * @type Function
13167      */
13168     this.updateDelegate = this.update.createDelegate(this);
13169     /**
13170      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13171      * @type Function
13172      */
13173     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13174     /**
13175      * @private
13176      */
13177     this.successDelegate = this.processSuccess.createDelegate(this);
13178     /**
13179      * @private
13180      */
13181     this.failureDelegate = this.processFailure.createDelegate(this);
13182
13183     if(!this.renderer){
13184      /**
13185       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13186       */
13187     this.renderer = new Roo.UpdateManager.BasicRenderer();
13188     }
13189     
13190     Roo.UpdateManager.superclass.constructor.call(this);
13191 };
13192
13193 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13194     /**
13195      * Get the Element this UpdateManager is bound to
13196      * @return {Roo.Element} The element
13197      */
13198     getEl : function(){
13199         return this.el;
13200     },
13201     /**
13202      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13203      * @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:
13204 <pre><code>
13205 um.update({<br/>
13206     url: "your-url.php",<br/>
13207     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13208     callback: yourFunction,<br/>
13209     scope: yourObject, //(optional scope)  <br/>
13210     discardUrl: false, <br/>
13211     nocache: false,<br/>
13212     text: "Loading...",<br/>
13213     timeout: 30,<br/>
13214     scripts: false<br/>
13215 });
13216 </code></pre>
13217      * The only required property is url. The optional properties nocache, text and scripts
13218      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13219      * @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}
13220      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13221      * @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.
13222      */
13223     update : function(url, params, callback, discardUrl){
13224         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13225             var method = this.method,
13226                 cfg;
13227             if(typeof url == "object"){ // must be config object
13228                 cfg = url;
13229                 url = cfg.url;
13230                 params = params || cfg.params;
13231                 callback = callback || cfg.callback;
13232                 discardUrl = discardUrl || cfg.discardUrl;
13233                 if(callback && cfg.scope){
13234                     callback = callback.createDelegate(cfg.scope);
13235                 }
13236                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13237                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13238                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13239                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13240                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13241             }
13242             this.showLoading();
13243             if(!discardUrl){
13244                 this.defaultUrl = url;
13245             }
13246             if(typeof url == "function"){
13247                 url = url.call(this);
13248             }
13249
13250             method = method || (params ? "POST" : "GET");
13251             if(method == "GET"){
13252                 url = this.prepareUrl(url);
13253             }
13254
13255             var o = Roo.apply(cfg ||{}, {
13256                 url : url,
13257                 params: params,
13258                 success: this.successDelegate,
13259                 failure: this.failureDelegate,
13260                 callback: undefined,
13261                 timeout: (this.timeout*1000),
13262                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13263             });
13264             Roo.log("updated manager called with timeout of " + o.timeout);
13265             this.transaction = Roo.Ajax.request(o);
13266         }
13267     },
13268
13269     /**
13270      * 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.
13271      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13272      * @param {String/HTMLElement} form The form Id or form element
13273      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13274      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13275      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13276      */
13277     formUpdate : function(form, url, reset, callback){
13278         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13279             if(typeof url == "function"){
13280                 url = url.call(this);
13281             }
13282             form = Roo.getDom(form);
13283             this.transaction = Roo.Ajax.request({
13284                 form: form,
13285                 url:url,
13286                 success: this.successDelegate,
13287                 failure: this.failureDelegate,
13288                 timeout: (this.timeout*1000),
13289                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13290             });
13291             this.showLoading.defer(1, this);
13292         }
13293     },
13294
13295     /**
13296      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13297      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13298      */
13299     refresh : function(callback){
13300         if(this.defaultUrl == null){
13301             return;
13302         }
13303         this.update(this.defaultUrl, null, callback, true);
13304     },
13305
13306     /**
13307      * Set this element to auto refresh.
13308      * @param {Number} interval How often to update (in seconds).
13309      * @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)
13310      * @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}
13311      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13312      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13313      */
13314     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13315         if(refreshNow){
13316             this.update(url || this.defaultUrl, params, callback, true);
13317         }
13318         if(this.autoRefreshProcId){
13319             clearInterval(this.autoRefreshProcId);
13320         }
13321         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13322     },
13323
13324     /**
13325      * Stop auto refresh on this element.
13326      */
13327      stopAutoRefresh : function(){
13328         if(this.autoRefreshProcId){
13329             clearInterval(this.autoRefreshProcId);
13330             delete this.autoRefreshProcId;
13331         }
13332     },
13333
13334     isAutoRefreshing : function(){
13335        return this.autoRefreshProcId ? true : false;
13336     },
13337     /**
13338      * Called to update the element to "Loading" state. Override to perform custom action.
13339      */
13340     showLoading : function(){
13341         if(this.showLoadIndicator){
13342             this.el.update(this.indicatorText);
13343         }
13344     },
13345
13346     /**
13347      * Adds unique parameter to query string if disableCaching = true
13348      * @private
13349      */
13350     prepareUrl : function(url){
13351         if(this.disableCaching){
13352             var append = "_dc=" + (new Date().getTime());
13353             if(url.indexOf("?") !== -1){
13354                 url += "&" + append;
13355             }else{
13356                 url += "?" + append;
13357             }
13358         }
13359         return url;
13360     },
13361
13362     /**
13363      * @private
13364      */
13365     processSuccess : function(response){
13366         this.transaction = null;
13367         if(response.argument.form && response.argument.reset){
13368             try{ // put in try/catch since some older FF releases had problems with this
13369                 response.argument.form.reset();
13370             }catch(e){}
13371         }
13372         if(this.loadScripts){
13373             this.renderer.render(this.el, response, this,
13374                 this.updateComplete.createDelegate(this, [response]));
13375         }else{
13376             this.renderer.render(this.el, response, this);
13377             this.updateComplete(response);
13378         }
13379     },
13380
13381     updateComplete : function(response){
13382         this.fireEvent("update", this.el, response);
13383         if(typeof response.argument.callback == "function"){
13384             response.argument.callback(this.el, true, response);
13385         }
13386     },
13387
13388     /**
13389      * @private
13390      */
13391     processFailure : function(response){
13392         this.transaction = null;
13393         this.fireEvent("failure", this.el, response);
13394         if(typeof response.argument.callback == "function"){
13395             response.argument.callback(this.el, false, response);
13396         }
13397     },
13398
13399     /**
13400      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13401      * @param {Object} renderer The object implementing the render() method
13402      */
13403     setRenderer : function(renderer){
13404         this.renderer = renderer;
13405     },
13406
13407     getRenderer : function(){
13408        return this.renderer;
13409     },
13410
13411     /**
13412      * Set the defaultUrl used for updates
13413      * @param {String/Function} defaultUrl The url or a function to call to get the url
13414      */
13415     setDefaultUrl : function(defaultUrl){
13416         this.defaultUrl = defaultUrl;
13417     },
13418
13419     /**
13420      * Aborts the executing transaction
13421      */
13422     abort : function(){
13423         if(this.transaction){
13424             Roo.Ajax.abort(this.transaction);
13425         }
13426     },
13427
13428     /**
13429      * Returns true if an update is in progress
13430      * @return {Boolean}
13431      */
13432     isUpdating : function(){
13433         if(this.transaction){
13434             return Roo.Ajax.isLoading(this.transaction);
13435         }
13436         return false;
13437     }
13438 });
13439
13440 /**
13441  * @class Roo.UpdateManager.defaults
13442  * @static (not really - but it helps the doc tool)
13443  * The defaults collection enables customizing the default properties of UpdateManager
13444  */
13445    Roo.UpdateManager.defaults = {
13446        /**
13447          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13448          * @type Number
13449          */
13450          timeout : 30,
13451
13452          /**
13453          * True to process scripts by default (Defaults to false).
13454          * @type Boolean
13455          */
13456         loadScripts : false,
13457
13458         /**
13459         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13460         * @type String
13461         */
13462         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13463         /**
13464          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13465          * @type Boolean
13466          */
13467         disableCaching : false,
13468         /**
13469          * Whether to show indicatorText when loading (Defaults to true).
13470          * @type Boolean
13471          */
13472         showLoadIndicator : true,
13473         /**
13474          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13475          * @type String
13476          */
13477         indicatorText : '<div class="loading-indicator">Loading...</div>'
13478    };
13479
13480 /**
13481  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13482  *Usage:
13483  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13484  * @param {String/HTMLElement/Roo.Element} el The element to update
13485  * @param {String} url The url
13486  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13487  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13488  * @static
13489  * @deprecated
13490  * @member Roo.UpdateManager
13491  */
13492 Roo.UpdateManager.updateElement = function(el, url, params, options){
13493     var um = Roo.get(el, true).getUpdateManager();
13494     Roo.apply(um, options);
13495     um.update(url, params, options ? options.callback : null);
13496 };
13497 // alias for backwards compat
13498 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13499 /**
13500  * @class Roo.UpdateManager.BasicRenderer
13501  * Default Content renderer. Updates the elements innerHTML with the responseText.
13502  */
13503 Roo.UpdateManager.BasicRenderer = function(){};
13504
13505 Roo.UpdateManager.BasicRenderer.prototype = {
13506     /**
13507      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13508      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13509      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13510      * @param {Roo.Element} el The element being rendered
13511      * @param {Object} response The YUI Connect response object
13512      * @param {UpdateManager} updateManager The calling update manager
13513      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13514      */
13515      render : function(el, response, updateManager, callback){
13516         el.update(response.responseText, updateManager.loadScripts, callback);
13517     }
13518 };
13519 /*
13520  * Based on:
13521  * Roo JS
13522  * (c)) Alan Knowles
13523  * Licence : LGPL
13524  */
13525
13526
13527 /**
13528  * @class Roo.DomTemplate
13529  * @extends Roo.Template
13530  * An effort at a dom based template engine..
13531  *
13532  * Similar to XTemplate, except it uses dom parsing to create the template..
13533  *
13534  * Supported features:
13535  *
13536  *  Tags:
13537
13538 <pre><code>
13539       {a_variable} - output encoded.
13540       {a_variable.format:("Y-m-d")} - call a method on the variable
13541       {a_variable:raw} - unencoded output
13542       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13543       {a_variable:this.method_on_template(...)} - call a method on the template object.
13544  
13545 </code></pre>
13546  *  The tpl tag:
13547 <pre><code>
13548         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13549         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13550         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13551         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13552   
13553 </code></pre>
13554  *      
13555  */
13556 Roo.DomTemplate = function()
13557 {
13558      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13559      if (this.html) {
13560         this.compile();
13561      }
13562 };
13563
13564
13565 Roo.extend(Roo.DomTemplate, Roo.Template, {
13566     /**
13567      * id counter for sub templates.
13568      */
13569     id : 0,
13570     /**
13571      * flag to indicate if dom parser is inside a pre,
13572      * it will strip whitespace if not.
13573      */
13574     inPre : false,
13575     
13576     /**
13577      * The various sub templates
13578      */
13579     tpls : false,
13580     
13581     
13582     
13583     /**
13584      *
13585      * basic tag replacing syntax
13586      * WORD:WORD()
13587      *
13588      * // you can fake an object call by doing this
13589      *  x.t:(test,tesT) 
13590      * 
13591      */
13592     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13593     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13594     
13595     iterChild : function (node, method) {
13596         
13597         var oldPre = this.inPre;
13598         if (node.tagName == 'PRE') {
13599             this.inPre = true;
13600         }
13601         for( var i = 0; i < node.childNodes.length; i++) {
13602             method.call(this, node.childNodes[i]);
13603         }
13604         this.inPre = oldPre;
13605     },
13606     
13607     
13608     
13609     /**
13610      * compile the template
13611      *
13612      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13613      *
13614      */
13615     compile: function()
13616     {
13617         var s = this.html;
13618         
13619         // covert the html into DOM...
13620         var doc = false;
13621         var div =false;
13622         try {
13623             doc = document.implementation.createHTMLDocument("");
13624             doc.documentElement.innerHTML =   this.html  ;
13625             div = doc.documentElement;
13626         } catch (e) {
13627             // old IE... - nasty -- it causes all sorts of issues.. with
13628             // images getting pulled from server..
13629             div = document.createElement('div');
13630             div.innerHTML = this.html;
13631         }
13632         //doc.documentElement.innerHTML = htmlBody
13633          
13634         
13635         
13636         this.tpls = [];
13637         var _t = this;
13638         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13639         
13640         var tpls = this.tpls;
13641         
13642         // create a top level template from the snippet..
13643         
13644         //Roo.log(div.innerHTML);
13645         
13646         var tpl = {
13647             uid : 'master',
13648             id : this.id++,
13649             attr : false,
13650             value : false,
13651             body : div.innerHTML,
13652             
13653             forCall : false,
13654             execCall : false,
13655             dom : div,
13656             isTop : true
13657             
13658         };
13659         tpls.unshift(tpl);
13660         
13661         
13662         // compile them...
13663         this.tpls = [];
13664         Roo.each(tpls, function(tp){
13665             this.compileTpl(tp);
13666             this.tpls[tp.id] = tp;
13667         }, this);
13668         
13669         this.master = tpls[0];
13670         return this;
13671         
13672         
13673     },
13674     
13675     compileNode : function(node, istop) {
13676         // test for
13677         //Roo.log(node);
13678         
13679         
13680         // skip anything not a tag..
13681         if (node.nodeType != 1) {
13682             if (node.nodeType == 3 && !this.inPre) {
13683                 // reduce white space..
13684                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13685                 
13686             }
13687             return;
13688         }
13689         
13690         var tpl = {
13691             uid : false,
13692             id : false,
13693             attr : false,
13694             value : false,
13695             body : '',
13696             
13697             forCall : false,
13698             execCall : false,
13699             dom : false,
13700             isTop : istop
13701             
13702             
13703         };
13704         
13705         
13706         switch(true) {
13707             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13708             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13709             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13710             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13711             // no default..
13712         }
13713         
13714         
13715         if (!tpl.attr) {
13716             // just itterate children..
13717             this.iterChild(node,this.compileNode);
13718             return;
13719         }
13720         tpl.uid = this.id++;
13721         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13722         node.removeAttribute('roo-'+ tpl.attr);
13723         if (tpl.attr != 'name') {
13724             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13725             node.parentNode.replaceChild(placeholder,  node);
13726         } else {
13727             
13728             var placeholder =  document.createElement('span');
13729             placeholder.className = 'roo-tpl-' + tpl.value;
13730             node.parentNode.replaceChild(placeholder,  node);
13731         }
13732         
13733         // parent now sees '{domtplXXXX}
13734         this.iterChild(node,this.compileNode);
13735         
13736         // we should now have node body...
13737         var div = document.createElement('div');
13738         div.appendChild(node);
13739         tpl.dom = node;
13740         // this has the unfortunate side effect of converting tagged attributes
13741         // eg. href="{...}" into %7C...%7D
13742         // this has been fixed by searching for those combo's although it's a bit hacky..
13743         
13744         
13745         tpl.body = div.innerHTML;
13746         
13747         
13748          
13749         tpl.id = tpl.uid;
13750         switch(tpl.attr) {
13751             case 'for' :
13752                 switch (tpl.value) {
13753                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13754                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13755                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13756                 }
13757                 break;
13758             
13759             case 'exec':
13760                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13761                 break;
13762             
13763             case 'if':     
13764                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13765                 break;
13766             
13767             case 'name':
13768                 tpl.id  = tpl.value; // replace non characters???
13769                 break;
13770             
13771         }
13772         
13773         
13774         this.tpls.push(tpl);
13775         
13776         
13777         
13778     },
13779     
13780     
13781     
13782     
13783     /**
13784      * Compile a segment of the template into a 'sub-template'
13785      *
13786      * 
13787      * 
13788      *
13789      */
13790     compileTpl : function(tpl)
13791     {
13792         var fm = Roo.util.Format;
13793         var useF = this.disableFormats !== true;
13794         
13795         var sep = Roo.isGecko ? "+\n" : ",\n";
13796         
13797         var undef = function(str) {
13798             Roo.debug && Roo.log("Property not found :"  + str);
13799             return '';
13800         };
13801           
13802         //Roo.log(tpl.body);
13803         
13804         
13805         
13806         var fn = function(m, lbrace, name, format, args)
13807         {
13808             //Roo.log("ARGS");
13809             //Roo.log(arguments);
13810             args = args ? args.replace(/\\'/g,"'") : args;
13811             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13812             if (typeof(format) == 'undefined') {
13813                 format =  'htmlEncode'; 
13814             }
13815             if (format == 'raw' ) {
13816                 format = false;
13817             }
13818             
13819             if(name.substr(0, 6) == 'domtpl'){
13820                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13821             }
13822             
13823             // build an array of options to determine if value is undefined..
13824             
13825             // basically get 'xxxx.yyyy' then do
13826             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13827             //    (function () { Roo.log("Property not found"); return ''; })() :
13828             //    ......
13829             
13830             var udef_ar = [];
13831             var lookfor = '';
13832             Roo.each(name.split('.'), function(st) {
13833                 lookfor += (lookfor.length ? '.': '') + st;
13834                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13835             });
13836             
13837             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13838             
13839             
13840             if(format && useF){
13841                 
13842                 args = args ? ',' + args : "";
13843                  
13844                 if(format.substr(0, 5) != "this."){
13845                     format = "fm." + format + '(';
13846                 }else{
13847                     format = 'this.call("'+ format.substr(5) + '", ';
13848                     args = ", values";
13849                 }
13850                 
13851                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13852             }
13853              
13854             if (args && args.length) {
13855                 // called with xxyx.yuu:(test,test)
13856                 // change to ()
13857                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13858             }
13859             // raw.. - :raw modifier..
13860             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13861             
13862         };
13863         var body;
13864         // branched to use + in gecko and [].join() in others
13865         if(Roo.isGecko){
13866             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13867                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13868                     "';};};";
13869         }else{
13870             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13871             body.push(tpl.body.replace(/(\r\n|\n)/g,
13872                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13873             body.push("'].join('');};};");
13874             body = body.join('');
13875         }
13876         
13877         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13878        
13879         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13880         eval(body);
13881         
13882         return this;
13883     },
13884      
13885     /**
13886      * same as applyTemplate, except it's done to one of the subTemplates
13887      * when using named templates, you can do:
13888      *
13889      * var str = pl.applySubTemplate('your-name', values);
13890      *
13891      * 
13892      * @param {Number} id of the template
13893      * @param {Object} values to apply to template
13894      * @param {Object} parent (normaly the instance of this object)
13895      */
13896     applySubTemplate : function(id, values, parent)
13897     {
13898         
13899         
13900         var t = this.tpls[id];
13901         
13902         
13903         try { 
13904             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13905                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13906                 return '';
13907             }
13908         } catch(e) {
13909             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13910             Roo.log(values);
13911           
13912             return '';
13913         }
13914         try { 
13915             
13916             if(t.execCall && t.execCall.call(this, values, parent)){
13917                 return '';
13918             }
13919         } catch(e) {
13920             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13921             Roo.log(values);
13922             return '';
13923         }
13924         
13925         try {
13926             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13927             parent = t.target ? values : parent;
13928             if(t.forCall && vs instanceof Array){
13929                 var buf = [];
13930                 for(var i = 0, len = vs.length; i < len; i++){
13931                     try {
13932                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13933                     } catch (e) {
13934                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13935                         Roo.log(e.body);
13936                         //Roo.log(t.compiled);
13937                         Roo.log(vs[i]);
13938                     }   
13939                 }
13940                 return buf.join('');
13941             }
13942         } catch (e) {
13943             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13944             Roo.log(values);
13945             return '';
13946         }
13947         try {
13948             return t.compiled.call(this, vs, parent);
13949         } catch (e) {
13950             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13951             Roo.log(e.body);
13952             //Roo.log(t.compiled);
13953             Roo.log(values);
13954             return '';
13955         }
13956     },
13957
13958    
13959
13960     applyTemplate : function(values){
13961         return this.master.compiled.call(this, values, {});
13962         //var s = this.subs;
13963     },
13964
13965     apply : function(){
13966         return this.applyTemplate.apply(this, arguments);
13967     }
13968
13969  });
13970
13971 Roo.DomTemplate.from = function(el){
13972     el = Roo.getDom(el);
13973     return new Roo.Domtemplate(el.value || el.innerHTML);
13974 };/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984
13985 /**
13986  * @class Roo.util.DelayedTask
13987  * Provides a convenient method of performing setTimeout where a new
13988  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13989  * You can use this class to buffer
13990  * the keypress events for a certain number of milliseconds, and perform only if they stop
13991  * for that amount of time.
13992  * @constructor The parameters to this constructor serve as defaults and are not required.
13993  * @param {Function} fn (optional) The default function to timeout
13994  * @param {Object} scope (optional) The default scope of that timeout
13995  * @param {Array} args (optional) The default Array of arguments
13996  */
13997 Roo.util.DelayedTask = function(fn, scope, args){
13998     var id = null, d, t;
13999
14000     var call = function(){
14001         var now = new Date().getTime();
14002         if(now - t >= d){
14003             clearInterval(id);
14004             id = null;
14005             fn.apply(scope, args || []);
14006         }
14007     };
14008     /**
14009      * Cancels any pending timeout and queues a new one
14010      * @param {Number} delay The milliseconds to delay
14011      * @param {Function} newFn (optional) Overrides function passed to constructor
14012      * @param {Object} newScope (optional) Overrides scope passed to constructor
14013      * @param {Array} newArgs (optional) Overrides args passed to constructor
14014      */
14015     this.delay = function(delay, newFn, newScope, newArgs){
14016         if(id && delay != d){
14017             this.cancel();
14018         }
14019         d = delay;
14020         t = new Date().getTime();
14021         fn = newFn || fn;
14022         scope = newScope || scope;
14023         args = newArgs || args;
14024         if(!id){
14025             id = setInterval(call, d);
14026         }
14027     };
14028
14029     /**
14030      * Cancel the last queued timeout
14031      */
14032     this.cancel = function(){
14033         if(id){
14034             clearInterval(id);
14035             id = null;
14036         }
14037     };
14038 };/*
14039  * Based on:
14040  * Ext JS Library 1.1.1
14041  * Copyright(c) 2006-2007, Ext JS, LLC.
14042  *
14043  * Originally Released Under LGPL - original licence link has changed is not relivant.
14044  *
14045  * Fork - LGPL
14046  * <script type="text/javascript">
14047  */
14048 /**
14049  * @class Roo.util.TaskRunner
14050  * Manage background tasks - not sure why this is better that setInterval?
14051  * @static
14052  *
14053  */
14054  
14055 Roo.util.TaskRunner = function(interval){
14056     interval = interval || 10;
14057     var tasks = [], removeQueue = [];
14058     var id = 0;
14059     var running = false;
14060
14061     var stopThread = function(){
14062         running = false;
14063         clearInterval(id);
14064         id = 0;
14065     };
14066
14067     var startThread = function(){
14068         if(!running){
14069             running = true;
14070             id = setInterval(runTasks, interval);
14071         }
14072     };
14073
14074     var removeTask = function(task){
14075         removeQueue.push(task);
14076         if(task.onStop){
14077             task.onStop();
14078         }
14079     };
14080
14081     var runTasks = function(){
14082         if(removeQueue.length > 0){
14083             for(var i = 0, len = removeQueue.length; i < len; i++){
14084                 tasks.remove(removeQueue[i]);
14085             }
14086             removeQueue = [];
14087             if(tasks.length < 1){
14088                 stopThread();
14089                 return;
14090             }
14091         }
14092         var now = new Date().getTime();
14093         for(var i = 0, len = tasks.length; i < len; ++i){
14094             var t = tasks[i];
14095             var itime = now - t.taskRunTime;
14096             if(t.interval <= itime){
14097                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14098                 t.taskRunTime = now;
14099                 if(rt === false || t.taskRunCount === t.repeat){
14100                     removeTask(t);
14101                     return;
14102                 }
14103             }
14104             if(t.duration && t.duration <= (now - t.taskStartTime)){
14105                 removeTask(t);
14106             }
14107         }
14108     };
14109
14110     /**
14111      * Queues a new task.
14112      * @param {Object} task
14113      *
14114      * Task property : interval = how frequent to run.
14115      * Task object should implement
14116      * function run()
14117      * Task object may implement
14118      * function onStop()
14119      */
14120     this.start = function(task){
14121         tasks.push(task);
14122         task.taskStartTime = new Date().getTime();
14123         task.taskRunTime = 0;
14124         task.taskRunCount = 0;
14125         startThread();
14126         return task;
14127     };
14128     /**
14129      * Stop  new task.
14130      * @param {Object} task
14131      */
14132     this.stop = function(task){
14133         removeTask(task);
14134         return task;
14135     };
14136     /**
14137      * Stop all Tasks
14138      */
14139     this.stopAll = function(){
14140         stopThread();
14141         for(var i = 0, len = tasks.length; i < len; i++){
14142             if(tasks[i].onStop){
14143                 tasks[i].onStop();
14144             }
14145         }
14146         tasks = [];
14147         removeQueue = [];
14148     };
14149 };
14150
14151 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14152  * Based on:
14153  * Ext JS Library 1.1.1
14154  * Copyright(c) 2006-2007, Ext JS, LLC.
14155  *
14156  * Originally Released Under LGPL - original licence link has changed is not relivant.
14157  *
14158  * Fork - LGPL
14159  * <script type="text/javascript">
14160  */
14161
14162  
14163 /**
14164  * @class Roo.util.MixedCollection
14165  * @extends Roo.util.Observable
14166  * A Collection class that maintains both numeric indexes and keys and exposes events.
14167  * @constructor
14168  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14169  * collection (defaults to false)
14170  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14171  * and return the key value for that item.  This is used when available to look up the key on items that
14172  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14173  * equivalent to providing an implementation for the {@link #getKey} method.
14174  */
14175 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14176     this.items = [];
14177     this.map = {};
14178     this.keys = [];
14179     this.length = 0;
14180     this.addEvents({
14181         /**
14182          * @event clear
14183          * Fires when the collection is cleared.
14184          */
14185         "clear" : true,
14186         /**
14187          * @event add
14188          * Fires when an item is added to the collection.
14189          * @param {Number} index The index at which the item was added.
14190          * @param {Object} o The item added.
14191          * @param {String} key The key associated with the added item.
14192          */
14193         "add" : true,
14194         /**
14195          * @event replace
14196          * Fires when an item is replaced in the collection.
14197          * @param {String} key he key associated with the new added.
14198          * @param {Object} old The item being replaced.
14199          * @param {Object} new The new item.
14200          */
14201         "replace" : true,
14202         /**
14203          * @event remove
14204          * Fires when an item is removed from the collection.
14205          * @param {Object} o The item being removed.
14206          * @param {String} key (optional) The key associated with the removed item.
14207          */
14208         "remove" : true,
14209         "sort" : true
14210     });
14211     this.allowFunctions = allowFunctions === true;
14212     if(keyFn){
14213         this.getKey = keyFn;
14214     }
14215     Roo.util.MixedCollection.superclass.constructor.call(this);
14216 };
14217
14218 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14219     allowFunctions : false,
14220     
14221 /**
14222  * Adds an item to the collection.
14223  * @param {String} key The key to associate with the item
14224  * @param {Object} o The item to add.
14225  * @return {Object} The item added.
14226  */
14227     add : function(key, o){
14228         if(arguments.length == 1){
14229             o = arguments[0];
14230             key = this.getKey(o);
14231         }
14232         if(typeof key == "undefined" || key === null){
14233             this.length++;
14234             this.items.push(o);
14235             this.keys.push(null);
14236         }else{
14237             var old = this.map[key];
14238             if(old){
14239                 return this.replace(key, o);
14240             }
14241             this.length++;
14242             this.items.push(o);
14243             this.map[key] = o;
14244             this.keys.push(key);
14245         }
14246         this.fireEvent("add", this.length-1, o, key);
14247         return o;
14248     },
14249        
14250 /**
14251   * MixedCollection has a generic way to fetch keys if you implement getKey.
14252 <pre><code>
14253 // normal way
14254 var mc = new Roo.util.MixedCollection();
14255 mc.add(someEl.dom.id, someEl);
14256 mc.add(otherEl.dom.id, otherEl);
14257 //and so on
14258
14259 // using getKey
14260 var mc = new Roo.util.MixedCollection();
14261 mc.getKey = function(el){
14262    return el.dom.id;
14263 };
14264 mc.add(someEl);
14265 mc.add(otherEl);
14266
14267 // or via the constructor
14268 var mc = new Roo.util.MixedCollection(false, function(el){
14269    return el.dom.id;
14270 });
14271 mc.add(someEl);
14272 mc.add(otherEl);
14273 </code></pre>
14274  * @param o {Object} The item for which to find the key.
14275  * @return {Object} The key for the passed item.
14276  */
14277     getKey : function(o){
14278          return o.id; 
14279     },
14280    
14281 /**
14282  * Replaces an item in the collection.
14283  * @param {String} key The key associated with the item to replace, or the item to replace.
14284  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14285  * @return {Object}  The new item.
14286  */
14287     replace : function(key, o){
14288         if(arguments.length == 1){
14289             o = arguments[0];
14290             key = this.getKey(o);
14291         }
14292         var old = this.item(key);
14293         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14294              return this.add(key, o);
14295         }
14296         var index = this.indexOfKey(key);
14297         this.items[index] = o;
14298         this.map[key] = o;
14299         this.fireEvent("replace", key, old, o);
14300         return o;
14301     },
14302    
14303 /**
14304  * Adds all elements of an Array or an Object to the collection.
14305  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14306  * an Array of values, each of which are added to the collection.
14307  */
14308     addAll : function(objs){
14309         if(arguments.length > 1 || objs instanceof Array){
14310             var args = arguments.length > 1 ? arguments : objs;
14311             for(var i = 0, len = args.length; i < len; i++){
14312                 this.add(args[i]);
14313             }
14314         }else{
14315             for(var key in objs){
14316                 if(this.allowFunctions || typeof objs[key] != "function"){
14317                     this.add(key, objs[key]);
14318                 }
14319             }
14320         }
14321     },
14322    
14323 /**
14324  * Executes the specified function once for every item in the collection, passing each
14325  * item as the first and only parameter. returning false from the function will stop the iteration.
14326  * @param {Function} fn The function to execute for each item.
14327  * @param {Object} scope (optional) The scope in which to execute the function.
14328  */
14329     each : function(fn, scope){
14330         var items = [].concat(this.items); // each safe for removal
14331         for(var i = 0, len = items.length; i < len; i++){
14332             if(fn.call(scope || items[i], items[i], i, len) === false){
14333                 break;
14334             }
14335         }
14336     },
14337    
14338 /**
14339  * Executes the specified function once for every key in the collection, passing each
14340  * key, and its associated item as the first two parameters.
14341  * @param {Function} fn The function to execute for each item.
14342  * @param {Object} scope (optional) The scope in which to execute the function.
14343  */
14344     eachKey : function(fn, scope){
14345         for(var i = 0, len = this.keys.length; i < len; i++){
14346             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14347         }
14348     },
14349    
14350 /**
14351  * Returns the first item in the collection which elicits a true return value from the
14352  * passed selection function.
14353  * @param {Function} fn The selection function to execute for each item.
14354  * @param {Object} scope (optional) The scope in which to execute the function.
14355  * @return {Object} The first item in the collection which returned true from the selection function.
14356  */
14357     find : function(fn, scope){
14358         for(var i = 0, len = this.items.length; i < len; i++){
14359             if(fn.call(scope || window, this.items[i], this.keys[i])){
14360                 return this.items[i];
14361             }
14362         }
14363         return null;
14364     },
14365    
14366 /**
14367  * Inserts an item at the specified index in the collection.
14368  * @param {Number} index The index to insert the item at.
14369  * @param {String} key The key to associate with the new item, or the item itself.
14370  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14371  * @return {Object} The item inserted.
14372  */
14373     insert : function(index, key, o){
14374         if(arguments.length == 2){
14375             o = arguments[1];
14376             key = this.getKey(o);
14377         }
14378         if(index >= this.length){
14379             return this.add(key, o);
14380         }
14381         this.length++;
14382         this.items.splice(index, 0, o);
14383         if(typeof key != "undefined" && key != null){
14384             this.map[key] = o;
14385         }
14386         this.keys.splice(index, 0, key);
14387         this.fireEvent("add", index, o, key);
14388         return o;
14389     },
14390    
14391 /**
14392  * Removed an item from the collection.
14393  * @param {Object} o The item to remove.
14394  * @return {Object} The item removed.
14395  */
14396     remove : function(o){
14397         return this.removeAt(this.indexOf(o));
14398     },
14399    
14400 /**
14401  * Remove an item from a specified index in the collection.
14402  * @param {Number} index The index within the collection of the item to remove.
14403  */
14404     removeAt : function(index){
14405         if(index < this.length && index >= 0){
14406             this.length--;
14407             var o = this.items[index];
14408             this.items.splice(index, 1);
14409             var key = this.keys[index];
14410             if(typeof key != "undefined"){
14411                 delete this.map[key];
14412             }
14413             this.keys.splice(index, 1);
14414             this.fireEvent("remove", o, key);
14415         }
14416     },
14417    
14418 /**
14419  * Removed an item associated with the passed key fom the collection.
14420  * @param {String} key The key of the item to remove.
14421  */
14422     removeKey : function(key){
14423         return this.removeAt(this.indexOfKey(key));
14424     },
14425    
14426 /**
14427  * Returns the number of items in the collection.
14428  * @return {Number} the number of items in the collection.
14429  */
14430     getCount : function(){
14431         return this.length; 
14432     },
14433    
14434 /**
14435  * Returns index within the collection of the passed Object.
14436  * @param {Object} o The item to find the index of.
14437  * @return {Number} index of the item.
14438  */
14439     indexOf : function(o){
14440         if(!this.items.indexOf){
14441             for(var i = 0, len = this.items.length; i < len; i++){
14442                 if(this.items[i] == o) {
14443                     return i;
14444                 }
14445             }
14446             return -1;
14447         }else{
14448             return this.items.indexOf(o);
14449         }
14450     },
14451    
14452 /**
14453  * Returns index within the collection of the passed key.
14454  * @param {String} key The key to find the index of.
14455  * @return {Number} index of the key.
14456  */
14457     indexOfKey : function(key){
14458         if(!this.keys.indexOf){
14459             for(var i = 0, len = this.keys.length; i < len; i++){
14460                 if(this.keys[i] == key) {
14461                     return i;
14462                 }
14463             }
14464             return -1;
14465         }else{
14466             return this.keys.indexOf(key);
14467         }
14468     },
14469    
14470 /**
14471  * Returns the item associated with the passed key OR index. Key has priority over index.
14472  * @param {String/Number} key The key or index of the item.
14473  * @return {Object} The item associated with the passed key.
14474  */
14475     item : function(key){
14476         if (key === 'length') {
14477             return null;
14478         }
14479         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14480         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14481     },
14482     
14483 /**
14484  * Returns the item at the specified index.
14485  * @param {Number} index The index of the item.
14486  * @return {Object}
14487  */
14488     itemAt : function(index){
14489         return this.items[index];
14490     },
14491     
14492 /**
14493  * Returns the item associated with the passed key.
14494  * @param {String/Number} key The key of the item.
14495  * @return {Object} The item associated with the passed key.
14496  */
14497     key : function(key){
14498         return this.map[key];
14499     },
14500    
14501 /**
14502  * Returns true if the collection contains the passed Object as an item.
14503  * @param {Object} o  The Object to look for in the collection.
14504  * @return {Boolean} True if the collection contains the Object as an item.
14505  */
14506     contains : function(o){
14507         return this.indexOf(o) != -1;
14508     },
14509    
14510 /**
14511  * Returns true if the collection contains the passed Object as a key.
14512  * @param {String} key The key to look for in the collection.
14513  * @return {Boolean} True if the collection contains the Object as a key.
14514  */
14515     containsKey : function(key){
14516         return typeof this.map[key] != "undefined";
14517     },
14518    
14519 /**
14520  * Removes all items from the collection.
14521  */
14522     clear : function(){
14523         this.length = 0;
14524         this.items = [];
14525         this.keys = [];
14526         this.map = {};
14527         this.fireEvent("clear");
14528     },
14529    
14530 /**
14531  * Returns the first item in the collection.
14532  * @return {Object} the first item in the collection..
14533  */
14534     first : function(){
14535         return this.items[0]; 
14536     },
14537    
14538 /**
14539  * Returns the last item in the collection.
14540  * @return {Object} the last item in the collection..
14541  */
14542     last : function(){
14543         return this.items[this.length-1];   
14544     },
14545     
14546     _sort : function(property, dir, fn){
14547         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14548         fn = fn || function(a, b){
14549             return a-b;
14550         };
14551         var c = [], k = this.keys, items = this.items;
14552         for(var i = 0, len = items.length; i < len; i++){
14553             c[c.length] = {key: k[i], value: items[i], index: i};
14554         }
14555         c.sort(function(a, b){
14556             var v = fn(a[property], b[property]) * dsc;
14557             if(v == 0){
14558                 v = (a.index < b.index ? -1 : 1);
14559             }
14560             return v;
14561         });
14562         for(var i = 0, len = c.length; i < len; i++){
14563             items[i] = c[i].value;
14564             k[i] = c[i].key;
14565         }
14566         this.fireEvent("sort", this);
14567     },
14568     
14569     /**
14570      * Sorts this collection with the passed comparison function
14571      * @param {String} direction (optional) "ASC" or "DESC"
14572      * @param {Function} fn (optional) comparison function
14573      */
14574     sort : function(dir, fn){
14575         this._sort("value", dir, fn);
14576     },
14577     
14578     /**
14579      * Sorts this collection by keys
14580      * @param {String} direction (optional) "ASC" or "DESC"
14581      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14582      */
14583     keySort : function(dir, fn){
14584         this._sort("key", dir, fn || function(a, b){
14585             return String(a).toUpperCase()-String(b).toUpperCase();
14586         });
14587     },
14588     
14589     /**
14590      * Returns a range of items in this collection
14591      * @param {Number} startIndex (optional) defaults to 0
14592      * @param {Number} endIndex (optional) default to the last item
14593      * @return {Array} An array of items
14594      */
14595     getRange : function(start, end){
14596         var items = this.items;
14597         if(items.length < 1){
14598             return [];
14599         }
14600         start = start || 0;
14601         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14602         var r = [];
14603         if(start <= end){
14604             for(var i = start; i <= end; i++) {
14605                     r[r.length] = items[i];
14606             }
14607         }else{
14608             for(var i = start; i >= end; i--) {
14609                     r[r.length] = items[i];
14610             }
14611         }
14612         return r;
14613     },
14614         
14615     /**
14616      * Filter the <i>objects</i> in this collection by a specific property. 
14617      * Returns a new collection that has been filtered.
14618      * @param {String} property A property on your objects
14619      * @param {String/RegExp} value Either string that the property values 
14620      * should start with or a RegExp to test against the property
14621      * @return {MixedCollection} The new filtered collection
14622      */
14623     filter : function(property, value){
14624         if(!value.exec){ // not a regex
14625             value = String(value);
14626             if(value.length == 0){
14627                 return this.clone();
14628             }
14629             value = new RegExp("^" + Roo.escapeRe(value), "i");
14630         }
14631         return this.filterBy(function(o){
14632             return o && value.test(o[property]);
14633         });
14634         },
14635     
14636     /**
14637      * Filter by a function. * Returns a new collection that has been filtered.
14638      * The passed function will be called with each 
14639      * object in the collection. If the function returns true, the value is included 
14640      * otherwise it is filtered.
14641      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14642      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14643      * @return {MixedCollection} The new filtered collection
14644      */
14645     filterBy : function(fn, scope){
14646         var r = new Roo.util.MixedCollection();
14647         r.getKey = this.getKey;
14648         var k = this.keys, it = this.items;
14649         for(var i = 0, len = it.length; i < len; i++){
14650             if(fn.call(scope||this, it[i], k[i])){
14651                                 r.add(k[i], it[i]);
14652                         }
14653         }
14654         return r;
14655     },
14656     
14657     /**
14658      * Creates a duplicate of this collection
14659      * @return {MixedCollection}
14660      */
14661     clone : function(){
14662         var r = new Roo.util.MixedCollection();
14663         var k = this.keys, it = this.items;
14664         for(var i = 0, len = it.length; i < len; i++){
14665             r.add(k[i], it[i]);
14666         }
14667         r.getKey = this.getKey;
14668         return r;
14669     }
14670 });
14671 /**
14672  * Returns the item associated with the passed key or index.
14673  * @method
14674  * @param {String/Number} key The key or index of the item.
14675  * @return {Object} The item associated with the passed key.
14676  */
14677 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14678  * Based on:
14679  * Ext JS Library 1.1.1
14680  * Copyright(c) 2006-2007, Ext JS, LLC.
14681  *
14682  * Originally Released Under LGPL - original licence link has changed is not relivant.
14683  *
14684  * Fork - LGPL
14685  * <script type="text/javascript">
14686  */
14687 /**
14688  * @class Roo.util.JSON
14689  * Modified version of Douglas Crockford"s json.js that doesn"t
14690  * mess with the Object prototype 
14691  * http://www.json.org/js.html
14692  * @static
14693  */
14694 Roo.util.JSON = new (function(){
14695     var useHasOwn = {}.hasOwnProperty ? true : false;
14696     
14697     // crashes Safari in some instances
14698     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14699     
14700     var pad = function(n) {
14701         return n < 10 ? "0" + n : n;
14702     };
14703     
14704     var m = {
14705         "\b": '\\b',
14706         "\t": '\\t',
14707         "\n": '\\n',
14708         "\f": '\\f',
14709         "\r": '\\r',
14710         '"' : '\\"',
14711         "\\": '\\\\'
14712     };
14713
14714     var encodeString = function(s){
14715         if (/["\\\x00-\x1f]/.test(s)) {
14716             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14717                 var c = m[b];
14718                 if(c){
14719                     return c;
14720                 }
14721                 c = b.charCodeAt();
14722                 return "\\u00" +
14723                     Math.floor(c / 16).toString(16) +
14724                     (c % 16).toString(16);
14725             }) + '"';
14726         }
14727         return '"' + s + '"';
14728     };
14729     
14730     var encodeArray = function(o){
14731         var a = ["["], b, i, l = o.length, v;
14732             for (i = 0; i < l; i += 1) {
14733                 v = o[i];
14734                 switch (typeof v) {
14735                     case "undefined":
14736                     case "function":
14737                     case "unknown":
14738                         break;
14739                     default:
14740                         if (b) {
14741                             a.push(',');
14742                         }
14743                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14744                         b = true;
14745                 }
14746             }
14747             a.push("]");
14748             return a.join("");
14749     };
14750     
14751     var encodeDate = function(o){
14752         return '"' + o.getFullYear() + "-" +
14753                 pad(o.getMonth() + 1) + "-" +
14754                 pad(o.getDate()) + "T" +
14755                 pad(o.getHours()) + ":" +
14756                 pad(o.getMinutes()) + ":" +
14757                 pad(o.getSeconds()) + '"';
14758     };
14759     
14760     /**
14761      * Encodes an Object, Array or other value
14762      * @param {Mixed} o The variable to encode
14763      * @return {String} The JSON string
14764      */
14765     this.encode = function(o)
14766     {
14767         // should this be extended to fully wrap stringify..
14768         
14769         if(typeof o == "undefined" || o === null){
14770             return "null";
14771         }else if(o instanceof Array){
14772             return encodeArray(o);
14773         }else if(o instanceof Date){
14774             return encodeDate(o);
14775         }else if(typeof o == "string"){
14776             return encodeString(o);
14777         }else if(typeof o == "number"){
14778             return isFinite(o) ? String(o) : "null";
14779         }else if(typeof o == "boolean"){
14780             return String(o);
14781         }else {
14782             var a = ["{"], b, i, v;
14783             for (i in o) {
14784                 if(!useHasOwn || o.hasOwnProperty(i)) {
14785                     v = o[i];
14786                     switch (typeof v) {
14787                     case "undefined":
14788                     case "function":
14789                     case "unknown":
14790                         break;
14791                     default:
14792                         if(b){
14793                             a.push(',');
14794                         }
14795                         a.push(this.encode(i), ":",
14796                                 v === null ? "null" : this.encode(v));
14797                         b = true;
14798                     }
14799                 }
14800             }
14801             a.push("}");
14802             return a.join("");
14803         }
14804     };
14805     
14806     /**
14807      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14808      * @param {String} json The JSON string
14809      * @return {Object} The resulting object
14810      */
14811     this.decode = function(json){
14812         
14813         return  /** eval:var:json */ eval("(" + json + ')');
14814     };
14815 })();
14816 /** 
14817  * Shorthand for {@link Roo.util.JSON#encode}
14818  * @member Roo encode 
14819  * @method */
14820 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14821 /** 
14822  * Shorthand for {@link Roo.util.JSON#decode}
14823  * @member Roo decode 
14824  * @method */
14825 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14826 /*
14827  * Based on:
14828  * Ext JS Library 1.1.1
14829  * Copyright(c) 2006-2007, Ext JS, LLC.
14830  *
14831  * Originally Released Under LGPL - original licence link has changed is not relivant.
14832  *
14833  * Fork - LGPL
14834  * <script type="text/javascript">
14835  */
14836  
14837 /**
14838  * @class Roo.util.Format
14839  * Reusable data formatting functions
14840  * @static
14841  */
14842 Roo.util.Format = function(){
14843     var trimRe = /^\s+|\s+$/g;
14844     return {
14845         /**
14846          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14847          * @param {String} value The string to truncate
14848          * @param {Number} length The maximum length to allow before truncating
14849          * @return {String} The converted text
14850          */
14851         ellipsis : function(value, len){
14852             if(value && value.length > len){
14853                 return value.substr(0, len-3)+"...";
14854             }
14855             return value;
14856         },
14857
14858         /**
14859          * Checks a reference and converts it to empty string if it is undefined
14860          * @param {Mixed} value Reference to check
14861          * @return {Mixed} Empty string if converted, otherwise the original value
14862          */
14863         undef : function(value){
14864             return typeof value != "undefined" ? value : "";
14865         },
14866
14867         /**
14868          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14869          * @param {String} value The string to encode
14870          * @return {String} The encoded text
14871          */
14872         htmlEncode : function(value){
14873             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14874         },
14875
14876         /**
14877          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14878          * @param {String} value The string to decode
14879          * @return {String} The decoded text
14880          */
14881         htmlDecode : function(value){
14882             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14883         },
14884
14885         /**
14886          * Trims any whitespace from either side of a string
14887          * @param {String} value The text to trim
14888          * @return {String} The trimmed text
14889          */
14890         trim : function(value){
14891             return String(value).replace(trimRe, "");
14892         },
14893
14894         /**
14895          * Returns a substring from within an original string
14896          * @param {String} value The original text
14897          * @param {Number} start The start index of the substring
14898          * @param {Number} length The length of the substring
14899          * @return {String} The substring
14900          */
14901         substr : function(value, start, length){
14902             return String(value).substr(start, length);
14903         },
14904
14905         /**
14906          * Converts a string to all lower case letters
14907          * @param {String} value The text to convert
14908          * @return {String} The converted text
14909          */
14910         lowercase : function(value){
14911             return String(value).toLowerCase();
14912         },
14913
14914         /**
14915          * Converts a string to all upper case letters
14916          * @param {String} value The text to convert
14917          * @return {String} The converted text
14918          */
14919         uppercase : function(value){
14920             return String(value).toUpperCase();
14921         },
14922
14923         /**
14924          * Converts the first character only of a string to upper case
14925          * @param {String} value The text to convert
14926          * @return {String} The converted text
14927          */
14928         capitalize : function(value){
14929             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14930         },
14931
14932         // private
14933         call : function(value, fn){
14934             if(arguments.length > 2){
14935                 var args = Array.prototype.slice.call(arguments, 2);
14936                 args.unshift(value);
14937                  
14938                 return /** eval:var:value */  eval(fn).apply(window, args);
14939             }else{
14940                 /** eval:var:value */
14941                 return /** eval:var:value */ eval(fn).call(window, value);
14942             }
14943         },
14944
14945        
14946         /**
14947          * safer version of Math.toFixed..??/
14948          * @param {Number/String} value The numeric value to format
14949          * @param {Number/String} value Decimal places 
14950          * @return {String} The formatted currency string
14951          */
14952         toFixed : function(v, n)
14953         {
14954             // why not use to fixed - precision is buggered???
14955             if (!n) {
14956                 return Math.round(v-0);
14957             }
14958             var fact = Math.pow(10,n+1);
14959             v = (Math.round((v-0)*fact))/fact;
14960             var z = (''+fact).substring(2);
14961             if (v == Math.floor(v)) {
14962                 return Math.floor(v) + '.' + z;
14963             }
14964             
14965             // now just padd decimals..
14966             var ps = String(v).split('.');
14967             var fd = (ps[1] + z);
14968             var r = fd.substring(0,n); 
14969             var rm = fd.substring(n); 
14970             if (rm < 5) {
14971                 return ps[0] + '.' + r;
14972             }
14973             r*=1; // turn it into a number;
14974             r++;
14975             if (String(r).length != n) {
14976                 ps[0]*=1;
14977                 ps[0]++;
14978                 r = String(r).substring(1); // chop the end off.
14979             }
14980             
14981             return ps[0] + '.' + r;
14982              
14983         },
14984         
14985         /**
14986          * Format a number as US currency
14987          * @param {Number/String} value The numeric value to format
14988          * @return {String} The formatted currency string
14989          */
14990         usMoney : function(v){
14991             return '$' + Roo.util.Format.number(v);
14992         },
14993         
14994         /**
14995          * Format a number
14996          * eventually this should probably emulate php's number_format
14997          * @param {Number/String} value The numeric value to format
14998          * @param {Number} decimals number of decimal places
14999          * @param {String} delimiter for thousands (default comma)
15000          * @return {String} The formatted currency string
15001          */
15002         number : function(v, decimals, thousandsDelimiter)
15003         {
15004             // multiply and round.
15005             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15006             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15007             
15008             var mul = Math.pow(10, decimals);
15009             var zero = String(mul).substring(1);
15010             v = (Math.round((v-0)*mul))/mul;
15011             
15012             // if it's '0' number.. then
15013             
15014             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15015             v = String(v);
15016             var ps = v.split('.');
15017             var whole = ps[0];
15018             
15019             var r = /(\d+)(\d{3})/;
15020             // add comma's
15021             
15022             if(thousandsDelimiter.length != 0) {
15023                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15024             } 
15025             
15026             var sub = ps[1] ?
15027                     // has decimals..
15028                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15029                     // does not have decimals
15030                     (decimals ? ('.' + zero) : '');
15031             
15032             
15033             return whole + sub ;
15034         },
15035         
15036         /**
15037          * Parse a value into a formatted date using the specified format pattern.
15038          * @param {Mixed} value The value to format
15039          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15040          * @return {String} The formatted date string
15041          */
15042         date : function(v, format){
15043             if(!v){
15044                 return "";
15045             }
15046             if(!(v instanceof Date)){
15047                 v = new Date(Date.parse(v));
15048             }
15049             return v.dateFormat(format || Roo.util.Format.defaults.date);
15050         },
15051
15052         /**
15053          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15054          * @param {String} format Any valid date format string
15055          * @return {Function} The date formatting function
15056          */
15057         dateRenderer : function(format){
15058             return function(v){
15059                 return Roo.util.Format.date(v, format);  
15060             };
15061         },
15062
15063         // private
15064         stripTagsRE : /<\/?[^>]+>/gi,
15065         
15066         /**
15067          * Strips all HTML tags
15068          * @param {Mixed} value The text from which to strip tags
15069          * @return {String} The stripped text
15070          */
15071         stripTags : function(v){
15072             return !v ? v : String(v).replace(this.stripTagsRE, "");
15073         },
15074         
15075         /**
15076          * Size in Mb,Gb etc.
15077          * @param {Number} value The number to be formated
15078          * @param {number} decimals how many decimal places
15079          * @return {String} the formated string
15080          */
15081         size : function(value, decimals)
15082         {
15083             var sizes = ['b', 'k', 'M', 'G', 'T'];
15084             if (value == 0) {
15085                 return 0;
15086             }
15087             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15088             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15089         }
15090         
15091         
15092         
15093     };
15094 }();
15095 Roo.util.Format.defaults = {
15096     date : 'd/M/Y'
15097 };/*
15098  * Based on:
15099  * Ext JS Library 1.1.1
15100  * Copyright(c) 2006-2007, Ext JS, LLC.
15101  *
15102  * Originally Released Under LGPL - original licence link has changed is not relivant.
15103  *
15104  * Fork - LGPL
15105  * <script type="text/javascript">
15106  */
15107
15108
15109  
15110
15111 /**
15112  * @class Roo.MasterTemplate
15113  * @extends Roo.Template
15114  * Provides a template that can have child templates. The syntax is:
15115 <pre><code>
15116 var t = new Roo.MasterTemplate(
15117         '&lt;select name="{name}"&gt;',
15118                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15119         '&lt;/select&gt;'
15120 );
15121 t.add('options', {value: 'foo', text: 'bar'});
15122 // or you can add multiple child elements in one shot
15123 t.addAll('options', [
15124     {value: 'foo', text: 'bar'},
15125     {value: 'foo2', text: 'bar2'},
15126     {value: 'foo3', text: 'bar3'}
15127 ]);
15128 // then append, applying the master template values
15129 t.append('my-form', {name: 'my-select'});
15130 </code></pre>
15131 * A name attribute for the child template is not required if you have only one child
15132 * template or you want to refer to them by index.
15133  */
15134 Roo.MasterTemplate = function(){
15135     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15136     this.originalHtml = this.html;
15137     var st = {};
15138     var m, re = this.subTemplateRe;
15139     re.lastIndex = 0;
15140     var subIndex = 0;
15141     while(m = re.exec(this.html)){
15142         var name = m[1], content = m[2];
15143         st[subIndex] = {
15144             name: name,
15145             index: subIndex,
15146             buffer: [],
15147             tpl : new Roo.Template(content)
15148         };
15149         if(name){
15150             st[name] = st[subIndex];
15151         }
15152         st[subIndex].tpl.compile();
15153         st[subIndex].tpl.call = this.call.createDelegate(this);
15154         subIndex++;
15155     }
15156     this.subCount = subIndex;
15157     this.subs = st;
15158 };
15159 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15160     /**
15161     * The regular expression used to match sub templates
15162     * @type RegExp
15163     * @property
15164     */
15165     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15166
15167     /**
15168      * Applies the passed values to a child template.
15169      * @param {String/Number} name (optional) The name or index of the child template
15170      * @param {Array/Object} values The values to be applied to the template
15171      * @return {MasterTemplate} this
15172      */
15173      add : function(name, values){
15174         if(arguments.length == 1){
15175             values = arguments[0];
15176             name = 0;
15177         }
15178         var s = this.subs[name];
15179         s.buffer[s.buffer.length] = s.tpl.apply(values);
15180         return this;
15181     },
15182
15183     /**
15184      * Applies all the passed values to a child template.
15185      * @param {String/Number} name (optional) The name or index of the child template
15186      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15187      * @param {Boolean} reset (optional) True to reset the template first
15188      * @return {MasterTemplate} this
15189      */
15190     fill : function(name, values, reset){
15191         var a = arguments;
15192         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15193             values = a[0];
15194             name = 0;
15195             reset = a[1];
15196         }
15197         if(reset){
15198             this.reset();
15199         }
15200         for(var i = 0, len = values.length; i < len; i++){
15201             this.add(name, values[i]);
15202         }
15203         return this;
15204     },
15205
15206     /**
15207      * Resets the template for reuse
15208      * @return {MasterTemplate} this
15209      */
15210      reset : function(){
15211         var s = this.subs;
15212         for(var i = 0; i < this.subCount; i++){
15213             s[i].buffer = [];
15214         }
15215         return this;
15216     },
15217
15218     applyTemplate : function(values){
15219         var s = this.subs;
15220         var replaceIndex = -1;
15221         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15222             return s[++replaceIndex].buffer.join("");
15223         });
15224         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15225     },
15226
15227     apply : function(){
15228         return this.applyTemplate.apply(this, arguments);
15229     },
15230
15231     compile : function(){return this;}
15232 });
15233
15234 /**
15235  * Alias for fill().
15236  * @method
15237  */
15238 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15239  /**
15240  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15241  * var tpl = Roo.MasterTemplate.from('element-id');
15242  * @param {String/HTMLElement} el
15243  * @param {Object} config
15244  * @static
15245  */
15246 Roo.MasterTemplate.from = function(el, config){
15247     el = Roo.getDom(el);
15248     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15249 };/*
15250  * Based on:
15251  * Ext JS Library 1.1.1
15252  * Copyright(c) 2006-2007, Ext JS, LLC.
15253  *
15254  * Originally Released Under LGPL - original licence link has changed is not relivant.
15255  *
15256  * Fork - LGPL
15257  * <script type="text/javascript">
15258  */
15259
15260  
15261 /**
15262  * @class Roo.util.CSS
15263  * Utility class for manipulating CSS rules
15264  * @static
15265
15266  */
15267 Roo.util.CSS = function(){
15268         var rules = null;
15269         var doc = document;
15270
15271     var camelRe = /(-[a-z])/gi;
15272     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15273
15274    return {
15275    /**
15276     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15277     * tag and appended to the HEAD of the document.
15278     * @param {String|Object} cssText The text containing the css rules
15279     * @param {String} id An id to add to the stylesheet for later removal
15280     * @return {StyleSheet}
15281     */
15282     createStyleSheet : function(cssText, id){
15283         var ss;
15284         var head = doc.getElementsByTagName("head")[0];
15285         var nrules = doc.createElement("style");
15286         nrules.setAttribute("type", "text/css");
15287         if(id){
15288             nrules.setAttribute("id", id);
15289         }
15290         if (typeof(cssText) != 'string') {
15291             // support object maps..
15292             // not sure if this a good idea.. 
15293             // perhaps it should be merged with the general css handling
15294             // and handle js style props.
15295             var cssTextNew = [];
15296             for(var n in cssText) {
15297                 var citems = [];
15298                 for(var k in cssText[n]) {
15299                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15300                 }
15301                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15302                 
15303             }
15304             cssText = cssTextNew.join("\n");
15305             
15306         }
15307        
15308        
15309        if(Roo.isIE){
15310            head.appendChild(nrules);
15311            ss = nrules.styleSheet;
15312            ss.cssText = cssText;
15313        }else{
15314            try{
15315                 nrules.appendChild(doc.createTextNode(cssText));
15316            }catch(e){
15317                nrules.cssText = cssText; 
15318            }
15319            head.appendChild(nrules);
15320            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15321        }
15322        this.cacheStyleSheet(ss);
15323        return ss;
15324    },
15325
15326    /**
15327     * Removes a style or link tag by id
15328     * @param {String} id The id of the tag
15329     */
15330    removeStyleSheet : function(id){
15331        var existing = doc.getElementById(id);
15332        if(existing){
15333            existing.parentNode.removeChild(existing);
15334        }
15335    },
15336
15337    /**
15338     * Dynamically swaps an existing stylesheet reference for a new one
15339     * @param {String} id The id of an existing link tag to remove
15340     * @param {String} url The href of the new stylesheet to include
15341     */
15342    swapStyleSheet : function(id, url){
15343        this.removeStyleSheet(id);
15344        var ss = doc.createElement("link");
15345        ss.setAttribute("rel", "stylesheet");
15346        ss.setAttribute("type", "text/css");
15347        ss.setAttribute("id", id);
15348        ss.setAttribute("href", url);
15349        doc.getElementsByTagName("head")[0].appendChild(ss);
15350    },
15351    
15352    /**
15353     * Refresh the rule cache if you have dynamically added stylesheets
15354     * @return {Object} An object (hash) of rules indexed by selector
15355     */
15356    refreshCache : function(){
15357        return this.getRules(true);
15358    },
15359
15360    // private
15361    cacheStyleSheet : function(stylesheet){
15362        if(!rules){
15363            rules = {};
15364        }
15365        try{// try catch for cross domain access issue
15366            var ssRules = stylesheet.cssRules || stylesheet.rules;
15367            for(var j = ssRules.length-1; j >= 0; --j){
15368                rules[ssRules[j].selectorText] = ssRules[j];
15369            }
15370        }catch(e){}
15371    },
15372    
15373    /**
15374     * Gets all css rules for the document
15375     * @param {Boolean} refreshCache true to refresh the internal cache
15376     * @return {Object} An object (hash) of rules indexed by selector
15377     */
15378    getRules : function(refreshCache){
15379                 if(rules == null || refreshCache){
15380                         rules = {};
15381                         var ds = doc.styleSheets;
15382                         for(var i =0, len = ds.length; i < len; i++){
15383                             try{
15384                         this.cacheStyleSheet(ds[i]);
15385                     }catch(e){} 
15386                 }
15387                 }
15388                 return rules;
15389         },
15390         
15391         /**
15392     * Gets an an individual CSS rule by selector(s)
15393     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15394     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15395     * @return {CSSRule} The CSS rule or null if one is not found
15396     */
15397    getRule : function(selector, refreshCache){
15398                 var rs = this.getRules(refreshCache);
15399                 if(!(selector instanceof Array)){
15400                     return rs[selector];
15401                 }
15402                 for(var i = 0; i < selector.length; i++){
15403                         if(rs[selector[i]]){
15404                                 return rs[selector[i]];
15405                         }
15406                 }
15407                 return null;
15408         },
15409         
15410         
15411         /**
15412     * Updates a rule property
15413     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15414     * @param {String} property The css property
15415     * @param {String} value The new value for the property
15416     * @return {Boolean} true If a rule was found and updated
15417     */
15418    updateRule : function(selector, property, value){
15419                 if(!(selector instanceof Array)){
15420                         var rule = this.getRule(selector);
15421                         if(rule){
15422                                 rule.style[property.replace(camelRe, camelFn)] = value;
15423                                 return true;
15424                         }
15425                 }else{
15426                         for(var i = 0; i < selector.length; i++){
15427                                 if(this.updateRule(selector[i], property, value)){
15428                                         return true;
15429                                 }
15430                         }
15431                 }
15432                 return false;
15433         }
15434    };   
15435 }();/*
15436  * Based on:
15437  * Ext JS Library 1.1.1
15438  * Copyright(c) 2006-2007, Ext JS, LLC.
15439  *
15440  * Originally Released Under LGPL - original licence link has changed is not relivant.
15441  *
15442  * Fork - LGPL
15443  * <script type="text/javascript">
15444  */
15445
15446  
15447
15448 /**
15449  * @class Roo.util.ClickRepeater
15450  * @extends Roo.util.Observable
15451  * 
15452  * A wrapper class which can be applied to any element. Fires a "click" event while the
15453  * mouse is pressed. The interval between firings may be specified in the config but
15454  * defaults to 10 milliseconds.
15455  * 
15456  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15457  * 
15458  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15459  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15460  * Similar to an autorepeat key delay.
15461  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15462  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15463  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15464  *           "interval" and "delay" are ignored. "immediate" is honored.
15465  * @cfg {Boolean} preventDefault True to prevent the default click event
15466  * @cfg {Boolean} stopDefault True to stop the default click event
15467  * 
15468  * @history
15469  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15470  *     2007-02-02 jvs Renamed to ClickRepeater
15471  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15472  *
15473  *  @constructor
15474  * @param {String/HTMLElement/Element} el The element to listen on
15475  * @param {Object} config
15476  **/
15477 Roo.util.ClickRepeater = function(el, config)
15478 {
15479     this.el = Roo.get(el);
15480     this.el.unselectable();
15481
15482     Roo.apply(this, config);
15483
15484     this.addEvents({
15485     /**
15486      * @event mousedown
15487      * Fires when the mouse button is depressed.
15488      * @param {Roo.util.ClickRepeater} this
15489      */
15490         "mousedown" : true,
15491     /**
15492      * @event click
15493      * Fires on a specified interval during the time the element is pressed.
15494      * @param {Roo.util.ClickRepeater} this
15495      */
15496         "click" : true,
15497     /**
15498      * @event mouseup
15499      * Fires when the mouse key is released.
15500      * @param {Roo.util.ClickRepeater} this
15501      */
15502         "mouseup" : true
15503     });
15504
15505     this.el.on("mousedown", this.handleMouseDown, this);
15506     if(this.preventDefault || this.stopDefault){
15507         this.el.on("click", function(e){
15508             if(this.preventDefault){
15509                 e.preventDefault();
15510             }
15511             if(this.stopDefault){
15512                 e.stopEvent();
15513             }
15514         }, this);
15515     }
15516
15517     // allow inline handler
15518     if(this.handler){
15519         this.on("click", this.handler,  this.scope || this);
15520     }
15521
15522     Roo.util.ClickRepeater.superclass.constructor.call(this);
15523 };
15524
15525 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15526     interval : 20,
15527     delay: 250,
15528     preventDefault : true,
15529     stopDefault : false,
15530     timer : 0,
15531
15532     // private
15533     handleMouseDown : function(){
15534         clearTimeout(this.timer);
15535         this.el.blur();
15536         if(this.pressClass){
15537             this.el.addClass(this.pressClass);
15538         }
15539         this.mousedownTime = new Date();
15540
15541         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15542         this.el.on("mouseout", this.handleMouseOut, this);
15543
15544         this.fireEvent("mousedown", this);
15545         this.fireEvent("click", this);
15546         
15547         this.timer = this.click.defer(this.delay || this.interval, this);
15548     },
15549
15550     // private
15551     click : function(){
15552         this.fireEvent("click", this);
15553         this.timer = this.click.defer(this.getInterval(), this);
15554     },
15555
15556     // private
15557     getInterval: function(){
15558         if(!this.accelerate){
15559             return this.interval;
15560         }
15561         var pressTime = this.mousedownTime.getElapsed();
15562         if(pressTime < 500){
15563             return 400;
15564         }else if(pressTime < 1700){
15565             return 320;
15566         }else if(pressTime < 2600){
15567             return 250;
15568         }else if(pressTime < 3500){
15569             return 180;
15570         }else if(pressTime < 4400){
15571             return 140;
15572         }else if(pressTime < 5300){
15573             return 80;
15574         }else if(pressTime < 6200){
15575             return 50;
15576         }else{
15577             return 10;
15578         }
15579     },
15580
15581     // private
15582     handleMouseOut : function(){
15583         clearTimeout(this.timer);
15584         if(this.pressClass){
15585             this.el.removeClass(this.pressClass);
15586         }
15587         this.el.on("mouseover", this.handleMouseReturn, this);
15588     },
15589
15590     // private
15591     handleMouseReturn : function(){
15592         this.el.un("mouseover", this.handleMouseReturn);
15593         if(this.pressClass){
15594             this.el.addClass(this.pressClass);
15595         }
15596         this.click();
15597     },
15598
15599     // private
15600     handleMouseUp : function(){
15601         clearTimeout(this.timer);
15602         this.el.un("mouseover", this.handleMouseReturn);
15603         this.el.un("mouseout", this.handleMouseOut);
15604         Roo.get(document).un("mouseup", this.handleMouseUp);
15605         this.el.removeClass(this.pressClass);
15606         this.fireEvent("mouseup", this);
15607     }
15608 });/**
15609  * @class Roo.util.Clipboard
15610  * @static
15611  * 
15612  * Clipboard UTILS
15613  * 
15614  **/
15615 Roo.util.Clipboard = {
15616     /**
15617      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15618      * @param {String} text to copy to clipboard
15619      */
15620     write : function(text) {
15621         // navigator clipboard api needs a secure context (https)
15622         if (navigator.clipboard && window.isSecureContext) {
15623             // navigator clipboard api method'
15624             navigator.clipboard.writeText(text);
15625             return ;
15626         } 
15627         // text area method
15628         var ta = document.createElement("textarea");
15629         ta.value = text;
15630         // make the textarea out of viewport
15631         ta.style.position = "fixed";
15632         ta.style.left = "-999999px";
15633         ta.style.top = "-999999px";
15634         document.body.appendChild(ta);
15635         ta.focus();
15636         ta.select();
15637         document.execCommand('copy');
15638         (function() {
15639             ta.remove();
15640         }).defer(100);
15641         
15642     }
15643         
15644 }
15645     /*
15646  * Based on:
15647  * Ext JS Library 1.1.1
15648  * Copyright(c) 2006-2007, Ext JS, LLC.
15649  *
15650  * Originally Released Under LGPL - original licence link has changed is not relivant.
15651  *
15652  * Fork - LGPL
15653  * <script type="text/javascript">
15654  */
15655
15656  
15657 /**
15658  * @class Roo.KeyNav
15659  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15660  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15661  * way to implement custom navigation schemes for any UI component.</p>
15662  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15663  * pageUp, pageDown, del, home, end.  Usage:</p>
15664  <pre><code>
15665 var nav = new Roo.KeyNav("my-element", {
15666     "left" : function(e){
15667         this.moveLeft(e.ctrlKey);
15668     },
15669     "right" : function(e){
15670         this.moveRight(e.ctrlKey);
15671     },
15672     "enter" : function(e){
15673         this.save();
15674     },
15675     scope : this
15676 });
15677 </code></pre>
15678  * @constructor
15679  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15680  * @param {Object} config The config
15681  */
15682 Roo.KeyNav = function(el, config){
15683     this.el = Roo.get(el);
15684     Roo.apply(this, config);
15685     if(!this.disabled){
15686         this.disabled = true;
15687         this.enable();
15688     }
15689 };
15690
15691 Roo.KeyNav.prototype = {
15692     /**
15693      * @cfg {Boolean} disabled
15694      * True to disable this KeyNav instance (defaults to false)
15695      */
15696     disabled : false,
15697     /**
15698      * @cfg {String} defaultEventAction
15699      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15700      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15701      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15702      */
15703     defaultEventAction: "stopEvent",
15704     /**
15705      * @cfg {Boolean} forceKeyDown
15706      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15707      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15708      * handle keydown instead of keypress.
15709      */
15710     forceKeyDown : false,
15711
15712     // private
15713     prepareEvent : function(e){
15714         var k = e.getKey();
15715         var h = this.keyToHandler[k];
15716         //if(h && this[h]){
15717         //    e.stopPropagation();
15718         //}
15719         if(Roo.isSafari && h && k >= 37 && k <= 40){
15720             e.stopEvent();
15721         }
15722     },
15723
15724     // private
15725     relay : function(e){
15726         var k = e.getKey();
15727         var h = this.keyToHandler[k];
15728         if(h && this[h]){
15729             if(this.doRelay(e, this[h], h) !== true){
15730                 e[this.defaultEventAction]();
15731             }
15732         }
15733     },
15734
15735     // private
15736     doRelay : function(e, h, hname){
15737         return h.call(this.scope || this, e);
15738     },
15739
15740     // possible handlers
15741     enter : false,
15742     left : false,
15743     right : false,
15744     up : false,
15745     down : false,
15746     tab : false,
15747     esc : false,
15748     pageUp : false,
15749     pageDown : false,
15750     del : false,
15751     home : false,
15752     end : false,
15753
15754     // quick lookup hash
15755     keyToHandler : {
15756         37 : "left",
15757         39 : "right",
15758         38 : "up",
15759         40 : "down",
15760         33 : "pageUp",
15761         34 : "pageDown",
15762         46 : "del",
15763         36 : "home",
15764         35 : "end",
15765         13 : "enter",
15766         27 : "esc",
15767         9  : "tab"
15768     },
15769
15770         /**
15771          * Enable this KeyNav
15772          */
15773         enable: function(){
15774                 if(this.disabled){
15775             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15776             // the EventObject will normalize Safari automatically
15777             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15778                 this.el.on("keydown", this.relay,  this);
15779             }else{
15780                 this.el.on("keydown", this.prepareEvent,  this);
15781                 this.el.on("keypress", this.relay,  this);
15782             }
15783                     this.disabled = false;
15784                 }
15785         },
15786
15787         /**
15788          * Disable this KeyNav
15789          */
15790         disable: function(){
15791                 if(!this.disabled){
15792                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15793                 this.el.un("keydown", this.relay);
15794             }else{
15795                 this.el.un("keydown", this.prepareEvent);
15796                 this.el.un("keypress", this.relay);
15797             }
15798                     this.disabled = true;
15799                 }
15800         }
15801 };/*
15802  * Based on:
15803  * Ext JS Library 1.1.1
15804  * Copyright(c) 2006-2007, Ext JS, LLC.
15805  *
15806  * Originally Released Under LGPL - original licence link has changed is not relivant.
15807  *
15808  * Fork - LGPL
15809  * <script type="text/javascript">
15810  */
15811
15812  
15813 /**
15814  * @class Roo.KeyMap
15815  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15816  * The constructor accepts the same config object as defined by {@link #addBinding}.
15817  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15818  * combination it will call the function with this signature (if the match is a multi-key
15819  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15820  * A KeyMap can also handle a string representation of keys.<br />
15821  * Usage:
15822  <pre><code>
15823 // map one key by key code
15824 var map = new Roo.KeyMap("my-element", {
15825     key: 13, // or Roo.EventObject.ENTER
15826     fn: myHandler,
15827     scope: myObject
15828 });
15829
15830 // map multiple keys to one action by string
15831 var map = new Roo.KeyMap("my-element", {
15832     key: "a\r\n\t",
15833     fn: myHandler,
15834     scope: myObject
15835 });
15836
15837 // map multiple keys to multiple actions by strings and array of codes
15838 var map = new Roo.KeyMap("my-element", [
15839     {
15840         key: [10,13],
15841         fn: function(){ alert("Return was pressed"); }
15842     }, {
15843         key: "abc",
15844         fn: function(){ alert('a, b or c was pressed'); }
15845     }, {
15846         key: "\t",
15847         ctrl:true,
15848         shift:true,
15849         fn: function(){ alert('Control + shift + tab was pressed.'); }
15850     }
15851 ]);
15852 </code></pre>
15853  * <b>Note: A KeyMap starts enabled</b>
15854  * @constructor
15855  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15856  * @param {Object} config The config (see {@link #addBinding})
15857  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15858  */
15859 Roo.KeyMap = function(el, config, eventName){
15860     this.el  = Roo.get(el);
15861     this.eventName = eventName || "keydown";
15862     this.bindings = [];
15863     if(config){
15864         this.addBinding(config);
15865     }
15866     this.enable();
15867 };
15868
15869 Roo.KeyMap.prototype = {
15870     /**
15871      * True to stop the event from bubbling and prevent the default browser action if the
15872      * key was handled by the KeyMap (defaults to false)
15873      * @type Boolean
15874      */
15875     stopEvent : false,
15876
15877     /**
15878      * Add a new binding to this KeyMap. The following config object properties are supported:
15879      * <pre>
15880 Property    Type             Description
15881 ----------  ---------------  ----------------------------------------------------------------------
15882 key         String/Array     A single keycode or an array of keycodes to handle
15883 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15884 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15885 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15886 fn          Function         The function to call when KeyMap finds the expected key combination
15887 scope       Object           The scope of the callback function
15888 </pre>
15889      *
15890      * Usage:
15891      * <pre><code>
15892 // Create a KeyMap
15893 var map = new Roo.KeyMap(document, {
15894     key: Roo.EventObject.ENTER,
15895     fn: handleKey,
15896     scope: this
15897 });
15898
15899 //Add a new binding to the existing KeyMap later
15900 map.addBinding({
15901     key: 'abc',
15902     shift: true,
15903     fn: handleKey,
15904     scope: this
15905 });
15906 </code></pre>
15907      * @param {Object/Array} config A single KeyMap config or an array of configs
15908      */
15909         addBinding : function(config){
15910         if(config instanceof Array){
15911             for(var i = 0, len = config.length; i < len; i++){
15912                 this.addBinding(config[i]);
15913             }
15914             return;
15915         }
15916         var keyCode = config.key,
15917             shift = config.shift, 
15918             ctrl = config.ctrl, 
15919             alt = config.alt,
15920             fn = config.fn,
15921             scope = config.scope;
15922         if(typeof keyCode == "string"){
15923             var ks = [];
15924             var keyString = keyCode.toUpperCase();
15925             for(var j = 0, len = keyString.length; j < len; j++){
15926                 ks.push(keyString.charCodeAt(j));
15927             }
15928             keyCode = ks;
15929         }
15930         var keyArray = keyCode instanceof Array;
15931         var handler = function(e){
15932             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15933                 var k = e.getKey();
15934                 if(keyArray){
15935                     for(var i = 0, len = keyCode.length; i < len; i++){
15936                         if(keyCode[i] == k){
15937                           if(this.stopEvent){
15938                               e.stopEvent();
15939                           }
15940                           fn.call(scope || window, k, e);
15941                           return;
15942                         }
15943                     }
15944                 }else{
15945                     if(k == keyCode){
15946                         if(this.stopEvent){
15947                            e.stopEvent();
15948                         }
15949                         fn.call(scope || window, k, e);
15950                     }
15951                 }
15952             }
15953         };
15954         this.bindings.push(handler);  
15955         },
15956
15957     /**
15958      * Shorthand for adding a single key listener
15959      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15960      * following options:
15961      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15962      * @param {Function} fn The function to call
15963      * @param {Object} scope (optional) The scope of the function
15964      */
15965     on : function(key, fn, scope){
15966         var keyCode, shift, ctrl, alt;
15967         if(typeof key == "object" && !(key instanceof Array)){
15968             keyCode = key.key;
15969             shift = key.shift;
15970             ctrl = key.ctrl;
15971             alt = key.alt;
15972         }else{
15973             keyCode = key;
15974         }
15975         this.addBinding({
15976             key: keyCode,
15977             shift: shift,
15978             ctrl: ctrl,
15979             alt: alt,
15980             fn: fn,
15981             scope: scope
15982         })
15983     },
15984
15985     // private
15986     handleKeyDown : function(e){
15987             if(this.enabled){ //just in case
15988             var b = this.bindings;
15989             for(var i = 0, len = b.length; i < len; i++){
15990                 b[i].call(this, e);
15991             }
15992             }
15993         },
15994         
15995         /**
15996          * Returns true if this KeyMap is enabled
15997          * @return {Boolean} 
15998          */
15999         isEnabled : function(){
16000             return this.enabled;  
16001         },
16002         
16003         /**
16004          * Enables this KeyMap
16005          */
16006         enable: function(){
16007                 if(!this.enabled){
16008                     this.el.on(this.eventName, this.handleKeyDown, this);
16009                     this.enabled = true;
16010                 }
16011         },
16012
16013         /**
16014          * Disable this KeyMap
16015          */
16016         disable: function(){
16017                 if(this.enabled){
16018                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16019                     this.enabled = false;
16020                 }
16021         }
16022 };/*
16023  * Based on:
16024  * Ext JS Library 1.1.1
16025  * Copyright(c) 2006-2007, Ext JS, LLC.
16026  *
16027  * Originally Released Under LGPL - original licence link has changed is not relivant.
16028  *
16029  * Fork - LGPL
16030  * <script type="text/javascript">
16031  */
16032
16033  
16034 /**
16035  * @class Roo.util.TextMetrics
16036  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16037  * wide, in pixels, a given block of text will be.
16038  * @static
16039  */
16040 Roo.util.TextMetrics = function(){
16041     var shared;
16042     return {
16043         /**
16044          * Measures the size of the specified text
16045          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16046          * that can affect the size of the rendered text
16047          * @param {String} text The text to measure
16048          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16049          * in order to accurately measure the text height
16050          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16051          */
16052         measure : function(el, text, fixedWidth){
16053             if(!shared){
16054                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16055             }
16056             shared.bind(el);
16057             shared.setFixedWidth(fixedWidth || 'auto');
16058             return shared.getSize(text);
16059         },
16060
16061         /**
16062          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16063          * the overhead of multiple calls to initialize the style properties on each measurement.
16064          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16065          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16066          * in order to accurately measure the text height
16067          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16068          */
16069         createInstance : function(el, fixedWidth){
16070             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16071         }
16072     };
16073 }();
16074
16075 /**
16076  * @class Roo.util.TextMetrics.Instance
16077  * Instance of  TextMetrics Calcuation
16078  * @constructor
16079  * Create a new TextMetrics Instance
16080  * @param {Object} bindto
16081  * @param {Boolean} fixedWidth
16082  */
16083
16084 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16085 {
16086     var ml = new Roo.Element(document.createElement('div'));
16087     document.body.appendChild(ml.dom);
16088     ml.position('absolute');
16089     ml.setLeftTop(-1000, -1000);
16090     ml.hide();
16091
16092     if(fixedWidth){
16093         ml.setWidth(fixedWidth);
16094     }
16095      
16096     var instance = {
16097         /**
16098          * Returns the size of the specified text based on the internal element's style and width properties
16099          * @param {String} text The text to measure
16100          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16101          */
16102         getSize : function(text){
16103             ml.update(text);
16104             var s = ml.getSize();
16105             ml.update('');
16106             return s;
16107         },
16108
16109         /**
16110          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16111          * that can affect the size of the rendered text
16112          * @param {String/HTMLElement} el The element, dom node or id
16113          */
16114         bind : function(el){
16115             ml.setStyle(
16116                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16117             );
16118         },
16119
16120         /**
16121          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16122          * to set a fixed width in order to accurately measure the text height.
16123          * @param {Number} width The width to set on the element
16124          */
16125         setFixedWidth : function(width){
16126             ml.setWidth(width);
16127         },
16128
16129         /**
16130          * Returns the measured width of the specified text
16131          * @param {String} text The text to measure
16132          * @return {Number} width The width in pixels
16133          */
16134         getWidth : function(text){
16135             ml.dom.style.width = 'auto';
16136             return this.getSize(text).width;
16137         },
16138
16139         /**
16140          * Returns the measured height of the specified text.  For multiline text, be sure to call
16141          * {@link #setFixedWidth} if necessary.
16142          * @param {String} text The text to measure
16143          * @return {Number} height The height in pixels
16144          */
16145         getHeight : function(text){
16146             return this.getSize(text).height;
16147         }
16148     };
16149
16150     instance.bind(bindTo);
16151
16152     return instance;
16153 };
16154
16155 // backwards compat
16156 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16157  * Based on:
16158  * Ext JS Library 1.1.1
16159  * Copyright(c) 2006-2007, Ext JS, LLC.
16160  *
16161  * Originally Released Under LGPL - original licence link has changed is not relivant.
16162  *
16163  * Fork - LGPL
16164  * <script type="text/javascript">
16165  */
16166
16167 /**
16168  * @class Roo.state.Provider
16169  * Abstract base class for state provider implementations. This class provides methods
16170  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16171  * Provider interface.
16172  */
16173 Roo.state.Provider = function(){
16174     /**
16175      * @event statechange
16176      * Fires when a state change occurs.
16177      * @param {Provider} this This state provider
16178      * @param {String} key The state key which was changed
16179      * @param {String} value The encoded value for the state
16180      */
16181     this.addEvents({
16182         "statechange": true
16183     });
16184     this.state = {};
16185     Roo.state.Provider.superclass.constructor.call(this);
16186 };
16187 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16188     /**
16189      * Returns the current value for a key
16190      * @param {String} name The key name
16191      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16192      * @return {Mixed} The state data
16193      */
16194     get : function(name, defaultValue){
16195         return typeof this.state[name] == "undefined" ?
16196             defaultValue : this.state[name];
16197     },
16198     
16199     /**
16200      * Clears a value from the state
16201      * @param {String} name The key name
16202      */
16203     clear : function(name){
16204         delete this.state[name];
16205         this.fireEvent("statechange", this, name, null);
16206     },
16207     
16208     /**
16209      * Sets the value for a key
16210      * @param {String} name The key name
16211      * @param {Mixed} value The value to set
16212      */
16213     set : function(name, value){
16214         this.state[name] = value;
16215         this.fireEvent("statechange", this, name, value);
16216     },
16217     
16218     /**
16219      * Decodes a string previously encoded with {@link #encodeValue}.
16220      * @param {String} value The value to decode
16221      * @return {Mixed} The decoded value
16222      */
16223     decodeValue : function(cookie){
16224         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16225         var matches = re.exec(unescape(cookie));
16226         if(!matches || !matches[1]) {
16227             return; // non state cookie
16228         }
16229         var type = matches[1];
16230         var v = matches[2];
16231         switch(type){
16232             case "n":
16233                 return parseFloat(v);
16234             case "d":
16235                 return new Date(Date.parse(v));
16236             case "b":
16237                 return (v == "1");
16238             case "a":
16239                 var all = [];
16240                 var values = v.split("^");
16241                 for(var i = 0, len = values.length; i < len; i++){
16242                     all.push(this.decodeValue(values[i]));
16243                 }
16244                 return all;
16245            case "o":
16246                 var all = {};
16247                 var values = v.split("^");
16248                 for(var i = 0, len = values.length; i < len; i++){
16249                     var kv = values[i].split("=");
16250                     all[kv[0]] = this.decodeValue(kv[1]);
16251                 }
16252                 return all;
16253            default:
16254                 return v;
16255         }
16256     },
16257     
16258     /**
16259      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16260      * @param {Mixed} value The value to encode
16261      * @return {String} The encoded value
16262      */
16263     encodeValue : function(v){
16264         var enc;
16265         if(typeof v == "number"){
16266             enc = "n:" + v;
16267         }else if(typeof v == "boolean"){
16268             enc = "b:" + (v ? "1" : "0");
16269         }else if(v instanceof Date){
16270             enc = "d:" + v.toGMTString();
16271         }else if(v instanceof Array){
16272             var flat = "";
16273             for(var i = 0, len = v.length; i < len; i++){
16274                 flat += this.encodeValue(v[i]);
16275                 if(i != len-1) {
16276                     flat += "^";
16277                 }
16278             }
16279             enc = "a:" + flat;
16280         }else if(typeof v == "object"){
16281             var flat = "";
16282             for(var key in v){
16283                 if(typeof v[key] != "function"){
16284                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16285                 }
16286             }
16287             enc = "o:" + flat.substring(0, flat.length-1);
16288         }else{
16289             enc = "s:" + v;
16290         }
16291         return escape(enc);        
16292     }
16293 });
16294
16295 /*
16296  * Based on:
16297  * Ext JS Library 1.1.1
16298  * Copyright(c) 2006-2007, Ext JS, LLC.
16299  *
16300  * Originally Released Under LGPL - original licence link has changed is not relivant.
16301  *
16302  * Fork - LGPL
16303  * <script type="text/javascript">
16304  */
16305 /**
16306  * @class Roo.state.Manager
16307  * This is the global state manager. By default all components that are "state aware" check this class
16308  * for state information if you don't pass them a custom state provider. In order for this class
16309  * to be useful, it must be initialized with a provider when your application initializes.
16310  <pre><code>
16311 // in your initialization function
16312 init : function(){
16313    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16314    ...
16315    // supposed you have a {@link Roo.BorderLayout}
16316    var layout = new Roo.BorderLayout(...);
16317    layout.restoreState();
16318    // or a {Roo.BasicDialog}
16319    var dialog = new Roo.BasicDialog(...);
16320    dialog.restoreState();
16321  </code></pre>
16322  * @static
16323  */
16324 Roo.state.Manager = function(){
16325     var provider = new Roo.state.Provider();
16326     
16327     return {
16328         /**
16329          * Configures the default state provider for your application
16330          * @param {Provider} stateProvider The state provider to set
16331          */
16332         setProvider : function(stateProvider){
16333             provider = stateProvider;
16334         },
16335         
16336         /**
16337          * Returns the current value for a key
16338          * @param {String} name The key name
16339          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16340          * @return {Mixed} The state data
16341          */
16342         get : function(key, defaultValue){
16343             return provider.get(key, defaultValue);
16344         },
16345         
16346         /**
16347          * Sets the value for a key
16348          * @param {String} name The key name
16349          * @param {Mixed} value The state data
16350          */
16351          set : function(key, value){
16352             provider.set(key, value);
16353         },
16354         
16355         /**
16356          * Clears a value from the state
16357          * @param {String} name The key name
16358          */
16359         clear : function(key){
16360             provider.clear(key);
16361         },
16362         
16363         /**
16364          * Gets the currently configured state provider
16365          * @return {Provider} The state provider
16366          */
16367         getProvider : function(){
16368             return provider;
16369         }
16370     };
16371 }();
16372 /*
16373  * Based on:
16374  * Ext JS Library 1.1.1
16375  * Copyright(c) 2006-2007, Ext JS, LLC.
16376  *
16377  * Originally Released Under LGPL - original licence link has changed is not relivant.
16378  *
16379  * Fork - LGPL
16380  * <script type="text/javascript">
16381  */
16382 /**
16383  * @class Roo.state.CookieProvider
16384  * @extends Roo.state.Provider
16385  * The default Provider implementation which saves state via cookies.
16386  * <br />Usage:
16387  <pre><code>
16388    var cp = new Roo.state.CookieProvider({
16389        path: "/cgi-bin/",
16390        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16391        domain: "roojs.com"
16392    })
16393    Roo.state.Manager.setProvider(cp);
16394  </code></pre>
16395  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16396  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16397  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16398  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16399  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16400  * domain the page is running on including the 'www' like 'www.roojs.com')
16401  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16402  * @constructor
16403  * Create a new CookieProvider
16404  * @param {Object} config The configuration object
16405  */
16406 Roo.state.CookieProvider = function(config){
16407     Roo.state.CookieProvider.superclass.constructor.call(this);
16408     this.path = "/";
16409     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16410     this.domain = null;
16411     this.secure = false;
16412     Roo.apply(this, config);
16413     this.state = this.readCookies();
16414 };
16415
16416 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16417     // private
16418     set : function(name, value){
16419         if(typeof value == "undefined" || value === null){
16420             this.clear(name);
16421             return;
16422         }
16423         this.setCookie(name, value);
16424         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16425     },
16426
16427     // private
16428     clear : function(name){
16429         this.clearCookie(name);
16430         Roo.state.CookieProvider.superclass.clear.call(this, name);
16431     },
16432
16433     // private
16434     readCookies : function(){
16435         var cookies = {};
16436         var c = document.cookie + ";";
16437         var re = /\s?(.*?)=(.*?);/g;
16438         var matches;
16439         while((matches = re.exec(c)) != null){
16440             var name = matches[1];
16441             var value = matches[2];
16442             if(name && name.substring(0,3) == "ys-"){
16443                 cookies[name.substr(3)] = this.decodeValue(value);
16444             }
16445         }
16446         return cookies;
16447     },
16448
16449     // private
16450     setCookie : function(name, value){
16451         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16452            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16453            ((this.path == null) ? "" : ("; path=" + this.path)) +
16454            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16455            ((this.secure == true) ? "; secure" : "");
16456     },
16457
16458     // private
16459     clearCookie : function(name){
16460         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16461            ((this.path == null) ? "" : ("; path=" + this.path)) +
16462            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16463            ((this.secure == true) ? "; secure" : "");
16464     }
16465 });/*
16466  * Based on:
16467  * Ext JS Library 1.1.1
16468  * Copyright(c) 2006-2007, Ext JS, LLC.
16469  *
16470  * Originally Released Under LGPL - original licence link has changed is not relivant.
16471  *
16472  * Fork - LGPL
16473  * <script type="text/javascript">
16474  */
16475  
16476
16477 /**
16478  * @class Roo.ComponentMgr
16479  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16480  * @static
16481  */
16482 Roo.ComponentMgr = function(){
16483     var all = new Roo.util.MixedCollection();
16484
16485     return {
16486         /**
16487          * Registers a component.
16488          * @param {Roo.Component} c The component
16489          */
16490         register : function(c){
16491             all.add(c);
16492         },
16493
16494         /**
16495          * Unregisters a component.
16496          * @param {Roo.Component} c The component
16497          */
16498         unregister : function(c){
16499             all.remove(c);
16500         },
16501
16502         /**
16503          * Returns a component by id
16504          * @param {String} id The component id
16505          */
16506         get : function(id){
16507             return all.get(id);
16508         },
16509
16510         /**
16511          * Registers a function that will be called when a specified component is added to ComponentMgr
16512          * @param {String} id The component id
16513          * @param {Funtction} fn The callback function
16514          * @param {Object} scope The scope of the callback
16515          */
16516         onAvailable : function(id, fn, scope){
16517             all.on("add", function(index, o){
16518                 if(o.id == id){
16519                     fn.call(scope || o, o);
16520                     all.un("add", fn, scope);
16521                 }
16522             });
16523         }
16524     };
16525 }();/*
16526  * Based on:
16527  * Ext JS Library 1.1.1
16528  * Copyright(c) 2006-2007, Ext JS, LLC.
16529  *
16530  * Originally Released Under LGPL - original licence link has changed is not relivant.
16531  *
16532  * Fork - LGPL
16533  * <script type="text/javascript">
16534  */
16535  
16536 /**
16537  * @class Roo.Component
16538  * @extends Roo.util.Observable
16539  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16540  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16541  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16542  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16543  * All visual components (widgets) that require rendering into a layout should subclass Component.
16544  * @constructor
16545  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16546  * 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
16547  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16548  */
16549 Roo.Component = function(config){
16550     config = config || {};
16551     if(config.tagName || config.dom || typeof config == "string"){ // element object
16552         config = {el: config, id: config.id || config};
16553     }
16554     this.initialConfig = config;
16555
16556     Roo.apply(this, config);
16557     this.addEvents({
16558         /**
16559          * @event disable
16560          * Fires after the component is disabled.
16561              * @param {Roo.Component} this
16562              */
16563         disable : true,
16564         /**
16565          * @event enable
16566          * Fires after the component is enabled.
16567              * @param {Roo.Component} this
16568              */
16569         enable : true,
16570         /**
16571          * @event beforeshow
16572          * Fires before the component is shown.  Return false to stop the show.
16573              * @param {Roo.Component} this
16574              */
16575         beforeshow : true,
16576         /**
16577          * @event show
16578          * Fires after the component is shown.
16579              * @param {Roo.Component} this
16580              */
16581         show : true,
16582         /**
16583          * @event beforehide
16584          * Fires before the component is hidden. Return false to stop the hide.
16585              * @param {Roo.Component} this
16586              */
16587         beforehide : true,
16588         /**
16589          * @event hide
16590          * Fires after the component is hidden.
16591              * @param {Roo.Component} this
16592              */
16593         hide : true,
16594         /**
16595          * @event beforerender
16596          * Fires before the component is rendered. Return false to stop the render.
16597              * @param {Roo.Component} this
16598              */
16599         beforerender : true,
16600         /**
16601          * @event render
16602          * Fires after the component is rendered.
16603              * @param {Roo.Component} this
16604              */
16605         render : true,
16606         /**
16607          * @event beforedestroy
16608          * Fires before the component is destroyed. Return false to stop the destroy.
16609              * @param {Roo.Component} this
16610              */
16611         beforedestroy : true,
16612         /**
16613          * @event destroy
16614          * Fires after the component is destroyed.
16615              * @param {Roo.Component} this
16616              */
16617         destroy : true
16618     });
16619     if(!this.id){
16620         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16621     }
16622     Roo.ComponentMgr.register(this);
16623     Roo.Component.superclass.constructor.call(this);
16624     this.initComponent();
16625     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16626         this.render(this.renderTo);
16627         delete this.renderTo;
16628     }
16629 };
16630
16631 /** @private */
16632 Roo.Component.AUTO_ID = 1000;
16633
16634 Roo.extend(Roo.Component, Roo.util.Observable, {
16635     /**
16636      * @scope Roo.Component.prototype
16637      * @type {Boolean}
16638      * true if this component is hidden. Read-only.
16639      */
16640     hidden : false,
16641     /**
16642      * @type {Boolean}
16643      * true if this component is disabled. Read-only.
16644      */
16645     disabled : false,
16646     /**
16647      * @type {Boolean}
16648      * true if this component has been rendered. Read-only.
16649      */
16650     rendered : false,
16651     
16652     /** @cfg {String} disableClass
16653      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16654      */
16655     disabledClass : "x-item-disabled",
16656         /** @cfg {Boolean} allowDomMove
16657          * Whether the component can move the Dom node when rendering (defaults to true).
16658          */
16659     allowDomMove : true,
16660     /** @cfg {String} hideMode (display|visibility)
16661      * How this component should hidden. Supported values are
16662      * "visibility" (css visibility), "offsets" (negative offset position) and
16663      * "display" (css display) - defaults to "display".
16664      */
16665     hideMode: 'display',
16666
16667     /** @private */
16668     ctype : "Roo.Component",
16669
16670     /**
16671      * @cfg {String} actionMode 
16672      * which property holds the element that used for  hide() / show() / disable() / enable()
16673      * default is 'el' for forms you probably want to set this to fieldEl 
16674      */
16675     actionMode : "el",
16676
16677     /** @private */
16678     getActionEl : function(){
16679         return this[this.actionMode];
16680     },
16681
16682     initComponent : Roo.emptyFn,
16683     /**
16684      * If this is a lazy rendering component, render it to its container element.
16685      * @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.
16686      */
16687     render : function(container, position){
16688         
16689         if(this.rendered){
16690             return this;
16691         }
16692         
16693         if(this.fireEvent("beforerender", this) === false){
16694             return false;
16695         }
16696         
16697         if(!container && this.el){
16698             this.el = Roo.get(this.el);
16699             container = this.el.dom.parentNode;
16700             this.allowDomMove = false;
16701         }
16702         this.container = Roo.get(container);
16703         this.rendered = true;
16704         if(position !== undefined){
16705             if(typeof position == 'number'){
16706                 position = this.container.dom.childNodes[position];
16707             }else{
16708                 position = Roo.getDom(position);
16709             }
16710         }
16711         this.onRender(this.container, position || null);
16712         if(this.cls){
16713             this.el.addClass(this.cls);
16714             delete this.cls;
16715         }
16716         if(this.style){
16717             this.el.applyStyles(this.style);
16718             delete this.style;
16719         }
16720         this.fireEvent("render", this);
16721         this.afterRender(this.container);
16722         if(this.hidden){
16723             this.hide();
16724         }
16725         if(this.disabled){
16726             this.disable();
16727         }
16728
16729         return this;
16730         
16731     },
16732
16733     /** @private */
16734     // default function is not really useful
16735     onRender : function(ct, position){
16736         if(this.el){
16737             this.el = Roo.get(this.el);
16738             if(this.allowDomMove !== false){
16739                 ct.dom.insertBefore(this.el.dom, position);
16740             }
16741         }
16742     },
16743
16744     /** @private */
16745     getAutoCreate : function(){
16746         var cfg = typeof this.autoCreate == "object" ?
16747                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16748         if(this.id && !cfg.id){
16749             cfg.id = this.id;
16750         }
16751         return cfg;
16752     },
16753
16754     /** @private */
16755     afterRender : Roo.emptyFn,
16756
16757     /**
16758      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16759      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16760      */
16761     destroy : function(){
16762         if(this.fireEvent("beforedestroy", this) !== false){
16763             this.purgeListeners();
16764             this.beforeDestroy();
16765             if(this.rendered){
16766                 this.el.removeAllListeners();
16767                 this.el.remove();
16768                 if(this.actionMode == "container"){
16769                     this.container.remove();
16770                 }
16771             }
16772             this.onDestroy();
16773             Roo.ComponentMgr.unregister(this);
16774             this.fireEvent("destroy", this);
16775         }
16776     },
16777
16778         /** @private */
16779     beforeDestroy : function(){
16780
16781     },
16782
16783         /** @private */
16784         onDestroy : function(){
16785
16786     },
16787
16788     /**
16789      * Returns the underlying {@link Roo.Element}.
16790      * @return {Roo.Element} The element
16791      */
16792     getEl : function(){
16793         return this.el;
16794     },
16795
16796     /**
16797      * Returns the id of this component.
16798      * @return {String}
16799      */
16800     getId : function(){
16801         return this.id;
16802     },
16803
16804     /**
16805      * Try to focus this component.
16806      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16807      * @return {Roo.Component} this
16808      */
16809     focus : function(selectText){
16810         if(this.rendered){
16811             this.el.focus();
16812             if(selectText === true){
16813                 this.el.dom.select();
16814             }
16815         }
16816         return this;
16817     },
16818
16819     /** @private */
16820     blur : function(){
16821         if(this.rendered){
16822             this.el.blur();
16823         }
16824         return this;
16825     },
16826
16827     /**
16828      * Disable this component.
16829      * @return {Roo.Component} this
16830      */
16831     disable : function(){
16832         if(this.rendered){
16833             this.onDisable();
16834         }
16835         this.disabled = true;
16836         this.fireEvent("disable", this);
16837         return this;
16838     },
16839
16840         // private
16841     onDisable : function(){
16842         this.getActionEl().addClass(this.disabledClass);
16843         this.el.dom.disabled = true;
16844     },
16845
16846     /**
16847      * Enable this component.
16848      * @return {Roo.Component} this
16849      */
16850     enable : function(){
16851         if(this.rendered){
16852             this.onEnable();
16853         }
16854         this.disabled = false;
16855         this.fireEvent("enable", this);
16856         return this;
16857     },
16858
16859         // private
16860     onEnable : function(){
16861         this.getActionEl().removeClass(this.disabledClass);
16862         this.el.dom.disabled = false;
16863     },
16864
16865     /**
16866      * Convenience function for setting disabled/enabled by boolean.
16867      * @param {Boolean} disabled
16868      */
16869     setDisabled : function(disabled){
16870         this[disabled ? "disable" : "enable"]();
16871     },
16872
16873     /**
16874      * Show this component.
16875      * @return {Roo.Component} this
16876      */
16877     show: function(){
16878         if(this.fireEvent("beforeshow", this) !== false){
16879             this.hidden = false;
16880             if(this.rendered){
16881                 this.onShow();
16882             }
16883             this.fireEvent("show", this);
16884         }
16885         return this;
16886     },
16887
16888     // private
16889     onShow : function(){
16890         var ae = this.getActionEl();
16891         if(this.hideMode == 'visibility'){
16892             ae.dom.style.visibility = "visible";
16893         }else if(this.hideMode == 'offsets'){
16894             ae.removeClass('x-hidden');
16895         }else{
16896             ae.dom.style.display = "";
16897         }
16898     },
16899
16900     /**
16901      * Hide this component.
16902      * @return {Roo.Component} this
16903      */
16904     hide: function(){
16905         if(this.fireEvent("beforehide", this) !== false){
16906             this.hidden = true;
16907             if(this.rendered){
16908                 this.onHide();
16909             }
16910             this.fireEvent("hide", this);
16911         }
16912         return this;
16913     },
16914
16915     // private
16916     onHide : function(){
16917         var ae = this.getActionEl();
16918         if(this.hideMode == 'visibility'){
16919             ae.dom.style.visibility = "hidden";
16920         }else if(this.hideMode == 'offsets'){
16921             ae.addClass('x-hidden');
16922         }else{
16923             ae.dom.style.display = "none";
16924         }
16925     },
16926
16927     /**
16928      * Convenience function to hide or show this component by boolean.
16929      * @param {Boolean} visible True to show, false to hide
16930      * @return {Roo.Component} this
16931      */
16932     setVisible: function(visible){
16933         if(visible) {
16934             this.show();
16935         }else{
16936             this.hide();
16937         }
16938         return this;
16939     },
16940
16941     /**
16942      * Returns true if this component is visible.
16943      */
16944     isVisible : function(){
16945         return this.getActionEl().isVisible();
16946     },
16947
16948     cloneConfig : function(overrides){
16949         overrides = overrides || {};
16950         var id = overrides.id || Roo.id();
16951         var cfg = Roo.applyIf(overrides, this.initialConfig);
16952         cfg.id = id; // prevent dup id
16953         return new this.constructor(cfg);
16954     }
16955 });/*
16956  * Based on:
16957  * Ext JS Library 1.1.1
16958  * Copyright(c) 2006-2007, Ext JS, LLC.
16959  *
16960  * Originally Released Under LGPL - original licence link has changed is not relivant.
16961  *
16962  * Fork - LGPL
16963  * <script type="text/javascript">
16964  */
16965
16966 /**
16967  * @class Roo.BoxComponent
16968  * @extends Roo.Component
16969  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16970  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16971  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16972  * layout containers.
16973  * @constructor
16974  * @param {Roo.Element/String/Object} config The configuration options.
16975  */
16976 Roo.BoxComponent = function(config){
16977     Roo.Component.call(this, config);
16978     this.addEvents({
16979         /**
16980          * @event resize
16981          * Fires after the component is resized.
16982              * @param {Roo.Component} this
16983              * @param {Number} adjWidth The box-adjusted width that was set
16984              * @param {Number} adjHeight The box-adjusted height that was set
16985              * @param {Number} rawWidth The width that was originally specified
16986              * @param {Number} rawHeight The height that was originally specified
16987              */
16988         resize : true,
16989         /**
16990          * @event move
16991          * Fires after the component is moved.
16992              * @param {Roo.Component} this
16993              * @param {Number} x The new x position
16994              * @param {Number} y The new y position
16995              */
16996         move : true
16997     });
16998 };
16999
17000 Roo.extend(Roo.BoxComponent, Roo.Component, {
17001     // private, set in afterRender to signify that the component has been rendered
17002     boxReady : false,
17003     // private, used to defer height settings to subclasses
17004     deferHeight: false,
17005     /** @cfg {Number} width
17006      * width (optional) size of component
17007      */
17008      /** @cfg {Number} height
17009      * height (optional) size of component
17010      */
17011      
17012     /**
17013      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17014      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17015      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17016      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17017      * @return {Roo.BoxComponent} this
17018      */
17019     setSize : function(w, h){
17020         // support for standard size objects
17021         if(typeof w == 'object'){
17022             h = w.height;
17023             w = w.width;
17024         }
17025         // not rendered
17026         if(!this.boxReady){
17027             this.width = w;
17028             this.height = h;
17029             return this;
17030         }
17031
17032         // prevent recalcs when not needed
17033         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17034             return this;
17035         }
17036         this.lastSize = {width: w, height: h};
17037
17038         var adj = this.adjustSize(w, h);
17039         var aw = adj.width, ah = adj.height;
17040         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17041             var rz = this.getResizeEl();
17042             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17043                 rz.setSize(aw, ah);
17044             }else if(!this.deferHeight && ah !== undefined){
17045                 rz.setHeight(ah);
17046             }else if(aw !== undefined){
17047                 rz.setWidth(aw);
17048             }
17049             this.onResize(aw, ah, w, h);
17050             this.fireEvent('resize', this, aw, ah, w, h);
17051         }
17052         return this;
17053     },
17054
17055     /**
17056      * Gets the current size of the component's underlying element.
17057      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17058      */
17059     getSize : function(){
17060         return this.el.getSize();
17061     },
17062
17063     /**
17064      * Gets the current XY position of the component's underlying element.
17065      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17066      * @return {Array} The XY position of the element (e.g., [100, 200])
17067      */
17068     getPosition : function(local){
17069         if(local === true){
17070             return [this.el.getLeft(true), this.el.getTop(true)];
17071         }
17072         return this.xy || this.el.getXY();
17073     },
17074
17075     /**
17076      * Gets the current box measurements of the component's underlying element.
17077      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17078      * @returns {Object} box An object in the format {x, y, width, height}
17079      */
17080     getBox : function(local){
17081         var s = this.el.getSize();
17082         if(local){
17083             s.x = this.el.getLeft(true);
17084             s.y = this.el.getTop(true);
17085         }else{
17086             var xy = this.xy || this.el.getXY();
17087             s.x = xy[0];
17088             s.y = xy[1];
17089         }
17090         return s;
17091     },
17092
17093     /**
17094      * Sets the current box measurements of the component's underlying element.
17095      * @param {Object} box An object in the format {x, y, width, height}
17096      * @returns {Roo.BoxComponent} this
17097      */
17098     updateBox : function(box){
17099         this.setSize(box.width, box.height);
17100         this.setPagePosition(box.x, box.y);
17101         return this;
17102     },
17103
17104     // protected
17105     getResizeEl : function(){
17106         return this.resizeEl || this.el;
17107     },
17108
17109     // protected
17110     getPositionEl : function(){
17111         return this.positionEl || this.el;
17112     },
17113
17114     /**
17115      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17116      * This method fires the move event.
17117      * @param {Number} left The new left
17118      * @param {Number} top The new top
17119      * @returns {Roo.BoxComponent} this
17120      */
17121     setPosition : function(x, y){
17122         this.x = x;
17123         this.y = y;
17124         if(!this.boxReady){
17125             return this;
17126         }
17127         var adj = this.adjustPosition(x, y);
17128         var ax = adj.x, ay = adj.y;
17129
17130         var el = this.getPositionEl();
17131         if(ax !== undefined || ay !== undefined){
17132             if(ax !== undefined && ay !== undefined){
17133                 el.setLeftTop(ax, ay);
17134             }else if(ax !== undefined){
17135                 el.setLeft(ax);
17136             }else if(ay !== undefined){
17137                 el.setTop(ay);
17138             }
17139             this.onPosition(ax, ay);
17140             this.fireEvent('move', this, ax, ay);
17141         }
17142         return this;
17143     },
17144
17145     /**
17146      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17147      * This method fires the move event.
17148      * @param {Number} x The new x position
17149      * @param {Number} y The new y position
17150      * @returns {Roo.BoxComponent} this
17151      */
17152     setPagePosition : function(x, y){
17153         this.pageX = x;
17154         this.pageY = y;
17155         if(!this.boxReady){
17156             return;
17157         }
17158         if(x === undefined || y === undefined){ // cannot translate undefined points
17159             return;
17160         }
17161         var p = this.el.translatePoints(x, y);
17162         this.setPosition(p.left, p.top);
17163         return this;
17164     },
17165
17166     // private
17167     onRender : function(ct, position){
17168         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17169         if(this.resizeEl){
17170             this.resizeEl = Roo.get(this.resizeEl);
17171         }
17172         if(this.positionEl){
17173             this.positionEl = Roo.get(this.positionEl);
17174         }
17175     },
17176
17177     // private
17178     afterRender : function(){
17179         Roo.BoxComponent.superclass.afterRender.call(this);
17180         this.boxReady = true;
17181         this.setSize(this.width, this.height);
17182         if(this.x || this.y){
17183             this.setPosition(this.x, this.y);
17184         }
17185         if(this.pageX || this.pageY){
17186             this.setPagePosition(this.pageX, this.pageY);
17187         }
17188     },
17189
17190     /**
17191      * Force the component's size to recalculate based on the underlying element's current height and width.
17192      * @returns {Roo.BoxComponent} this
17193      */
17194     syncSize : function(){
17195         delete this.lastSize;
17196         this.setSize(this.el.getWidth(), this.el.getHeight());
17197         return this;
17198     },
17199
17200     /**
17201      * Called after the component is resized, this method is empty by default but can be implemented by any
17202      * subclass that needs to perform custom logic after a resize occurs.
17203      * @param {Number} adjWidth The box-adjusted width that was set
17204      * @param {Number} adjHeight The box-adjusted height that was set
17205      * @param {Number} rawWidth The width that was originally specified
17206      * @param {Number} rawHeight The height that was originally specified
17207      */
17208     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17209
17210     },
17211
17212     /**
17213      * Called after the component is moved, this method is empty by default but can be implemented by any
17214      * subclass that needs to perform custom logic after a move occurs.
17215      * @param {Number} x The new x position
17216      * @param {Number} y The new y position
17217      */
17218     onPosition : function(x, y){
17219
17220     },
17221
17222     // private
17223     adjustSize : function(w, h){
17224         if(this.autoWidth){
17225             w = 'auto';
17226         }
17227         if(this.autoHeight){
17228             h = 'auto';
17229         }
17230         return {width : w, height: h};
17231     },
17232
17233     // private
17234     adjustPosition : function(x, y){
17235         return {x : x, y: y};
17236     }
17237 });/*
17238  * Based on:
17239  * Ext JS Library 1.1.1
17240  * Copyright(c) 2006-2007, Ext JS, LLC.
17241  *
17242  * Originally Released Under LGPL - original licence link has changed is not relivant.
17243  *
17244  * Fork - LGPL
17245  * <script type="text/javascript">
17246  */
17247  (function(){ 
17248 /**
17249  * @class Roo.Layer
17250  * @extends Roo.Element
17251  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17252  * automatic maintaining of shadow/shim positions.
17253  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17254  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17255  * you can pass a string with a CSS class name. False turns off the shadow.
17256  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17257  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17258  * @cfg {String} cls CSS class to add to the element
17259  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17260  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17261  * @constructor
17262  * @param {Object} config An object with config options.
17263  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17264  */
17265
17266 Roo.Layer = function(config, existingEl){
17267     config = config || {};
17268     var dh = Roo.DomHelper;
17269     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17270     if(existingEl){
17271         this.dom = Roo.getDom(existingEl);
17272     }
17273     if(!this.dom){
17274         var o = config.dh || {tag: "div", cls: "x-layer"};
17275         this.dom = dh.append(pel, o);
17276     }
17277     if(config.cls){
17278         this.addClass(config.cls);
17279     }
17280     this.constrain = config.constrain !== false;
17281     this.visibilityMode = Roo.Element.VISIBILITY;
17282     if(config.id){
17283         this.id = this.dom.id = config.id;
17284     }else{
17285         this.id = Roo.id(this.dom);
17286     }
17287     this.zindex = config.zindex || this.getZIndex();
17288     this.position("absolute", this.zindex);
17289     if(config.shadow){
17290         this.shadowOffset = config.shadowOffset || 4;
17291         this.shadow = new Roo.Shadow({
17292             offset : this.shadowOffset,
17293             mode : config.shadow
17294         });
17295     }else{
17296         this.shadowOffset = 0;
17297     }
17298     this.useShim = config.shim !== false && Roo.useShims;
17299     this.useDisplay = config.useDisplay;
17300     this.hide();
17301 };
17302
17303 var supr = Roo.Element.prototype;
17304
17305 // shims are shared among layer to keep from having 100 iframes
17306 var shims = [];
17307
17308 Roo.extend(Roo.Layer, Roo.Element, {
17309
17310     getZIndex : function(){
17311         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17312     },
17313
17314     getShim : function(){
17315         if(!this.useShim){
17316             return null;
17317         }
17318         if(this.shim){
17319             return this.shim;
17320         }
17321         var shim = shims.shift();
17322         if(!shim){
17323             shim = this.createShim();
17324             shim.enableDisplayMode('block');
17325             shim.dom.style.display = 'none';
17326             shim.dom.style.visibility = 'visible';
17327         }
17328         var pn = this.dom.parentNode;
17329         if(shim.dom.parentNode != pn){
17330             pn.insertBefore(shim.dom, this.dom);
17331         }
17332         shim.setStyle('z-index', this.getZIndex()-2);
17333         this.shim = shim;
17334         return shim;
17335     },
17336
17337     hideShim : function(){
17338         if(this.shim){
17339             this.shim.setDisplayed(false);
17340             shims.push(this.shim);
17341             delete this.shim;
17342         }
17343     },
17344
17345     disableShadow : function(){
17346         if(this.shadow){
17347             this.shadowDisabled = true;
17348             this.shadow.hide();
17349             this.lastShadowOffset = this.shadowOffset;
17350             this.shadowOffset = 0;
17351         }
17352     },
17353
17354     enableShadow : function(show){
17355         if(this.shadow){
17356             this.shadowDisabled = false;
17357             this.shadowOffset = this.lastShadowOffset;
17358             delete this.lastShadowOffset;
17359             if(show){
17360                 this.sync(true);
17361             }
17362         }
17363     },
17364
17365     // private
17366     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17367     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17368     sync : function(doShow){
17369         var sw = this.shadow;
17370         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17371             var sh = this.getShim();
17372
17373             var w = this.getWidth(),
17374                 h = this.getHeight();
17375
17376             var l = this.getLeft(true),
17377                 t = this.getTop(true);
17378
17379             if(sw && !this.shadowDisabled){
17380                 if(doShow && !sw.isVisible()){
17381                     sw.show(this);
17382                 }else{
17383                     sw.realign(l, t, w, h);
17384                 }
17385                 if(sh){
17386                     if(doShow){
17387                        sh.show();
17388                     }
17389                     // fit the shim behind the shadow, so it is shimmed too
17390                     var a = sw.adjusts, s = sh.dom.style;
17391                     s.left = (Math.min(l, l+a.l))+"px";
17392                     s.top = (Math.min(t, t+a.t))+"px";
17393                     s.width = (w+a.w)+"px";
17394                     s.height = (h+a.h)+"px";
17395                 }
17396             }else if(sh){
17397                 if(doShow){
17398                    sh.show();
17399                 }
17400                 sh.setSize(w, h);
17401                 sh.setLeftTop(l, t);
17402             }
17403             
17404         }
17405     },
17406
17407     // private
17408     destroy : function(){
17409         this.hideShim();
17410         if(this.shadow){
17411             this.shadow.hide();
17412         }
17413         this.removeAllListeners();
17414         var pn = this.dom.parentNode;
17415         if(pn){
17416             pn.removeChild(this.dom);
17417         }
17418         Roo.Element.uncache(this.id);
17419     },
17420
17421     remove : function(){
17422         this.destroy();
17423     },
17424
17425     // private
17426     beginUpdate : function(){
17427         this.updating = true;
17428     },
17429
17430     // private
17431     endUpdate : function(){
17432         this.updating = false;
17433         this.sync(true);
17434     },
17435
17436     // private
17437     hideUnders : function(negOffset){
17438         if(this.shadow){
17439             this.shadow.hide();
17440         }
17441         this.hideShim();
17442     },
17443
17444     // private
17445     constrainXY : function(){
17446         if(this.constrain){
17447             var vw = Roo.lib.Dom.getViewWidth(),
17448                 vh = Roo.lib.Dom.getViewHeight();
17449             var s = Roo.get(document).getScroll();
17450
17451             var xy = this.getXY();
17452             var x = xy[0], y = xy[1];   
17453             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17454             // only move it if it needs it
17455             var moved = false;
17456             // first validate right/bottom
17457             if((x + w) > vw+s.left){
17458                 x = vw - w - this.shadowOffset;
17459                 moved = true;
17460             }
17461             if((y + h) > vh+s.top){
17462                 y = vh - h - this.shadowOffset;
17463                 moved = true;
17464             }
17465             // then make sure top/left isn't negative
17466             if(x < s.left){
17467                 x = s.left;
17468                 moved = true;
17469             }
17470             if(y < s.top){
17471                 y = s.top;
17472                 moved = true;
17473             }
17474             if(moved){
17475                 if(this.avoidY){
17476                     var ay = this.avoidY;
17477                     if(y <= ay && (y+h) >= ay){
17478                         y = ay-h-5;   
17479                     }
17480                 }
17481                 xy = [x, y];
17482                 this.storeXY(xy);
17483                 supr.setXY.call(this, xy);
17484                 this.sync();
17485             }
17486         }
17487     },
17488
17489     isVisible : function(){
17490         return this.visible;    
17491     },
17492
17493     // private
17494     showAction : function(){
17495         this.visible = true; // track visibility to prevent getStyle calls
17496         if(this.useDisplay === true){
17497             this.setDisplayed("");
17498         }else if(this.lastXY){
17499             supr.setXY.call(this, this.lastXY);
17500         }else if(this.lastLT){
17501             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17502         }
17503     },
17504
17505     // private
17506     hideAction : function(){
17507         this.visible = false;
17508         if(this.useDisplay === true){
17509             this.setDisplayed(false);
17510         }else{
17511             this.setLeftTop(-10000,-10000);
17512         }
17513     },
17514
17515     // overridden Element method
17516     setVisible : function(v, a, d, c, e){
17517         if(v){
17518             this.showAction();
17519         }
17520         if(a && v){
17521             var cb = function(){
17522                 this.sync(true);
17523                 if(c){
17524                     c();
17525                 }
17526             }.createDelegate(this);
17527             supr.setVisible.call(this, true, true, d, cb, e);
17528         }else{
17529             if(!v){
17530                 this.hideUnders(true);
17531             }
17532             var cb = c;
17533             if(a){
17534                 cb = function(){
17535                     this.hideAction();
17536                     if(c){
17537                         c();
17538                     }
17539                 }.createDelegate(this);
17540             }
17541             supr.setVisible.call(this, v, a, d, cb, e);
17542             if(v){
17543                 this.sync(true);
17544             }else if(!a){
17545                 this.hideAction();
17546             }
17547         }
17548     },
17549
17550     storeXY : function(xy){
17551         delete this.lastLT;
17552         this.lastXY = xy;
17553     },
17554
17555     storeLeftTop : function(left, top){
17556         delete this.lastXY;
17557         this.lastLT = [left, top];
17558     },
17559
17560     // private
17561     beforeFx : function(){
17562         this.beforeAction();
17563         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17564     },
17565
17566     // private
17567     afterFx : function(){
17568         Roo.Layer.superclass.afterFx.apply(this, arguments);
17569         this.sync(this.isVisible());
17570     },
17571
17572     // private
17573     beforeAction : function(){
17574         if(!this.updating && this.shadow){
17575             this.shadow.hide();
17576         }
17577     },
17578
17579     // overridden Element method
17580     setLeft : function(left){
17581         this.storeLeftTop(left, this.getTop(true));
17582         supr.setLeft.apply(this, arguments);
17583         this.sync();
17584     },
17585
17586     setTop : function(top){
17587         this.storeLeftTop(this.getLeft(true), top);
17588         supr.setTop.apply(this, arguments);
17589         this.sync();
17590     },
17591
17592     setLeftTop : function(left, top){
17593         this.storeLeftTop(left, top);
17594         supr.setLeftTop.apply(this, arguments);
17595         this.sync();
17596     },
17597
17598     setXY : function(xy, a, d, c, e){
17599         this.fixDisplay();
17600         this.beforeAction();
17601         this.storeXY(xy);
17602         var cb = this.createCB(c);
17603         supr.setXY.call(this, xy, a, d, cb, e);
17604         if(!a){
17605             cb();
17606         }
17607     },
17608
17609     // private
17610     createCB : function(c){
17611         var el = this;
17612         return function(){
17613             el.constrainXY();
17614             el.sync(true);
17615             if(c){
17616                 c();
17617             }
17618         };
17619     },
17620
17621     // overridden Element method
17622     setX : function(x, a, d, c, e){
17623         this.setXY([x, this.getY()], a, d, c, e);
17624     },
17625
17626     // overridden Element method
17627     setY : function(y, a, d, c, e){
17628         this.setXY([this.getX(), y], a, d, c, e);
17629     },
17630
17631     // overridden Element method
17632     setSize : function(w, h, a, d, c, e){
17633         this.beforeAction();
17634         var cb = this.createCB(c);
17635         supr.setSize.call(this, w, h, a, d, cb, e);
17636         if(!a){
17637             cb();
17638         }
17639     },
17640
17641     // overridden Element method
17642     setWidth : function(w, a, d, c, e){
17643         this.beforeAction();
17644         var cb = this.createCB(c);
17645         supr.setWidth.call(this, w, a, d, cb, e);
17646         if(!a){
17647             cb();
17648         }
17649     },
17650
17651     // overridden Element method
17652     setHeight : function(h, a, d, c, e){
17653         this.beforeAction();
17654         var cb = this.createCB(c);
17655         supr.setHeight.call(this, h, a, d, cb, e);
17656         if(!a){
17657             cb();
17658         }
17659     },
17660
17661     // overridden Element method
17662     setBounds : function(x, y, w, h, a, d, c, e){
17663         this.beforeAction();
17664         var cb = this.createCB(c);
17665         if(!a){
17666             this.storeXY([x, y]);
17667             supr.setXY.call(this, [x, y]);
17668             supr.setSize.call(this, w, h, a, d, cb, e);
17669             cb();
17670         }else{
17671             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17672         }
17673         return this;
17674     },
17675     
17676     /**
17677      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17678      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17679      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17680      * @param {Number} zindex The new z-index to set
17681      * @return {this} The Layer
17682      */
17683     setZIndex : function(zindex){
17684         this.zindex = zindex;
17685         this.setStyle("z-index", zindex + 2);
17686         if(this.shadow){
17687             this.shadow.setZIndex(zindex + 1);
17688         }
17689         if(this.shim){
17690             this.shim.setStyle("z-index", zindex);
17691         }
17692     }
17693 });
17694 })();/*
17695  * Original code for Roojs - LGPL
17696  * <script type="text/javascript">
17697  */
17698  
17699 /**
17700  * @class Roo.XComponent
17701  * A delayed Element creator...
17702  * Or a way to group chunks of interface together.
17703  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17704  *  used in conjunction with XComponent.build() it will create an instance of each element,
17705  *  then call addxtype() to build the User interface.
17706  * 
17707  * Mypart.xyx = new Roo.XComponent({
17708
17709     parent : 'Mypart.xyz', // empty == document.element.!!
17710     order : '001',
17711     name : 'xxxx'
17712     region : 'xxxx'
17713     disabled : function() {} 
17714      
17715     tree : function() { // return an tree of xtype declared components
17716         var MODULE = this;
17717         return 
17718         {
17719             xtype : 'NestedLayoutPanel',
17720             // technicall
17721         }
17722      ]
17723  *})
17724  *
17725  *
17726  * It can be used to build a big heiracy, with parent etc.
17727  * or you can just use this to render a single compoent to a dom element
17728  * MYPART.render(Roo.Element | String(id) | dom_element )
17729  *
17730  *
17731  * Usage patterns.
17732  *
17733  * Classic Roo
17734  *
17735  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17736  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17737  *
17738  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17739  *
17740  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17741  * - if mulitple topModules exist, the last one is defined as the top module.
17742  *
17743  * Embeded Roo
17744  * 
17745  * When the top level or multiple modules are to embedded into a existing HTML page,
17746  * the parent element can container '#id' of the element where the module will be drawn.
17747  *
17748  * Bootstrap Roo
17749  *
17750  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17751  * it relies more on a include mechanism, where sub modules are included into an outer page.
17752  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17753  * 
17754  * Bootstrap Roo Included elements
17755  *
17756  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17757  * hence confusing the component builder as it thinks there are multiple top level elements. 
17758  *
17759  * String Over-ride & Translations
17760  *
17761  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17762  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17763  * are needed. @see Roo.XComponent.overlayString  
17764  * 
17765  * 
17766  * 
17767  * @extends Roo.util.Observable
17768  * @constructor
17769  * @param cfg {Object} configuration of component
17770  * 
17771  */
17772 Roo.XComponent = function(cfg) {
17773     Roo.apply(this, cfg);
17774     this.addEvents({ 
17775         /**
17776              * @event built
17777              * Fires when this the componnt is built
17778              * @param {Roo.XComponent} c the component
17779              */
17780         'built' : true
17781         
17782     });
17783     this.region = this.region || 'center'; // default..
17784     Roo.XComponent.register(this);
17785     this.modules = false;
17786     this.el = false; // where the layout goes..
17787     
17788     
17789 }
17790 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17791     /**
17792      * @property el
17793      * The created element (with Roo.factory())
17794      * @type {Roo.Layout}
17795      */
17796     el  : false,
17797     
17798     /**
17799      * @property el
17800      * for BC  - use el in new code
17801      * @type {Roo.Layout}
17802      */
17803     panel : false,
17804     
17805     /**
17806      * @property layout
17807      * for BC  - use el in new code
17808      * @type {Roo.Layout}
17809      */
17810     layout : false,
17811     
17812      /**
17813      * @cfg {Function|boolean} disabled
17814      * If this module is disabled by some rule, return true from the funtion
17815      */
17816     disabled : false,
17817     
17818     /**
17819      * @cfg {String} parent 
17820      * Name of parent element which it get xtype added to..
17821      */
17822     parent: false,
17823     
17824     /**
17825      * @cfg {String} order
17826      * Used to set the order in which elements are created (usefull for multiple tabs)
17827      */
17828     
17829     order : false,
17830     /**
17831      * @cfg {String} name
17832      * String to display while loading.
17833      */
17834     name : false,
17835     /**
17836      * @cfg {String} region
17837      * Region to render component to (defaults to center)
17838      */
17839     region : 'center',
17840     
17841     /**
17842      * @cfg {Array} items
17843      * A single item array - the first element is the root of the tree..
17844      * It's done this way to stay compatible with the Xtype system...
17845      */
17846     items : false,
17847     
17848     /**
17849      * @property _tree
17850      * The method that retuns the tree of parts that make up this compoennt 
17851      * @type {function}
17852      */
17853     _tree  : false,
17854     
17855      /**
17856      * render
17857      * render element to dom or tree
17858      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17859      */
17860     
17861     render : function(el)
17862     {
17863         
17864         el = el || false;
17865         var hp = this.parent ? 1 : 0;
17866         Roo.debug &&  Roo.log(this);
17867         
17868         var tree = this._tree ? this._tree() : this.tree();
17869
17870         
17871         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17872             // if parent is a '#.....' string, then let's use that..
17873             var ename = this.parent.substr(1);
17874             this.parent = false;
17875             Roo.debug && Roo.log(ename);
17876             switch (ename) {
17877                 case 'bootstrap-body':
17878                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17879                         // this is the BorderLayout standard?
17880                        this.parent = { el : true };
17881                        break;
17882                     }
17883                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17884                         // need to insert stuff...
17885                         this.parent =  {
17886                              el : new Roo.bootstrap.layout.Border({
17887                                  el : document.body, 
17888                      
17889                                  center: {
17890                                     titlebar: false,
17891                                     autoScroll:false,
17892                                     closeOnTab: true,
17893                                     tabPosition: 'top',
17894                                       //resizeTabs: true,
17895                                     alwaysShowTabs: true,
17896                                     hideTabs: false
17897                                      //minTabWidth: 140
17898                                  }
17899                              })
17900                         
17901                          };
17902                          break;
17903                     }
17904                          
17905                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17906                         this.parent = { el :  new  Roo.bootstrap.Body() };
17907                         Roo.debug && Roo.log("setting el to doc body");
17908                          
17909                     } else {
17910                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17911                     }
17912                     break;
17913                 case 'bootstrap':
17914                     this.parent = { el : true};
17915                     // fall through
17916                 default:
17917                     el = Roo.get(ename);
17918                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17919                         this.parent = { el : true};
17920                     }
17921                     
17922                     break;
17923             }
17924                 
17925             
17926             if (!el && !this.parent) {
17927                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17928                 return;
17929             }
17930         }
17931         
17932         Roo.debug && Roo.log("EL:");
17933         Roo.debug && Roo.log(el);
17934         Roo.debug && Roo.log("this.parent.el:");
17935         Roo.debug && Roo.log(this.parent.el);
17936         
17937
17938         // altertive root elements ??? - we need a better way to indicate these.
17939         var is_alt = Roo.XComponent.is_alt ||
17940                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17941                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17942                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17943         
17944         
17945         
17946         if (!this.parent && is_alt) {
17947             //el = Roo.get(document.body);
17948             this.parent = { el : true };
17949         }
17950             
17951             
17952         
17953         if (!this.parent) {
17954             
17955             Roo.debug && Roo.log("no parent - creating one");
17956             
17957             el = el ? Roo.get(el) : false;      
17958             
17959             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17960                 
17961                 this.parent =  {
17962                     el : new Roo.bootstrap.layout.Border({
17963                         el: el || document.body,
17964                     
17965                         center: {
17966                             titlebar: false,
17967                             autoScroll:false,
17968                             closeOnTab: true,
17969                             tabPosition: 'top',
17970                              //resizeTabs: true,
17971                             alwaysShowTabs: false,
17972                             hideTabs: true,
17973                             minTabWidth: 140,
17974                             overflow: 'visible'
17975                          }
17976                      })
17977                 };
17978             } else {
17979             
17980                 // it's a top level one..
17981                 this.parent =  {
17982                     el : new Roo.BorderLayout(el || document.body, {
17983                         center: {
17984                             titlebar: false,
17985                             autoScroll:false,
17986                             closeOnTab: true,
17987                             tabPosition: 'top',
17988                              //resizeTabs: true,
17989                             alwaysShowTabs: el && hp? false :  true,
17990                             hideTabs: el || !hp ? true :  false,
17991                             minTabWidth: 140
17992                          }
17993                     })
17994                 };
17995             }
17996         }
17997         
17998         if (!this.parent.el) {
17999                 // probably an old style ctor, which has been disabled.
18000                 return;
18001
18002         }
18003                 // The 'tree' method is  '_tree now' 
18004             
18005         tree.region = tree.region || this.region;
18006         var is_body = false;
18007         if (this.parent.el === true) {
18008             // bootstrap... - body..
18009             if (el) {
18010                 tree.el = el;
18011             }
18012             this.parent.el = Roo.factory(tree);
18013             is_body = true;
18014         }
18015         
18016         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18017         this.fireEvent('built', this);
18018         
18019         this.panel = this.el;
18020         this.layout = this.panel.layout;
18021         this.parentLayout = this.parent.layout  || false;  
18022          
18023     }
18024     
18025 });
18026
18027 Roo.apply(Roo.XComponent, {
18028     /**
18029      * @property  hideProgress
18030      * true to disable the building progress bar.. usefull on single page renders.
18031      * @type Boolean
18032      */
18033     hideProgress : false,
18034     /**
18035      * @property  buildCompleted
18036      * True when the builder has completed building the interface.
18037      * @type Boolean
18038      */
18039     buildCompleted : false,
18040      
18041     /**
18042      * @property  topModule
18043      * the upper most module - uses document.element as it's constructor.
18044      * @type Object
18045      */
18046      
18047     topModule  : false,
18048       
18049     /**
18050      * @property  modules
18051      * array of modules to be created by registration system.
18052      * @type {Array} of Roo.XComponent
18053      */
18054     
18055     modules : [],
18056     /**
18057      * @property  elmodules
18058      * array of modules to be created by which use #ID 
18059      * @type {Array} of Roo.XComponent
18060      */
18061      
18062     elmodules : [],
18063
18064      /**
18065      * @property  is_alt
18066      * Is an alternative Root - normally used by bootstrap or other systems,
18067      *    where the top element in the tree can wrap 'body' 
18068      * @type {boolean}  (default false)
18069      */
18070      
18071     is_alt : false,
18072     /**
18073      * @property  build_from_html
18074      * Build elements from html - used by bootstrap HTML stuff 
18075      *    - this is cleared after build is completed
18076      * @type {boolean}    (default false)
18077      */
18078      
18079     build_from_html : false,
18080     /**
18081      * Register components to be built later.
18082      *
18083      * This solves the following issues
18084      * - Building is not done on page load, but after an authentication process has occured.
18085      * - Interface elements are registered on page load
18086      * - Parent Interface elements may not be loaded before child, so this handles that..
18087      * 
18088      *
18089      * example:
18090      * 
18091      * MyApp.register({
18092           order : '000001',
18093           module : 'Pman.Tab.projectMgr',
18094           region : 'center',
18095           parent : 'Pman.layout',
18096           disabled : false,  // or use a function..
18097         })
18098      
18099      * * @param {Object} details about module
18100      */
18101     register : function(obj) {
18102                 
18103         Roo.XComponent.event.fireEvent('register', obj);
18104         switch(typeof(obj.disabled) ) {
18105                 
18106             case 'undefined':
18107                 break;
18108             
18109             case 'function':
18110                 if ( obj.disabled() ) {
18111                         return;
18112                 }
18113                 break;
18114             
18115             default:
18116                 if (obj.disabled || obj.region == '#disabled') {
18117                         return;
18118                 }
18119                 break;
18120         }
18121                 
18122         this.modules.push(obj);
18123          
18124     },
18125     /**
18126      * convert a string to an object..
18127      * eg. 'AAA.BBB' -> finds AAA.BBB
18128
18129      */
18130     
18131     toObject : function(str)
18132     {
18133         if (!str || typeof(str) == 'object') {
18134             return str;
18135         }
18136         if (str.substring(0,1) == '#') {
18137             return str;
18138         }
18139
18140         var ar = str.split('.');
18141         var rt, o;
18142         rt = ar.shift();
18143             /** eval:var:o */
18144         try {
18145             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18146         } catch (e) {
18147             throw "Module not found : " + str;
18148         }
18149         
18150         if (o === false) {
18151             throw "Module not found : " + str;
18152         }
18153         Roo.each(ar, function(e) {
18154             if (typeof(o[e]) == 'undefined') {
18155                 throw "Module not found : " + str;
18156             }
18157             o = o[e];
18158         });
18159         
18160         return o;
18161         
18162     },
18163     
18164     
18165     /**
18166      * move modules into their correct place in the tree..
18167      * 
18168      */
18169     preBuild : function ()
18170     {
18171         var _t = this;
18172         Roo.each(this.modules , function (obj)
18173         {
18174             Roo.XComponent.event.fireEvent('beforebuild', obj);
18175             
18176             var opar = obj.parent;
18177             try { 
18178                 obj.parent = this.toObject(opar);
18179             } catch(e) {
18180                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18181                 return;
18182             }
18183             
18184             if (!obj.parent) {
18185                 Roo.debug && Roo.log("GOT top level module");
18186                 Roo.debug && Roo.log(obj);
18187                 obj.modules = new Roo.util.MixedCollection(false, 
18188                     function(o) { return o.order + '' }
18189                 );
18190                 this.topModule = obj;
18191                 return;
18192             }
18193                         // parent is a string (usually a dom element name..)
18194             if (typeof(obj.parent) == 'string') {
18195                 this.elmodules.push(obj);
18196                 return;
18197             }
18198             if (obj.parent.constructor != Roo.XComponent) {
18199                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18200             }
18201             if (!obj.parent.modules) {
18202                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18203                     function(o) { return o.order + '' }
18204                 );
18205             }
18206             if (obj.parent.disabled) {
18207                 obj.disabled = true;
18208             }
18209             obj.parent.modules.add(obj);
18210         }, this);
18211     },
18212     
18213      /**
18214      * make a list of modules to build.
18215      * @return {Array} list of modules. 
18216      */ 
18217     
18218     buildOrder : function()
18219     {
18220         var _this = this;
18221         var cmp = function(a,b) {   
18222             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18223         };
18224         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18225             throw "No top level modules to build";
18226         }
18227         
18228         // make a flat list in order of modules to build.
18229         var mods = this.topModule ? [ this.topModule ] : [];
18230                 
18231         
18232         // elmodules (is a list of DOM based modules )
18233         Roo.each(this.elmodules, function(e) {
18234             mods.push(e);
18235             if (!this.topModule &&
18236                 typeof(e.parent) == 'string' &&
18237                 e.parent.substring(0,1) == '#' &&
18238                 Roo.get(e.parent.substr(1))
18239                ) {
18240                 
18241                 _this.topModule = e;
18242             }
18243             
18244         });
18245
18246         
18247         // add modules to their parents..
18248         var addMod = function(m) {
18249             Roo.debug && Roo.log("build Order: add: " + m.name);
18250                 
18251             mods.push(m);
18252             if (m.modules && !m.disabled) {
18253                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18254                 m.modules.keySort('ASC',  cmp );
18255                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18256     
18257                 m.modules.each(addMod);
18258             } else {
18259                 Roo.debug && Roo.log("build Order: no child modules");
18260             }
18261             // not sure if this is used any more..
18262             if (m.finalize) {
18263                 m.finalize.name = m.name + " (clean up) ";
18264                 mods.push(m.finalize);
18265             }
18266             
18267         }
18268         if (this.topModule && this.topModule.modules) { 
18269             this.topModule.modules.keySort('ASC',  cmp );
18270             this.topModule.modules.each(addMod);
18271         } 
18272         return mods;
18273     },
18274     
18275      /**
18276      * Build the registered modules.
18277      * @param {Object} parent element.
18278      * @param {Function} optional method to call after module has been added.
18279      * 
18280      */ 
18281    
18282     build : function(opts) 
18283     {
18284         
18285         if (typeof(opts) != 'undefined') {
18286             Roo.apply(this,opts);
18287         }
18288         
18289         this.preBuild();
18290         var mods = this.buildOrder();
18291       
18292         //this.allmods = mods;
18293         //Roo.debug && Roo.log(mods);
18294         //return;
18295         if (!mods.length) { // should not happen
18296             throw "NO modules!!!";
18297         }
18298         
18299         
18300         var msg = "Building Interface...";
18301         // flash it up as modal - so we store the mask!?
18302         if (!this.hideProgress && Roo.MessageBox) {
18303             Roo.MessageBox.show({ title: 'loading' });
18304             Roo.MessageBox.show({
18305                title: "Please wait...",
18306                msg: msg,
18307                width:450,
18308                progress:true,
18309                buttons : false,
18310                closable:false,
18311                modal: false
18312               
18313             });
18314         }
18315         var total = mods.length;
18316         
18317         var _this = this;
18318         var progressRun = function() {
18319             if (!mods.length) {
18320                 Roo.debug && Roo.log('hide?');
18321                 if (!this.hideProgress && Roo.MessageBox) {
18322                     Roo.MessageBox.hide();
18323                 }
18324                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18325                 
18326                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18327                 
18328                 // THE END...
18329                 return false;   
18330             }
18331             
18332             var m = mods.shift();
18333             
18334             
18335             Roo.debug && Roo.log(m);
18336             // not sure if this is supported any more.. - modules that are are just function
18337             if (typeof(m) == 'function') { 
18338                 m.call(this);
18339                 return progressRun.defer(10, _this);
18340             } 
18341             
18342             
18343             msg = "Building Interface " + (total  - mods.length) + 
18344                     " of " + total + 
18345                     (m.name ? (' - ' + m.name) : '');
18346                         Roo.debug && Roo.log(msg);
18347             if (!_this.hideProgress &&  Roo.MessageBox) { 
18348                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18349             }
18350             
18351          
18352             // is the module disabled?
18353             var disabled = (typeof(m.disabled) == 'function') ?
18354                 m.disabled.call(m.module.disabled) : m.disabled;    
18355             
18356             
18357             if (disabled) {
18358                 return progressRun(); // we do not update the display!
18359             }
18360             
18361             // now build 
18362             
18363                         
18364                         
18365             m.render();
18366             // it's 10 on top level, and 1 on others??? why...
18367             return progressRun.defer(10, _this);
18368              
18369         }
18370         progressRun.defer(1, _this);
18371      
18372         
18373         
18374     },
18375     /**
18376      * Overlay a set of modified strings onto a component
18377      * This is dependant on our builder exporting the strings and 'named strings' elements.
18378      * 
18379      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18380      * @param {Object} associative array of 'named' string and it's new value.
18381      * 
18382      */
18383         overlayStrings : function( component, strings )
18384     {
18385         if (typeof(component['_named_strings']) == 'undefined') {
18386             throw "ERROR: component does not have _named_strings";
18387         }
18388         for ( var k in strings ) {
18389             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18390             if (md !== false) {
18391                 component['_strings'][md] = strings[k];
18392             } else {
18393                 Roo.log('could not find named string: ' + k + ' in');
18394                 Roo.log(component);
18395             }
18396             
18397         }
18398         
18399     },
18400     
18401         
18402         /**
18403          * Event Object.
18404          *
18405          *
18406          */
18407         event: false, 
18408     /**
18409          * wrapper for event.on - aliased later..  
18410          * Typically use to register a event handler for register:
18411          *
18412          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18413          *
18414          */
18415     on : false
18416    
18417     
18418     
18419 });
18420
18421 Roo.XComponent.event = new Roo.util.Observable({
18422                 events : { 
18423                         /**
18424                          * @event register
18425                          * Fires when an Component is registered,
18426                          * set the disable property on the Component to stop registration.
18427                          * @param {Roo.XComponent} c the component being registerd.
18428                          * 
18429                          */
18430                         'register' : true,
18431             /**
18432                          * @event beforebuild
18433                          * Fires before each Component is built
18434                          * can be used to apply permissions.
18435                          * @param {Roo.XComponent} c the component being registerd.
18436                          * 
18437                          */
18438                         'beforebuild' : true,
18439                         /**
18440                          * @event buildcomplete
18441                          * Fires on the top level element when all elements have been built
18442                          * @param {Roo.XComponent} the top level component.
18443                          */
18444                         'buildcomplete' : true
18445                         
18446                 }
18447 });
18448
18449 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18450  //
18451  /**
18452  * marked - a markdown parser
18453  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18454  * https://github.com/chjj/marked
18455  */
18456
18457
18458 /**
18459  *
18460  * Roo.Markdown - is a very crude wrapper around marked..
18461  *
18462  * usage:
18463  * 
18464  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18465  * 
18466  * Note: move the sample code to the bottom of this
18467  * file before uncommenting it.
18468  *
18469  */
18470
18471 Roo.Markdown = {};
18472 Roo.Markdown.toHtml = function(text) {
18473     
18474     var c = new Roo.Markdown.marked.setOptions({
18475             renderer: new Roo.Markdown.marked.Renderer(),
18476             gfm: true,
18477             tables: true,
18478             breaks: false,
18479             pedantic: false,
18480             sanitize: false,
18481             smartLists: true,
18482             smartypants: false
18483           });
18484     // A FEW HACKS!!?
18485     
18486     text = text.replace(/\\\n/g,' ');
18487     return Roo.Markdown.marked(text);
18488 };
18489 //
18490 // converter
18491 //
18492 // Wraps all "globals" so that the only thing
18493 // exposed is makeHtml().
18494 //
18495 (function() {
18496     
18497      /**
18498          * eval:var:escape
18499          * eval:var:unescape
18500          * eval:var:replace
18501          */
18502       
18503     /**
18504      * Helpers
18505      */
18506     
18507     var escape = function (html, encode) {
18508       return html
18509         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18510         .replace(/</g, '&lt;')
18511         .replace(/>/g, '&gt;')
18512         .replace(/"/g, '&quot;')
18513         .replace(/'/g, '&#39;');
18514     }
18515     
18516     var unescape = function (html) {
18517         // explicitly match decimal, hex, and named HTML entities 
18518       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18519         n = n.toLowerCase();
18520         if (n === 'colon') { return ':'; }
18521         if (n.charAt(0) === '#') {
18522           return n.charAt(1) === 'x'
18523             ? String.fromCharCode(parseInt(n.substring(2), 16))
18524             : String.fromCharCode(+n.substring(1));
18525         }
18526         return '';
18527       });
18528     }
18529     
18530     var replace = function (regex, opt) {
18531       regex = regex.source;
18532       opt = opt || '';
18533       return function self(name, val) {
18534         if (!name) { return new RegExp(regex, opt); }
18535         val = val.source || val;
18536         val = val.replace(/(^|[^\[])\^/g, '$1');
18537         regex = regex.replace(name, val);
18538         return self;
18539       };
18540     }
18541
18542
18543          /**
18544          * eval:var:noop
18545     */
18546     var noop = function () {}
18547     noop.exec = noop;
18548     
18549          /**
18550          * eval:var:merge
18551     */
18552     var merge = function (obj) {
18553       var i = 1
18554         , target
18555         , key;
18556     
18557       for (; i < arguments.length; i++) {
18558         target = arguments[i];
18559         for (key in target) {
18560           if (Object.prototype.hasOwnProperty.call(target, key)) {
18561             obj[key] = target[key];
18562           }
18563         }
18564       }
18565     
18566       return obj;
18567     }
18568     
18569     
18570     /**
18571      * Block-Level Grammar
18572      */
18573     
18574     
18575     
18576     
18577     var block = {
18578       newline: /^\n+/,
18579       code: /^( {4}[^\n]+\n*)+/,
18580       fences: noop,
18581       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18582       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18583       nptable: noop,
18584       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18585       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18586       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18587       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18588       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18589       table: noop,
18590       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18591       text: /^[^\n]+/
18592     };
18593     
18594     block.bullet = /(?:[*+-]|\d+\.)/;
18595     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18596     block.item = replace(block.item, 'gm')
18597       (/bull/g, block.bullet)
18598       ();
18599     
18600     block.list = replace(block.list)
18601       (/bull/g, block.bullet)
18602       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18603       ('def', '\\n+(?=' + block.def.source + ')')
18604       ();
18605     
18606     block.blockquote = replace(block.blockquote)
18607       ('def', block.def)
18608       ();
18609     
18610     block._tag = '(?!(?:'
18611       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18612       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18613       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18614     
18615     block.html = replace(block.html)
18616       ('comment', /<!--[\s\S]*?-->/)
18617       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18618       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18619       (/tag/g, block._tag)
18620       ();
18621     
18622     block.paragraph = replace(block.paragraph)
18623       ('hr', block.hr)
18624       ('heading', block.heading)
18625       ('lheading', block.lheading)
18626       ('blockquote', block.blockquote)
18627       ('tag', '<' + block._tag)
18628       ('def', block.def)
18629       ();
18630     
18631     /**
18632      * Normal Block Grammar
18633      */
18634     
18635     block.normal = merge({}, block);
18636     
18637     /**
18638      * GFM Block Grammar
18639      */
18640     
18641     block.gfm = merge({}, block.normal, {
18642       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18643       paragraph: /^/,
18644       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18645     });
18646     
18647     block.gfm.paragraph = replace(block.paragraph)
18648       ('(?!', '(?!'
18649         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18650         + block.list.source.replace('\\1', '\\3') + '|')
18651       ();
18652     
18653     /**
18654      * GFM + Tables Block Grammar
18655      */
18656     
18657     block.tables = merge({}, block.gfm, {
18658       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18659       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18660     });
18661     
18662     /**
18663      * Block Lexer
18664      */
18665     
18666     var Lexer = function (options) {
18667       this.tokens = [];
18668       this.tokens.links = {};
18669       this.options = options || marked.defaults;
18670       this.rules = block.normal;
18671     
18672       if (this.options.gfm) {
18673         if (this.options.tables) {
18674           this.rules = block.tables;
18675         } else {
18676           this.rules = block.gfm;
18677         }
18678       }
18679     }
18680     
18681     /**
18682      * Expose Block Rules
18683      */
18684     
18685     Lexer.rules = block;
18686     
18687     /**
18688      * Static Lex Method
18689      */
18690     
18691     Lexer.lex = function(src, options) {
18692       var lexer = new Lexer(options);
18693       return lexer.lex(src);
18694     };
18695     
18696     /**
18697      * Preprocessing
18698      */
18699     
18700     Lexer.prototype.lex = function(src) {
18701       src = src
18702         .replace(/\r\n|\r/g, '\n')
18703         .replace(/\t/g, '    ')
18704         .replace(/\u00a0/g, ' ')
18705         .replace(/\u2424/g, '\n');
18706     
18707       return this.token(src, true);
18708     };
18709     
18710     /**
18711      * Lexing
18712      */
18713     
18714     Lexer.prototype.token = function(src, top, bq) {
18715       var src = src.replace(/^ +$/gm, '')
18716         , next
18717         , loose
18718         , cap
18719         , bull
18720         , b
18721         , item
18722         , space
18723         , i
18724         , l;
18725     
18726       while (src) {
18727         // newline
18728         if (cap = this.rules.newline.exec(src)) {
18729           src = src.substring(cap[0].length);
18730           if (cap[0].length > 1) {
18731             this.tokens.push({
18732               type: 'space'
18733             });
18734           }
18735         }
18736     
18737         // code
18738         if (cap = this.rules.code.exec(src)) {
18739           src = src.substring(cap[0].length);
18740           cap = cap[0].replace(/^ {4}/gm, '');
18741           this.tokens.push({
18742             type: 'code',
18743             text: !this.options.pedantic
18744               ? cap.replace(/\n+$/, '')
18745               : cap
18746           });
18747           continue;
18748         }
18749     
18750         // fences (gfm)
18751         if (cap = this.rules.fences.exec(src)) {
18752           src = src.substring(cap[0].length);
18753           this.tokens.push({
18754             type: 'code',
18755             lang: cap[2],
18756             text: cap[3] || ''
18757           });
18758           continue;
18759         }
18760     
18761         // heading
18762         if (cap = this.rules.heading.exec(src)) {
18763           src = src.substring(cap[0].length);
18764           this.tokens.push({
18765             type: 'heading',
18766             depth: cap[1].length,
18767             text: cap[2]
18768           });
18769           continue;
18770         }
18771     
18772         // table no leading pipe (gfm)
18773         if (top && (cap = this.rules.nptable.exec(src))) {
18774           src = src.substring(cap[0].length);
18775     
18776           item = {
18777             type: 'table',
18778             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18779             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18780             cells: cap[3].replace(/\n$/, '').split('\n')
18781           };
18782     
18783           for (i = 0; i < item.align.length; i++) {
18784             if (/^ *-+: *$/.test(item.align[i])) {
18785               item.align[i] = 'right';
18786             } else if (/^ *:-+: *$/.test(item.align[i])) {
18787               item.align[i] = 'center';
18788             } else if (/^ *:-+ *$/.test(item.align[i])) {
18789               item.align[i] = 'left';
18790             } else {
18791               item.align[i] = null;
18792             }
18793           }
18794     
18795           for (i = 0; i < item.cells.length; i++) {
18796             item.cells[i] = item.cells[i].split(/ *\| */);
18797           }
18798     
18799           this.tokens.push(item);
18800     
18801           continue;
18802         }
18803     
18804         // lheading
18805         if (cap = this.rules.lheading.exec(src)) {
18806           src = src.substring(cap[0].length);
18807           this.tokens.push({
18808             type: 'heading',
18809             depth: cap[2] === '=' ? 1 : 2,
18810             text: cap[1]
18811           });
18812           continue;
18813         }
18814     
18815         // hr
18816         if (cap = this.rules.hr.exec(src)) {
18817           src = src.substring(cap[0].length);
18818           this.tokens.push({
18819             type: 'hr'
18820           });
18821           continue;
18822         }
18823     
18824         // blockquote
18825         if (cap = this.rules.blockquote.exec(src)) {
18826           src = src.substring(cap[0].length);
18827     
18828           this.tokens.push({
18829             type: 'blockquote_start'
18830           });
18831     
18832           cap = cap[0].replace(/^ *> ?/gm, '');
18833     
18834           // Pass `top` to keep the current
18835           // "toplevel" state. This is exactly
18836           // how markdown.pl works.
18837           this.token(cap, top, true);
18838     
18839           this.tokens.push({
18840             type: 'blockquote_end'
18841           });
18842     
18843           continue;
18844         }
18845     
18846         // list
18847         if (cap = this.rules.list.exec(src)) {
18848           src = src.substring(cap[0].length);
18849           bull = cap[2];
18850     
18851           this.tokens.push({
18852             type: 'list_start',
18853             ordered: bull.length > 1
18854           });
18855     
18856           // Get each top-level item.
18857           cap = cap[0].match(this.rules.item);
18858     
18859           next = false;
18860           l = cap.length;
18861           i = 0;
18862     
18863           for (; i < l; i++) {
18864             item = cap[i];
18865     
18866             // Remove the list item's bullet
18867             // so it is seen as the next token.
18868             space = item.length;
18869             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18870     
18871             // Outdent whatever the
18872             // list item contains. Hacky.
18873             if (~item.indexOf('\n ')) {
18874               space -= item.length;
18875               item = !this.options.pedantic
18876                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18877                 : item.replace(/^ {1,4}/gm, '');
18878             }
18879     
18880             // Determine whether the next list item belongs here.
18881             // Backpedal if it does not belong in this list.
18882             if (this.options.smartLists && i !== l - 1) {
18883               b = block.bullet.exec(cap[i + 1])[0];
18884               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18885                 src = cap.slice(i + 1).join('\n') + src;
18886                 i = l - 1;
18887               }
18888             }
18889     
18890             // Determine whether item is loose or not.
18891             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18892             // for discount behavior.
18893             loose = next || /\n\n(?!\s*$)/.test(item);
18894             if (i !== l - 1) {
18895               next = item.charAt(item.length - 1) === '\n';
18896               if (!loose) { loose = next; }
18897             }
18898     
18899             this.tokens.push({
18900               type: loose
18901                 ? 'loose_item_start'
18902                 : 'list_item_start'
18903             });
18904     
18905             // Recurse.
18906             this.token(item, false, bq);
18907     
18908             this.tokens.push({
18909               type: 'list_item_end'
18910             });
18911           }
18912     
18913           this.tokens.push({
18914             type: 'list_end'
18915           });
18916     
18917           continue;
18918         }
18919     
18920         // html
18921         if (cap = this.rules.html.exec(src)) {
18922           src = src.substring(cap[0].length);
18923           this.tokens.push({
18924             type: this.options.sanitize
18925               ? 'paragraph'
18926               : 'html',
18927             pre: !this.options.sanitizer
18928               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18929             text: cap[0]
18930           });
18931           continue;
18932         }
18933     
18934         // def
18935         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18936           src = src.substring(cap[0].length);
18937           this.tokens.links[cap[1].toLowerCase()] = {
18938             href: cap[2],
18939             title: cap[3]
18940           };
18941           continue;
18942         }
18943     
18944         // table (gfm)
18945         if (top && (cap = this.rules.table.exec(src))) {
18946           src = src.substring(cap[0].length);
18947     
18948           item = {
18949             type: 'table',
18950             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18951             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18952             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18953           };
18954     
18955           for (i = 0; i < item.align.length; i++) {
18956             if (/^ *-+: *$/.test(item.align[i])) {
18957               item.align[i] = 'right';
18958             } else if (/^ *:-+: *$/.test(item.align[i])) {
18959               item.align[i] = 'center';
18960             } else if (/^ *:-+ *$/.test(item.align[i])) {
18961               item.align[i] = 'left';
18962             } else {
18963               item.align[i] = null;
18964             }
18965           }
18966     
18967           for (i = 0; i < item.cells.length; i++) {
18968             item.cells[i] = item.cells[i]
18969               .replace(/^ *\| *| *\| *$/g, '')
18970               .split(/ *\| */);
18971           }
18972     
18973           this.tokens.push(item);
18974     
18975           continue;
18976         }
18977     
18978         // top-level paragraph
18979         if (top && (cap = this.rules.paragraph.exec(src))) {
18980           src = src.substring(cap[0].length);
18981           this.tokens.push({
18982             type: 'paragraph',
18983             text: cap[1].charAt(cap[1].length - 1) === '\n'
18984               ? cap[1].slice(0, -1)
18985               : cap[1]
18986           });
18987           continue;
18988         }
18989     
18990         // text
18991         if (cap = this.rules.text.exec(src)) {
18992           // Top-level should never reach here.
18993           src = src.substring(cap[0].length);
18994           this.tokens.push({
18995             type: 'text',
18996             text: cap[0]
18997           });
18998           continue;
18999         }
19000     
19001         if (src) {
19002           throw new
19003             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19004         }
19005       }
19006     
19007       return this.tokens;
19008     };
19009     
19010     /**
19011      * Inline-Level Grammar
19012      */
19013     
19014     var inline = {
19015       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19016       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19017       url: noop,
19018       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19019       link: /^!?\[(inside)\]\(href\)/,
19020       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19021       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19022       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19023       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19024       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19025       br: /^ {2,}\n(?!\s*$)/,
19026       del: noop,
19027       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19028     };
19029     
19030     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19031     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19032     
19033     inline.link = replace(inline.link)
19034       ('inside', inline._inside)
19035       ('href', inline._href)
19036       ();
19037     
19038     inline.reflink = replace(inline.reflink)
19039       ('inside', inline._inside)
19040       ();
19041     
19042     /**
19043      * Normal Inline Grammar
19044      */
19045     
19046     inline.normal = merge({}, inline);
19047     
19048     /**
19049      * Pedantic Inline Grammar
19050      */
19051     
19052     inline.pedantic = merge({}, inline.normal, {
19053       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19054       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19055     });
19056     
19057     /**
19058      * GFM Inline Grammar
19059      */
19060     
19061     inline.gfm = merge({}, inline.normal, {
19062       escape: replace(inline.escape)('])', '~|])')(),
19063       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19064       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19065       text: replace(inline.text)
19066         (']|', '~]|')
19067         ('|', '|https?://|')
19068         ()
19069     });
19070     
19071     /**
19072      * GFM + Line Breaks Inline Grammar
19073      */
19074     
19075     inline.breaks = merge({}, inline.gfm, {
19076       br: replace(inline.br)('{2,}', '*')(),
19077       text: replace(inline.gfm.text)('{2,}', '*')()
19078     });
19079     
19080     /**
19081      * Inline Lexer & Compiler
19082      */
19083     
19084     var InlineLexer  = function (links, options) {
19085       this.options = options || marked.defaults;
19086       this.links = links;
19087       this.rules = inline.normal;
19088       this.renderer = this.options.renderer || new Renderer;
19089       this.renderer.options = this.options;
19090     
19091       if (!this.links) {
19092         throw new
19093           Error('Tokens array requires a `links` property.');
19094       }
19095     
19096       if (this.options.gfm) {
19097         if (this.options.breaks) {
19098           this.rules = inline.breaks;
19099         } else {
19100           this.rules = inline.gfm;
19101         }
19102       } else if (this.options.pedantic) {
19103         this.rules = inline.pedantic;
19104       }
19105     }
19106     
19107     /**
19108      * Expose Inline Rules
19109      */
19110     
19111     InlineLexer.rules = inline;
19112     
19113     /**
19114      * Static Lexing/Compiling Method
19115      */
19116     
19117     InlineLexer.output = function(src, links, options) {
19118       var inline = new InlineLexer(links, options);
19119       return inline.output(src);
19120     };
19121     
19122     /**
19123      * Lexing/Compiling
19124      */
19125     
19126     InlineLexer.prototype.output = function(src) {
19127       var out = ''
19128         , link
19129         , text
19130         , href
19131         , cap;
19132     
19133       while (src) {
19134         // escape
19135         if (cap = this.rules.escape.exec(src)) {
19136           src = src.substring(cap[0].length);
19137           out += cap[1];
19138           continue;
19139         }
19140     
19141         // autolink
19142         if (cap = this.rules.autolink.exec(src)) {
19143           src = src.substring(cap[0].length);
19144           if (cap[2] === '@') {
19145             text = cap[1].charAt(6) === ':'
19146               ? this.mangle(cap[1].substring(7))
19147               : this.mangle(cap[1]);
19148             href = this.mangle('mailto:') + text;
19149           } else {
19150             text = escape(cap[1]);
19151             href = text;
19152           }
19153           out += this.renderer.link(href, null, text);
19154           continue;
19155         }
19156     
19157         // url (gfm)
19158         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19159           src = src.substring(cap[0].length);
19160           text = escape(cap[1]);
19161           href = text;
19162           out += this.renderer.link(href, null, text);
19163           continue;
19164         }
19165     
19166         // tag
19167         if (cap = this.rules.tag.exec(src)) {
19168           if (!this.inLink && /^<a /i.test(cap[0])) {
19169             this.inLink = true;
19170           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19171             this.inLink = false;
19172           }
19173           src = src.substring(cap[0].length);
19174           out += this.options.sanitize
19175             ? this.options.sanitizer
19176               ? this.options.sanitizer(cap[0])
19177               : escape(cap[0])
19178             : cap[0];
19179           continue;
19180         }
19181     
19182         // link
19183         if (cap = this.rules.link.exec(src)) {
19184           src = src.substring(cap[0].length);
19185           this.inLink = true;
19186           out += this.outputLink(cap, {
19187             href: cap[2],
19188             title: cap[3]
19189           });
19190           this.inLink = false;
19191           continue;
19192         }
19193     
19194         // reflink, nolink
19195         if ((cap = this.rules.reflink.exec(src))
19196             || (cap = this.rules.nolink.exec(src))) {
19197           src = src.substring(cap[0].length);
19198           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19199           link = this.links[link.toLowerCase()];
19200           if (!link || !link.href) {
19201             out += cap[0].charAt(0);
19202             src = cap[0].substring(1) + src;
19203             continue;
19204           }
19205           this.inLink = true;
19206           out += this.outputLink(cap, link);
19207           this.inLink = false;
19208           continue;
19209         }
19210     
19211         // strong
19212         if (cap = this.rules.strong.exec(src)) {
19213           src = src.substring(cap[0].length);
19214           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19215           continue;
19216         }
19217     
19218         // em
19219         if (cap = this.rules.em.exec(src)) {
19220           src = src.substring(cap[0].length);
19221           out += this.renderer.em(this.output(cap[2] || cap[1]));
19222           continue;
19223         }
19224     
19225         // code
19226         if (cap = this.rules.code.exec(src)) {
19227           src = src.substring(cap[0].length);
19228           out += this.renderer.codespan(escape(cap[2], true));
19229           continue;
19230         }
19231     
19232         // br
19233         if (cap = this.rules.br.exec(src)) {
19234           src = src.substring(cap[0].length);
19235           out += this.renderer.br();
19236           continue;
19237         }
19238     
19239         // del (gfm)
19240         if (cap = this.rules.del.exec(src)) {
19241           src = src.substring(cap[0].length);
19242           out += this.renderer.del(this.output(cap[1]));
19243           continue;
19244         }
19245     
19246         // text
19247         if (cap = this.rules.text.exec(src)) {
19248           src = src.substring(cap[0].length);
19249           out += this.renderer.text(escape(this.smartypants(cap[0])));
19250           continue;
19251         }
19252     
19253         if (src) {
19254           throw new
19255             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19256         }
19257       }
19258     
19259       return out;
19260     };
19261     
19262     /**
19263      * Compile Link
19264      */
19265     
19266     InlineLexer.prototype.outputLink = function(cap, link) {
19267       var href = escape(link.href)
19268         , title = link.title ? escape(link.title) : null;
19269     
19270       return cap[0].charAt(0) !== '!'
19271         ? this.renderer.link(href, title, this.output(cap[1]))
19272         : this.renderer.image(href, title, escape(cap[1]));
19273     };
19274     
19275     /**
19276      * Smartypants Transformations
19277      */
19278     
19279     InlineLexer.prototype.smartypants = function(text) {
19280       if (!this.options.smartypants)  { return text; }
19281       return text
19282         // em-dashes
19283         .replace(/---/g, '\u2014')
19284         // en-dashes
19285         .replace(/--/g, '\u2013')
19286         // opening singles
19287         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19288         // closing singles & apostrophes
19289         .replace(/'/g, '\u2019')
19290         // opening doubles
19291         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19292         // closing doubles
19293         .replace(/"/g, '\u201d')
19294         // ellipses
19295         .replace(/\.{3}/g, '\u2026');
19296     };
19297     
19298     /**
19299      * Mangle Links
19300      */
19301     
19302     InlineLexer.prototype.mangle = function(text) {
19303       if (!this.options.mangle) { return text; }
19304       var out = ''
19305         , l = text.length
19306         , i = 0
19307         , ch;
19308     
19309       for (; i < l; i++) {
19310         ch = text.charCodeAt(i);
19311         if (Math.random() > 0.5) {
19312           ch = 'x' + ch.toString(16);
19313         }
19314         out += '&#' + ch + ';';
19315       }
19316     
19317       return out;
19318     };
19319     
19320     /**
19321      * Renderer
19322      */
19323     
19324      /**
19325          * eval:var:Renderer
19326     */
19327     
19328     var Renderer   = function (options) {
19329       this.options = options || {};
19330     }
19331     
19332     Renderer.prototype.code = function(code, lang, escaped) {
19333       if (this.options.highlight) {
19334         var out = this.options.highlight(code, lang);
19335         if (out != null && out !== code) {
19336           escaped = true;
19337           code = out;
19338         }
19339       } else {
19340             // hack!!! - it's already escapeD?
19341             escaped = true;
19342       }
19343     
19344       if (!lang) {
19345         return '<pre><code>'
19346           + (escaped ? code : escape(code, true))
19347           + '\n</code></pre>';
19348       }
19349     
19350       return '<pre><code class="'
19351         + this.options.langPrefix
19352         + escape(lang, true)
19353         + '">'
19354         + (escaped ? code : escape(code, true))
19355         + '\n</code></pre>\n';
19356     };
19357     
19358     Renderer.prototype.blockquote = function(quote) {
19359       return '<blockquote>\n' + quote + '</blockquote>\n';
19360     };
19361     
19362     Renderer.prototype.html = function(html) {
19363       return html;
19364     };
19365     
19366     Renderer.prototype.heading = function(text, level, raw) {
19367       return '<h'
19368         + level
19369         + ' id="'
19370         + this.options.headerPrefix
19371         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19372         + '">'
19373         + text
19374         + '</h'
19375         + level
19376         + '>\n';
19377     };
19378     
19379     Renderer.prototype.hr = function() {
19380       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19381     };
19382     
19383     Renderer.prototype.list = function(body, ordered) {
19384       var type = ordered ? 'ol' : 'ul';
19385       return '<' + type + '>\n' + body + '</' + type + '>\n';
19386     };
19387     
19388     Renderer.prototype.listitem = function(text) {
19389       return '<li>' + text + '</li>\n';
19390     };
19391     
19392     Renderer.prototype.paragraph = function(text) {
19393       return '<p>' + text + '</p>\n';
19394     };
19395     
19396     Renderer.prototype.table = function(header, body) {
19397       return '<table class="table table-striped">\n'
19398         + '<thead>\n'
19399         + header
19400         + '</thead>\n'
19401         + '<tbody>\n'
19402         + body
19403         + '</tbody>\n'
19404         + '</table>\n';
19405     };
19406     
19407     Renderer.prototype.tablerow = function(content) {
19408       return '<tr>\n' + content + '</tr>\n';
19409     };
19410     
19411     Renderer.prototype.tablecell = function(content, flags) {
19412       var type = flags.header ? 'th' : 'td';
19413       var tag = flags.align
19414         ? '<' + type + ' style="text-align:' + flags.align + '">'
19415         : '<' + type + '>';
19416       return tag + content + '</' + type + '>\n';
19417     };
19418     
19419     // span level renderer
19420     Renderer.prototype.strong = function(text) {
19421       return '<strong>' + text + '</strong>';
19422     };
19423     
19424     Renderer.prototype.em = function(text) {
19425       return '<em>' + text + '</em>';
19426     };
19427     
19428     Renderer.prototype.codespan = function(text) {
19429       return '<code>' + text + '</code>';
19430     };
19431     
19432     Renderer.prototype.br = function() {
19433       return this.options.xhtml ? '<br/>' : '<br>';
19434     };
19435     
19436     Renderer.prototype.del = function(text) {
19437       return '<del>' + text + '</del>';
19438     };
19439     
19440     Renderer.prototype.link = function(href, title, text) {
19441       if (this.options.sanitize) {
19442         try {
19443           var prot = decodeURIComponent(unescape(href))
19444             .replace(/[^\w:]/g, '')
19445             .toLowerCase();
19446         } catch (e) {
19447           return '';
19448         }
19449         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19450           return '';
19451         }
19452       }
19453       var out = '<a href="' + href + '"';
19454       if (title) {
19455         out += ' title="' + title + '"';
19456       }
19457       out += '>' + text + '</a>';
19458       return out;
19459     };
19460     
19461     Renderer.prototype.image = function(href, title, text) {
19462       var out = '<img src="' + href + '" alt="' + text + '"';
19463       if (title) {
19464         out += ' title="' + title + '"';
19465       }
19466       out += this.options.xhtml ? '/>' : '>';
19467       return out;
19468     };
19469     
19470     Renderer.prototype.text = function(text) {
19471       return text;
19472     };
19473     
19474     /**
19475      * Parsing & Compiling
19476      */
19477          /**
19478          * eval:var:Parser
19479     */
19480     
19481     var Parser= function (options) {
19482       this.tokens = [];
19483       this.token = null;
19484       this.options = options || marked.defaults;
19485       this.options.renderer = this.options.renderer || new Renderer;
19486       this.renderer = this.options.renderer;
19487       this.renderer.options = this.options;
19488     }
19489     
19490     /**
19491      * Static Parse Method
19492      */
19493     
19494     Parser.parse = function(src, options, renderer) {
19495       var parser = new Parser(options, renderer);
19496       return parser.parse(src);
19497     };
19498     
19499     /**
19500      * Parse Loop
19501      */
19502     
19503     Parser.prototype.parse = function(src) {
19504       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19505       this.tokens = src.reverse();
19506     
19507       var out = '';
19508       while (this.next()) {
19509         out += this.tok();
19510       }
19511     
19512       return out;
19513     };
19514     
19515     /**
19516      * Next Token
19517      */
19518     
19519     Parser.prototype.next = function() {
19520       return this.token = this.tokens.pop();
19521     };
19522     
19523     /**
19524      * Preview Next Token
19525      */
19526     
19527     Parser.prototype.peek = function() {
19528       return this.tokens[this.tokens.length - 1] || 0;
19529     };
19530     
19531     /**
19532      * Parse Text Tokens
19533      */
19534     
19535     Parser.prototype.parseText = function() {
19536       var body = this.token.text;
19537     
19538       while (this.peek().type === 'text') {
19539         body += '\n' + this.next().text;
19540       }
19541     
19542       return this.inline.output(body);
19543     };
19544     
19545     /**
19546      * Parse Current Token
19547      */
19548     
19549     Parser.prototype.tok = function() {
19550       switch (this.token.type) {
19551         case 'space': {
19552           return '';
19553         }
19554         case 'hr': {
19555           return this.renderer.hr();
19556         }
19557         case 'heading': {
19558           return this.renderer.heading(
19559             this.inline.output(this.token.text),
19560             this.token.depth,
19561             this.token.text);
19562         }
19563         case 'code': {
19564           return this.renderer.code(this.token.text,
19565             this.token.lang,
19566             this.token.escaped);
19567         }
19568         case 'table': {
19569           var header = ''
19570             , body = ''
19571             , i
19572             , row
19573             , cell
19574             , flags
19575             , j;
19576     
19577           // header
19578           cell = '';
19579           for (i = 0; i < this.token.header.length; i++) {
19580             flags = { header: true, align: this.token.align[i] };
19581             cell += this.renderer.tablecell(
19582               this.inline.output(this.token.header[i]),
19583               { header: true, align: this.token.align[i] }
19584             );
19585           }
19586           header += this.renderer.tablerow(cell);
19587     
19588           for (i = 0; i < this.token.cells.length; i++) {
19589             row = this.token.cells[i];
19590     
19591             cell = '';
19592             for (j = 0; j < row.length; j++) {
19593               cell += this.renderer.tablecell(
19594                 this.inline.output(row[j]),
19595                 { header: false, align: this.token.align[j] }
19596               );
19597             }
19598     
19599             body += this.renderer.tablerow(cell);
19600           }
19601           return this.renderer.table(header, body);
19602         }
19603         case 'blockquote_start': {
19604           var body = '';
19605     
19606           while (this.next().type !== 'blockquote_end') {
19607             body += this.tok();
19608           }
19609     
19610           return this.renderer.blockquote(body);
19611         }
19612         case 'list_start': {
19613           var body = ''
19614             , ordered = this.token.ordered;
19615     
19616           while (this.next().type !== 'list_end') {
19617             body += this.tok();
19618           }
19619     
19620           return this.renderer.list(body, ordered);
19621         }
19622         case 'list_item_start': {
19623           var body = '';
19624     
19625           while (this.next().type !== 'list_item_end') {
19626             body += this.token.type === 'text'
19627               ? this.parseText()
19628               : this.tok();
19629           }
19630     
19631           return this.renderer.listitem(body);
19632         }
19633         case 'loose_item_start': {
19634           var body = '';
19635     
19636           while (this.next().type !== 'list_item_end') {
19637             body += this.tok();
19638           }
19639     
19640           return this.renderer.listitem(body);
19641         }
19642         case 'html': {
19643           var html = !this.token.pre && !this.options.pedantic
19644             ? this.inline.output(this.token.text)
19645             : this.token.text;
19646           return this.renderer.html(html);
19647         }
19648         case 'paragraph': {
19649           return this.renderer.paragraph(this.inline.output(this.token.text));
19650         }
19651         case 'text': {
19652           return this.renderer.paragraph(this.parseText());
19653         }
19654       }
19655     };
19656   
19657     
19658     /**
19659      * Marked
19660      */
19661          /**
19662          * eval:var:marked
19663     */
19664     var marked = function (src, opt, callback) {
19665       if (callback || typeof opt === 'function') {
19666         if (!callback) {
19667           callback = opt;
19668           opt = null;
19669         }
19670     
19671         opt = merge({}, marked.defaults, opt || {});
19672     
19673         var highlight = opt.highlight
19674           , tokens
19675           , pending
19676           , i = 0;
19677     
19678         try {
19679           tokens = Lexer.lex(src, opt)
19680         } catch (e) {
19681           return callback(e);
19682         }
19683     
19684         pending = tokens.length;
19685          /**
19686          * eval:var:done
19687     */
19688         var done = function(err) {
19689           if (err) {
19690             opt.highlight = highlight;
19691             return callback(err);
19692           }
19693     
19694           var out;
19695     
19696           try {
19697             out = Parser.parse(tokens, opt);
19698           } catch (e) {
19699             err = e;
19700           }
19701     
19702           opt.highlight = highlight;
19703     
19704           return err
19705             ? callback(err)
19706             : callback(null, out);
19707         };
19708     
19709         if (!highlight || highlight.length < 3) {
19710           return done();
19711         }
19712     
19713         delete opt.highlight;
19714     
19715         if (!pending) { return done(); }
19716     
19717         for (; i < tokens.length; i++) {
19718           (function(token) {
19719             if (token.type !== 'code') {
19720               return --pending || done();
19721             }
19722             return highlight(token.text, token.lang, function(err, code) {
19723               if (err) { return done(err); }
19724               if (code == null || code === token.text) {
19725                 return --pending || done();
19726               }
19727               token.text = code;
19728               token.escaped = true;
19729               --pending || done();
19730             });
19731           })(tokens[i]);
19732         }
19733     
19734         return;
19735       }
19736       try {
19737         if (opt) { opt = merge({}, marked.defaults, opt); }
19738         return Parser.parse(Lexer.lex(src, opt), opt);
19739       } catch (e) {
19740         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19741         if ((opt || marked.defaults).silent) {
19742           return '<p>An error occured:</p><pre>'
19743             + escape(e.message + '', true)
19744             + '</pre>';
19745         }
19746         throw e;
19747       }
19748     }
19749     
19750     /**
19751      * Options
19752      */
19753     
19754     marked.options =
19755     marked.setOptions = function(opt) {
19756       merge(marked.defaults, opt);
19757       return marked;
19758     };
19759     
19760     marked.defaults = {
19761       gfm: true,
19762       tables: true,
19763       breaks: false,
19764       pedantic: false,
19765       sanitize: false,
19766       sanitizer: null,
19767       mangle: true,
19768       smartLists: false,
19769       silent: false,
19770       highlight: null,
19771       langPrefix: 'lang-',
19772       smartypants: false,
19773       headerPrefix: '',
19774       renderer: new Renderer,
19775       xhtml: false
19776     };
19777     
19778     /**
19779      * Expose
19780      */
19781     
19782     marked.Parser = Parser;
19783     marked.parser = Parser.parse;
19784     
19785     marked.Renderer = Renderer;
19786     
19787     marked.Lexer = Lexer;
19788     marked.lexer = Lexer.lex;
19789     
19790     marked.InlineLexer = InlineLexer;
19791     marked.inlineLexer = InlineLexer.output;
19792     
19793     marked.parse = marked;
19794     
19795     Roo.Markdown.marked = marked;
19796
19797 })();/*
19798  * Based on:
19799  * Ext JS Library 1.1.1
19800  * Copyright(c) 2006-2007, Ext JS, LLC.
19801  *
19802  * Originally Released Under LGPL - original licence link has changed is not relivant.
19803  *
19804  * Fork - LGPL
19805  * <script type="text/javascript">
19806  */
19807
19808
19809
19810 /*
19811  * These classes are derivatives of the similarly named classes in the YUI Library.
19812  * The original license:
19813  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19814  * Code licensed under the BSD License:
19815  * http://developer.yahoo.net/yui/license.txt
19816  */
19817
19818 (function() {
19819
19820 var Event=Roo.EventManager;
19821 var Dom=Roo.lib.Dom;
19822
19823 /**
19824  * @class Roo.dd.DragDrop
19825  * @extends Roo.util.Observable
19826  * Defines the interface and base operation of items that that can be
19827  * dragged or can be drop targets.  It was designed to be extended, overriding
19828  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19829  * Up to three html elements can be associated with a DragDrop instance:
19830  * <ul>
19831  * <li>linked element: the element that is passed into the constructor.
19832  * This is the element which defines the boundaries for interaction with
19833  * other DragDrop objects.</li>
19834  * <li>handle element(s): The drag operation only occurs if the element that
19835  * was clicked matches a handle element.  By default this is the linked
19836  * element, but there are times that you will want only a portion of the
19837  * linked element to initiate the drag operation, and the setHandleElId()
19838  * method provides a way to define this.</li>
19839  * <li>drag element: this represents the element that would be moved along
19840  * with the cursor during a drag operation.  By default, this is the linked
19841  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19842  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19843  * </li>
19844  * </ul>
19845  * This class should not be instantiated until the onload event to ensure that
19846  * the associated elements are available.
19847  * The following would define a DragDrop obj that would interact with any
19848  * other DragDrop obj in the "group1" group:
19849  * <pre>
19850  *  dd = new Roo.dd.DragDrop("div1", "group1");
19851  * </pre>
19852  * Since none of the event handlers have been implemented, nothing would
19853  * actually happen if you were to run the code above.  Normally you would
19854  * override this class or one of the default implementations, but you can
19855  * also override the methods you want on an instance of the class...
19856  * <pre>
19857  *  dd.onDragDrop = function(e, id) {
19858  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19859  *  }
19860  * </pre>
19861  * @constructor
19862  * @param {String} id of the element that is linked to this instance
19863  * @param {String} sGroup the group of related DragDrop objects
19864  * @param {object} config an object containing configurable attributes
19865  *                Valid properties for DragDrop:
19866  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19867  */
19868 Roo.dd.DragDrop = function(id, sGroup, config) {
19869     if (id) {
19870         this.init(id, sGroup, config);
19871     }
19872     
19873 };
19874
19875 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19876
19877     /**
19878      * The id of the element associated with this object.  This is what we
19879      * refer to as the "linked element" because the size and position of
19880      * this element is used to determine when the drag and drop objects have
19881      * interacted.
19882      * @property id
19883      * @type String
19884      */
19885     id: null,
19886
19887     /**
19888      * Configuration attributes passed into the constructor
19889      * @property config
19890      * @type object
19891      */
19892     config: null,
19893
19894     /**
19895      * The id of the element that will be dragged.  By default this is same
19896      * as the linked element , but could be changed to another element. Ex:
19897      * Roo.dd.DDProxy
19898      * @property dragElId
19899      * @type String
19900      * @private
19901      */
19902     dragElId: null,
19903
19904     /**
19905      * the id of the element that initiates the drag operation.  By default
19906      * this is the linked element, but could be changed to be a child of this
19907      * element.  This lets us do things like only starting the drag when the
19908      * header element within the linked html element is clicked.
19909      * @property handleElId
19910      * @type String
19911      * @private
19912      */
19913     handleElId: null,
19914
19915     /**
19916      * An associative array of HTML tags that will be ignored if clicked.
19917      * @property invalidHandleTypes
19918      * @type {string: string}
19919      */
19920     invalidHandleTypes: null,
19921
19922     /**
19923      * An associative array of ids for elements that will be ignored if clicked
19924      * @property invalidHandleIds
19925      * @type {string: string}
19926      */
19927     invalidHandleIds: null,
19928
19929     /**
19930      * An indexted array of css class names for elements that will be ignored
19931      * if clicked.
19932      * @property invalidHandleClasses
19933      * @type string[]
19934      */
19935     invalidHandleClasses: null,
19936
19937     /**
19938      * The linked element's absolute X position at the time the drag was
19939      * started
19940      * @property startPageX
19941      * @type int
19942      * @private
19943      */
19944     startPageX: 0,
19945
19946     /**
19947      * The linked element's absolute X position at the time the drag was
19948      * started
19949      * @property startPageY
19950      * @type int
19951      * @private
19952      */
19953     startPageY: 0,
19954
19955     /**
19956      * The group defines a logical collection of DragDrop objects that are
19957      * related.  Instances only get events when interacting with other
19958      * DragDrop object in the same group.  This lets us define multiple
19959      * groups using a single DragDrop subclass if we want.
19960      * @property groups
19961      * @type {string: string}
19962      */
19963     groups: null,
19964
19965     /**
19966      * Individual drag/drop instances can be locked.  This will prevent
19967      * onmousedown start drag.
19968      * @property locked
19969      * @type boolean
19970      * @private
19971      */
19972     locked: false,
19973
19974     /**
19975      * Lock this instance
19976      * @method lock
19977      */
19978     lock: function() { this.locked = true; },
19979
19980     /**
19981      * Unlock this instace
19982      * @method unlock
19983      */
19984     unlock: function() { this.locked = false; },
19985
19986     /**
19987      * By default, all insances can be a drop target.  This can be disabled by
19988      * setting isTarget to false.
19989      * @method isTarget
19990      * @type boolean
19991      */
19992     isTarget: true,
19993
19994     /**
19995      * The padding configured for this drag and drop object for calculating
19996      * the drop zone intersection with this object.
19997      * @method padding
19998      * @type int[]
19999      */
20000     padding: null,
20001
20002     /**
20003      * Cached reference to the linked element
20004      * @property _domRef
20005      * @private
20006      */
20007     _domRef: null,
20008
20009     /**
20010      * Internal typeof flag
20011      * @property __ygDragDrop
20012      * @private
20013      */
20014     __ygDragDrop: true,
20015
20016     /**
20017      * Set to true when horizontal contraints are applied
20018      * @property constrainX
20019      * @type boolean
20020      * @private
20021      */
20022     constrainX: false,
20023
20024     /**
20025      * Set to true when vertical contraints are applied
20026      * @property constrainY
20027      * @type boolean
20028      * @private
20029      */
20030     constrainY: false,
20031
20032     /**
20033      * The left constraint
20034      * @property minX
20035      * @type int
20036      * @private
20037      */
20038     minX: 0,
20039
20040     /**
20041      * The right constraint
20042      * @property maxX
20043      * @type int
20044      * @private
20045      */
20046     maxX: 0,
20047
20048     /**
20049      * The up constraint
20050      * @property minY
20051      * @type int
20052      * @type int
20053      * @private
20054      */
20055     minY: 0,
20056
20057     /**
20058      * The down constraint
20059      * @property maxY
20060      * @type int
20061      * @private
20062      */
20063     maxY: 0,
20064
20065     /**
20066      * Maintain offsets when we resetconstraints.  Set to true when you want
20067      * the position of the element relative to its parent to stay the same
20068      * when the page changes
20069      *
20070      * @property maintainOffset
20071      * @type boolean
20072      */
20073     maintainOffset: false,
20074
20075     /**
20076      * Array of pixel locations the element will snap to if we specified a
20077      * horizontal graduation/interval.  This array is generated automatically
20078      * when you define a tick interval.
20079      * @property xTicks
20080      * @type int[]
20081      */
20082     xTicks: null,
20083
20084     /**
20085      * Array of pixel locations the element will snap to if we specified a
20086      * vertical graduation/interval.  This array is generated automatically
20087      * when you define a tick interval.
20088      * @property yTicks
20089      * @type int[]
20090      */
20091     yTicks: null,
20092
20093     /**
20094      * By default the drag and drop instance will only respond to the primary
20095      * button click (left button for a right-handed mouse).  Set to true to
20096      * allow drag and drop to start with any mouse click that is propogated
20097      * by the browser
20098      * @property primaryButtonOnly
20099      * @type boolean
20100      */
20101     primaryButtonOnly: true,
20102
20103     /**
20104      * The availabe property is false until the linked dom element is accessible.
20105      * @property available
20106      * @type boolean
20107      */
20108     available: false,
20109
20110     /**
20111      * By default, drags can only be initiated if the mousedown occurs in the
20112      * region the linked element is.  This is done in part to work around a
20113      * bug in some browsers that mis-report the mousedown if the previous
20114      * mouseup happened outside of the window.  This property is set to true
20115      * if outer handles are defined.
20116      *
20117      * @property hasOuterHandles
20118      * @type boolean
20119      * @default false
20120      */
20121     hasOuterHandles: false,
20122
20123     /**
20124      * Code that executes immediately before the startDrag event
20125      * @method b4StartDrag
20126      * @private
20127      */
20128     b4StartDrag: function(x, y) { },
20129
20130     /**
20131      * Abstract method called after a drag/drop object is clicked
20132      * and the drag or mousedown time thresholds have beeen met.
20133      * @method startDrag
20134      * @param {int} X click location
20135      * @param {int} Y click location
20136      */
20137     startDrag: function(x, y) { /* override this */ },
20138
20139     /**
20140      * Code that executes immediately before the onDrag event
20141      * @method b4Drag
20142      * @private
20143      */
20144     b4Drag: function(e) { },
20145
20146     /**
20147      * Abstract method called during the onMouseMove event while dragging an
20148      * object.
20149      * @method onDrag
20150      * @param {Event} e the mousemove event
20151      */
20152     onDrag: function(e) { /* override this */ },
20153
20154     /**
20155      * Abstract method called when this element fist begins hovering over
20156      * another DragDrop obj
20157      * @method onDragEnter
20158      * @param {Event} e the mousemove event
20159      * @param {String|DragDrop[]} id In POINT mode, the element
20160      * id this is hovering over.  In INTERSECT mode, an array of one or more
20161      * dragdrop items being hovered over.
20162      */
20163     onDragEnter: function(e, id) { /* override this */ },
20164
20165     /**
20166      * Code that executes immediately before the onDragOver event
20167      * @method b4DragOver
20168      * @private
20169      */
20170     b4DragOver: function(e) { },
20171
20172     /**
20173      * Abstract method called when this element is hovering over another
20174      * DragDrop obj
20175      * @method onDragOver
20176      * @param {Event} e the mousemove event
20177      * @param {String|DragDrop[]} id In POINT mode, the element
20178      * id this is hovering over.  In INTERSECT mode, an array of dd items
20179      * being hovered over.
20180      */
20181     onDragOver: function(e, id) { /* override this */ },
20182
20183     /**
20184      * Code that executes immediately before the onDragOut event
20185      * @method b4DragOut
20186      * @private
20187      */
20188     b4DragOut: function(e) { },
20189
20190     /**
20191      * Abstract method called when we are no longer hovering over an element
20192      * @method onDragOut
20193      * @param {Event} e the mousemove event
20194      * @param {String|DragDrop[]} id In POINT mode, the element
20195      * id this was hovering over.  In INTERSECT mode, an array of dd items
20196      * that the mouse is no longer over.
20197      */
20198     onDragOut: function(e, id) { /* override this */ },
20199
20200     /**
20201      * Code that executes immediately before the onDragDrop event
20202      * @method b4DragDrop
20203      * @private
20204      */
20205     b4DragDrop: function(e) { },
20206
20207     /**
20208      * Abstract method called when this item is dropped on another DragDrop
20209      * obj
20210      * @method onDragDrop
20211      * @param {Event} e the mouseup event
20212      * @param {String|DragDrop[]} id In POINT mode, the element
20213      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20214      * was dropped on.
20215      */
20216     onDragDrop: function(e, id) { /* override this */ },
20217
20218     /**
20219      * Abstract method called when this item is dropped on an area with no
20220      * drop target
20221      * @method onInvalidDrop
20222      * @param {Event} e the mouseup event
20223      */
20224     onInvalidDrop: function(e) { /* override this */ },
20225
20226     /**
20227      * Code that executes immediately before the endDrag event
20228      * @method b4EndDrag
20229      * @private
20230      */
20231     b4EndDrag: function(e) { },
20232
20233     /**
20234      * Fired when we are done dragging the object
20235      * @method endDrag
20236      * @param {Event} e the mouseup event
20237      */
20238     endDrag: function(e) { /* override this */ },
20239
20240     /**
20241      * Code executed immediately before the onMouseDown event
20242      * @method b4MouseDown
20243      * @param {Event} e the mousedown event
20244      * @private
20245      */
20246     b4MouseDown: function(e) {  },
20247
20248     /**
20249      * Event handler that fires when a drag/drop obj gets a mousedown
20250      * @method onMouseDown
20251      * @param {Event} e the mousedown event
20252      */
20253     onMouseDown: function(e) { /* override this */ },
20254
20255     /**
20256      * Event handler that fires when a drag/drop obj gets a mouseup
20257      * @method onMouseUp
20258      * @param {Event} e the mouseup event
20259      */
20260     onMouseUp: function(e) { /* override this */ },
20261
20262     /**
20263      * Override the onAvailable method to do what is needed after the initial
20264      * position was determined.
20265      * @method onAvailable
20266      */
20267     onAvailable: function () {
20268     },
20269
20270     /*
20271      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20272      * @type Object
20273      */
20274     defaultPadding : {left:0, right:0, top:0, bottom:0},
20275
20276     /*
20277      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20278  *
20279  * Usage:
20280  <pre><code>
20281  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20282                 { dragElId: "existingProxyDiv" });
20283  dd.startDrag = function(){
20284      this.constrainTo("parent-id");
20285  };
20286  </code></pre>
20287  * Or you can initalize it using the {@link Roo.Element} object:
20288  <pre><code>
20289  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20290      startDrag : function(){
20291          this.constrainTo("parent-id");
20292      }
20293  });
20294  </code></pre>
20295      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20296      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20297      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20298      * an object containing the sides to pad. For example: {right:10, bottom:10}
20299      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20300      */
20301     constrainTo : function(constrainTo, pad, inContent){
20302         if(typeof pad == "number"){
20303             pad = {left: pad, right:pad, top:pad, bottom:pad};
20304         }
20305         pad = pad || this.defaultPadding;
20306         var b = Roo.get(this.getEl()).getBox();
20307         var ce = Roo.get(constrainTo);
20308         var s = ce.getScroll();
20309         var c, cd = ce.dom;
20310         if(cd == document.body){
20311             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20312         }else{
20313             xy = ce.getXY();
20314             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20315         }
20316
20317
20318         var topSpace = b.y - c.y;
20319         var leftSpace = b.x - c.x;
20320
20321         this.resetConstraints();
20322         this.setXConstraint(leftSpace - (pad.left||0), // left
20323                 c.width - leftSpace - b.width - (pad.right||0) //right
20324         );
20325         this.setYConstraint(topSpace - (pad.top||0), //top
20326                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20327         );
20328     },
20329
20330     /**
20331      * Returns a reference to the linked element
20332      * @method getEl
20333      * @return {HTMLElement} the html element
20334      */
20335     getEl: function() {
20336         if (!this._domRef) {
20337             this._domRef = Roo.getDom(this.id);
20338         }
20339
20340         return this._domRef;
20341     },
20342
20343     /**
20344      * Returns a reference to the actual element to drag.  By default this is
20345      * the same as the html element, but it can be assigned to another
20346      * element. An example of this can be found in Roo.dd.DDProxy
20347      * @method getDragEl
20348      * @return {HTMLElement} the html element
20349      */
20350     getDragEl: function() {
20351         return Roo.getDom(this.dragElId);
20352     },
20353
20354     /**
20355      * Sets up the DragDrop object.  Must be called in the constructor of any
20356      * Roo.dd.DragDrop subclass
20357      * @method init
20358      * @param id the id of the linked element
20359      * @param {String} sGroup the group of related items
20360      * @param {object} config configuration attributes
20361      */
20362     init: function(id, sGroup, config) {
20363         this.initTarget(id, sGroup, config);
20364         if (!Roo.isTouch) {
20365             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20366         }
20367         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20368         // Event.on(this.id, "selectstart", Event.preventDefault);
20369     },
20370
20371     /**
20372      * Initializes Targeting functionality only... the object does not
20373      * get a mousedown handler.
20374      * @method initTarget
20375      * @param id the id of the linked element
20376      * @param {String} sGroup the group of related items
20377      * @param {object} config configuration attributes
20378      */
20379     initTarget: function(id, sGroup, config) {
20380
20381         // configuration attributes
20382         this.config = config || {};
20383
20384         // create a local reference to the drag and drop manager
20385         this.DDM = Roo.dd.DDM;
20386         // initialize the groups array
20387         this.groups = {};
20388
20389         // assume that we have an element reference instead of an id if the
20390         // parameter is not a string
20391         if (typeof id !== "string") {
20392             id = Roo.id(id);
20393         }
20394
20395         // set the id
20396         this.id = id;
20397
20398         // add to an interaction group
20399         this.addToGroup((sGroup) ? sGroup : "default");
20400
20401         // We don't want to register this as the handle with the manager
20402         // so we just set the id rather than calling the setter.
20403         this.handleElId = id;
20404
20405         // the linked element is the element that gets dragged by default
20406         this.setDragElId(id);
20407
20408         // by default, clicked anchors will not start drag operations.
20409         this.invalidHandleTypes = { A: "A" };
20410         this.invalidHandleIds = {};
20411         this.invalidHandleClasses = [];
20412
20413         this.applyConfig();
20414
20415         this.handleOnAvailable();
20416     },
20417
20418     /**
20419      * Applies the configuration parameters that were passed into the constructor.
20420      * This is supposed to happen at each level through the inheritance chain.  So
20421      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20422      * DragDrop in order to get all of the parameters that are available in
20423      * each object.
20424      * @method applyConfig
20425      */
20426     applyConfig: function() {
20427
20428         // configurable properties:
20429         //    padding, isTarget, maintainOffset, primaryButtonOnly
20430         this.padding           = this.config.padding || [0, 0, 0, 0];
20431         this.isTarget          = (this.config.isTarget !== false);
20432         this.maintainOffset    = (this.config.maintainOffset);
20433         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20434
20435     },
20436
20437     /**
20438      * Executed when the linked element is available
20439      * @method handleOnAvailable
20440      * @private
20441      */
20442     handleOnAvailable: function() {
20443         this.available = true;
20444         this.resetConstraints();
20445         this.onAvailable();
20446     },
20447
20448      /**
20449      * Configures the padding for the target zone in px.  Effectively expands
20450      * (or reduces) the virtual object size for targeting calculations.
20451      * Supports css-style shorthand; if only one parameter is passed, all sides
20452      * will have that padding, and if only two are passed, the top and bottom
20453      * will have the first param, the left and right the second.
20454      * @method setPadding
20455      * @param {int} iTop    Top pad
20456      * @param {int} iRight  Right pad
20457      * @param {int} iBot    Bot pad
20458      * @param {int} iLeft   Left pad
20459      */
20460     setPadding: function(iTop, iRight, iBot, iLeft) {
20461         // this.padding = [iLeft, iRight, iTop, iBot];
20462         if (!iRight && 0 !== iRight) {
20463             this.padding = [iTop, iTop, iTop, iTop];
20464         } else if (!iBot && 0 !== iBot) {
20465             this.padding = [iTop, iRight, iTop, iRight];
20466         } else {
20467             this.padding = [iTop, iRight, iBot, iLeft];
20468         }
20469     },
20470
20471     /**
20472      * Stores the initial placement of the linked element.
20473      * @method setInitialPosition
20474      * @param {int} diffX   the X offset, default 0
20475      * @param {int} diffY   the Y offset, default 0
20476      */
20477     setInitPosition: function(diffX, diffY) {
20478         var el = this.getEl();
20479
20480         if (!this.DDM.verifyEl(el)) {
20481             return;
20482         }
20483
20484         var dx = diffX || 0;
20485         var dy = diffY || 0;
20486
20487         var p = Dom.getXY( el );
20488
20489         this.initPageX = p[0] - dx;
20490         this.initPageY = p[1] - dy;
20491
20492         this.lastPageX = p[0];
20493         this.lastPageY = p[1];
20494
20495
20496         this.setStartPosition(p);
20497     },
20498
20499     /**
20500      * Sets the start position of the element.  This is set when the obj
20501      * is initialized, the reset when a drag is started.
20502      * @method setStartPosition
20503      * @param pos current position (from previous lookup)
20504      * @private
20505      */
20506     setStartPosition: function(pos) {
20507         var p = pos || Dom.getXY( this.getEl() );
20508         this.deltaSetXY = null;
20509
20510         this.startPageX = p[0];
20511         this.startPageY = p[1];
20512     },
20513
20514     /**
20515      * Add this instance to a group of related drag/drop objects.  All
20516      * instances belong to at least one group, and can belong to as many
20517      * groups as needed.
20518      * @method addToGroup
20519      * @param sGroup {string} the name of the group
20520      */
20521     addToGroup: function(sGroup) {
20522         this.groups[sGroup] = true;
20523         this.DDM.regDragDrop(this, sGroup);
20524     },
20525
20526     /**
20527      * Remove's this instance from the supplied interaction group
20528      * @method removeFromGroup
20529      * @param {string}  sGroup  The group to drop
20530      */
20531     removeFromGroup: function(sGroup) {
20532         if (this.groups[sGroup]) {
20533             delete this.groups[sGroup];
20534         }
20535
20536         this.DDM.removeDDFromGroup(this, sGroup);
20537     },
20538
20539     /**
20540      * Allows you to specify that an element other than the linked element
20541      * will be moved with the cursor during a drag
20542      * @method setDragElId
20543      * @param id {string} the id of the element that will be used to initiate the drag
20544      */
20545     setDragElId: function(id) {
20546         this.dragElId = id;
20547     },
20548
20549     /**
20550      * Allows you to specify a child of the linked element that should be
20551      * used to initiate the drag operation.  An example of this would be if
20552      * you have a content div with text and links.  Clicking anywhere in the
20553      * content area would normally start the drag operation.  Use this method
20554      * to specify that an element inside of the content div is the element
20555      * that starts the drag operation.
20556      * @method setHandleElId
20557      * @param id {string} the id of the element that will be used to
20558      * initiate the drag.
20559      */
20560     setHandleElId: function(id) {
20561         if (typeof id !== "string") {
20562             id = Roo.id(id);
20563         }
20564         this.handleElId = id;
20565         this.DDM.regHandle(this.id, id);
20566     },
20567
20568     /**
20569      * Allows you to set an element outside of the linked element as a drag
20570      * handle
20571      * @method setOuterHandleElId
20572      * @param id the id of the element that will be used to initiate the drag
20573      */
20574     setOuterHandleElId: function(id) {
20575         if (typeof id !== "string") {
20576             id = Roo.id(id);
20577         }
20578         Event.on(id, "mousedown",
20579                 this.handleMouseDown, this);
20580         this.setHandleElId(id);
20581
20582         this.hasOuterHandles = true;
20583     },
20584
20585     /**
20586      * Remove all drag and drop hooks for this element
20587      * @method unreg
20588      */
20589     unreg: function() {
20590         Event.un(this.id, "mousedown",
20591                 this.handleMouseDown);
20592         Event.un(this.id, "touchstart",
20593                 this.handleMouseDown);
20594         this._domRef = null;
20595         this.DDM._remove(this);
20596     },
20597
20598     destroy : function(){
20599         this.unreg();
20600     },
20601
20602     /**
20603      * Returns true if this instance is locked, or the drag drop mgr is locked
20604      * (meaning that all drag/drop is disabled on the page.)
20605      * @method isLocked
20606      * @return {boolean} true if this obj or all drag/drop is locked, else
20607      * false
20608      */
20609     isLocked: function() {
20610         return (this.DDM.isLocked() || this.locked);
20611     },
20612
20613     /**
20614      * Fired when this object is clicked
20615      * @method handleMouseDown
20616      * @param {Event} e
20617      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20618      * @private
20619      */
20620     handleMouseDown: function(e, oDD){
20621      
20622         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20623             //Roo.log('not touch/ button !=0');
20624             return;
20625         }
20626         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20627             return; // double touch..
20628         }
20629         
20630
20631         if (this.isLocked()) {
20632             //Roo.log('locked');
20633             return;
20634         }
20635
20636         this.DDM.refreshCache(this.groups);
20637 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20638         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20639         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20640             //Roo.log('no outer handes or not over target');
20641                 // do nothing.
20642         } else {
20643 //            Roo.log('check validator');
20644             if (this.clickValidator(e)) {
20645 //                Roo.log('validate success');
20646                 // set the initial element position
20647                 this.setStartPosition();
20648
20649
20650                 this.b4MouseDown(e);
20651                 this.onMouseDown(e);
20652
20653                 this.DDM.handleMouseDown(e, this);
20654
20655                 this.DDM.stopEvent(e);
20656             } else {
20657
20658
20659             }
20660         }
20661     },
20662
20663     clickValidator: function(e) {
20664         var target = e.getTarget();
20665         return ( this.isValidHandleChild(target) &&
20666                     (this.id == this.handleElId ||
20667                         this.DDM.handleWasClicked(target, this.id)) );
20668     },
20669
20670     /**
20671      * Allows you to specify a tag name that should not start a drag operation
20672      * when clicked.  This is designed to facilitate embedding links within a
20673      * drag handle that do something other than start the drag.
20674      * @method addInvalidHandleType
20675      * @param {string} tagName the type of element to exclude
20676      */
20677     addInvalidHandleType: function(tagName) {
20678         var type = tagName.toUpperCase();
20679         this.invalidHandleTypes[type] = type;
20680     },
20681
20682     /**
20683      * Lets you to specify an element id for a child of a drag handle
20684      * that should not initiate a drag
20685      * @method addInvalidHandleId
20686      * @param {string} id the element id of the element you wish to ignore
20687      */
20688     addInvalidHandleId: function(id) {
20689         if (typeof id !== "string") {
20690             id = Roo.id(id);
20691         }
20692         this.invalidHandleIds[id] = id;
20693     },
20694
20695     /**
20696      * Lets you specify a css class of elements that will not initiate a drag
20697      * @method addInvalidHandleClass
20698      * @param {string} cssClass the class of the elements you wish to ignore
20699      */
20700     addInvalidHandleClass: function(cssClass) {
20701         this.invalidHandleClasses.push(cssClass);
20702     },
20703
20704     /**
20705      * Unsets an excluded tag name set by addInvalidHandleType
20706      * @method removeInvalidHandleType
20707      * @param {string} tagName the type of element to unexclude
20708      */
20709     removeInvalidHandleType: function(tagName) {
20710         var type = tagName.toUpperCase();
20711         // this.invalidHandleTypes[type] = null;
20712         delete this.invalidHandleTypes[type];
20713     },
20714
20715     /**
20716      * Unsets an invalid handle id
20717      * @method removeInvalidHandleId
20718      * @param {string} id the id of the element to re-enable
20719      */
20720     removeInvalidHandleId: function(id) {
20721         if (typeof id !== "string") {
20722             id = Roo.id(id);
20723         }
20724         delete this.invalidHandleIds[id];
20725     },
20726
20727     /**
20728      * Unsets an invalid css class
20729      * @method removeInvalidHandleClass
20730      * @param {string} cssClass the class of the element(s) you wish to
20731      * re-enable
20732      */
20733     removeInvalidHandleClass: function(cssClass) {
20734         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20735             if (this.invalidHandleClasses[i] == cssClass) {
20736                 delete this.invalidHandleClasses[i];
20737             }
20738         }
20739     },
20740
20741     /**
20742      * Checks the tag exclusion list to see if this click should be ignored
20743      * @method isValidHandleChild
20744      * @param {HTMLElement} node the HTMLElement to evaluate
20745      * @return {boolean} true if this is a valid tag type, false if not
20746      */
20747     isValidHandleChild: function(node) {
20748
20749         var valid = true;
20750         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20751         var nodeName;
20752         try {
20753             nodeName = node.nodeName.toUpperCase();
20754         } catch(e) {
20755             nodeName = node.nodeName;
20756         }
20757         valid = valid && !this.invalidHandleTypes[nodeName];
20758         valid = valid && !this.invalidHandleIds[node.id];
20759
20760         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20761             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20762         }
20763
20764
20765         return valid;
20766
20767     },
20768
20769     /**
20770      * Create the array of horizontal tick marks if an interval was specified
20771      * in setXConstraint().
20772      * @method setXTicks
20773      * @private
20774      */
20775     setXTicks: function(iStartX, iTickSize) {
20776         this.xTicks = [];
20777         this.xTickSize = iTickSize;
20778
20779         var tickMap = {};
20780
20781         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20782             if (!tickMap[i]) {
20783                 this.xTicks[this.xTicks.length] = i;
20784                 tickMap[i] = true;
20785             }
20786         }
20787
20788         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20789             if (!tickMap[i]) {
20790                 this.xTicks[this.xTicks.length] = i;
20791                 tickMap[i] = true;
20792             }
20793         }
20794
20795         this.xTicks.sort(this.DDM.numericSort) ;
20796     },
20797
20798     /**
20799      * Create the array of vertical tick marks if an interval was specified in
20800      * setYConstraint().
20801      * @method setYTicks
20802      * @private
20803      */
20804     setYTicks: function(iStartY, iTickSize) {
20805         this.yTicks = [];
20806         this.yTickSize = iTickSize;
20807
20808         var tickMap = {};
20809
20810         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20811             if (!tickMap[i]) {
20812                 this.yTicks[this.yTicks.length] = i;
20813                 tickMap[i] = true;
20814             }
20815         }
20816
20817         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20818             if (!tickMap[i]) {
20819                 this.yTicks[this.yTicks.length] = i;
20820                 tickMap[i] = true;
20821             }
20822         }
20823
20824         this.yTicks.sort(this.DDM.numericSort) ;
20825     },
20826
20827     /**
20828      * By default, the element can be dragged any place on the screen.  Use
20829      * this method to limit the horizontal travel of the element.  Pass in
20830      * 0,0 for the parameters if you want to lock the drag to the y axis.
20831      * @method setXConstraint
20832      * @param {int} iLeft the number of pixels the element can move to the left
20833      * @param {int} iRight the number of pixels the element can move to the
20834      * right
20835      * @param {int} iTickSize optional parameter for specifying that the
20836      * element
20837      * should move iTickSize pixels at a time.
20838      */
20839     setXConstraint: function(iLeft, iRight, iTickSize) {
20840         this.leftConstraint = iLeft;
20841         this.rightConstraint = iRight;
20842
20843         this.minX = this.initPageX - iLeft;
20844         this.maxX = this.initPageX + iRight;
20845         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20846
20847         this.constrainX = true;
20848     },
20849
20850     /**
20851      * Clears any constraints applied to this instance.  Also clears ticks
20852      * since they can't exist independent of a constraint at this time.
20853      * @method clearConstraints
20854      */
20855     clearConstraints: function() {
20856         this.constrainX = false;
20857         this.constrainY = false;
20858         this.clearTicks();
20859     },
20860
20861     /**
20862      * Clears any tick interval defined for this instance
20863      * @method clearTicks
20864      */
20865     clearTicks: function() {
20866         this.xTicks = null;
20867         this.yTicks = null;
20868         this.xTickSize = 0;
20869         this.yTickSize = 0;
20870     },
20871
20872     /**
20873      * By default, the element can be dragged any place on the screen.  Set
20874      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20875      * parameters if you want to lock the drag to the x axis.
20876      * @method setYConstraint
20877      * @param {int} iUp the number of pixels the element can move up
20878      * @param {int} iDown the number of pixels the element can move down
20879      * @param {int} iTickSize optional parameter for specifying that the
20880      * element should move iTickSize pixels at a time.
20881      */
20882     setYConstraint: function(iUp, iDown, iTickSize) {
20883         this.topConstraint = iUp;
20884         this.bottomConstraint = iDown;
20885
20886         this.minY = this.initPageY - iUp;
20887         this.maxY = this.initPageY + iDown;
20888         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20889
20890         this.constrainY = true;
20891
20892     },
20893
20894     /**
20895      * resetConstraints must be called if you manually reposition a dd element.
20896      * @method resetConstraints
20897      * @param {boolean} maintainOffset
20898      */
20899     resetConstraints: function() {
20900
20901
20902         // Maintain offsets if necessary
20903         if (this.initPageX || this.initPageX === 0) {
20904             // figure out how much this thing has moved
20905             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20906             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20907
20908             this.setInitPosition(dx, dy);
20909
20910         // This is the first time we have detected the element's position
20911         } else {
20912             this.setInitPosition();
20913         }
20914
20915         if (this.constrainX) {
20916             this.setXConstraint( this.leftConstraint,
20917                                  this.rightConstraint,
20918                                  this.xTickSize        );
20919         }
20920
20921         if (this.constrainY) {
20922             this.setYConstraint( this.topConstraint,
20923                                  this.bottomConstraint,
20924                                  this.yTickSize         );
20925         }
20926     },
20927
20928     /**
20929      * Normally the drag element is moved pixel by pixel, but we can specify
20930      * that it move a number of pixels at a time.  This method resolves the
20931      * location when we have it set up like this.
20932      * @method getTick
20933      * @param {int} val where we want to place the object
20934      * @param {int[]} tickArray sorted array of valid points
20935      * @return {int} the closest tick
20936      * @private
20937      */
20938     getTick: function(val, tickArray) {
20939
20940         if (!tickArray) {
20941             // If tick interval is not defined, it is effectively 1 pixel,
20942             // so we return the value passed to us.
20943             return val;
20944         } else if (tickArray[0] >= val) {
20945             // The value is lower than the first tick, so we return the first
20946             // tick.
20947             return tickArray[0];
20948         } else {
20949             for (var i=0, len=tickArray.length; i<len; ++i) {
20950                 var next = i + 1;
20951                 if (tickArray[next] && tickArray[next] >= val) {
20952                     var diff1 = val - tickArray[i];
20953                     var diff2 = tickArray[next] - val;
20954                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20955                 }
20956             }
20957
20958             // The value is larger than the last tick, so we return the last
20959             // tick.
20960             return tickArray[tickArray.length - 1];
20961         }
20962     },
20963
20964     /**
20965      * toString method
20966      * @method toString
20967      * @return {string} string representation of the dd obj
20968      */
20969     toString: function() {
20970         return ("DragDrop " + this.id);
20971     }
20972
20973 });
20974
20975 })();
20976 /*
20977  * Based on:
20978  * Ext JS Library 1.1.1
20979  * Copyright(c) 2006-2007, Ext JS, LLC.
20980  *
20981  * Originally Released Under LGPL - original licence link has changed is not relivant.
20982  *
20983  * Fork - LGPL
20984  * <script type="text/javascript">
20985  */
20986
20987
20988 /**
20989  * The drag and drop utility provides a framework for building drag and drop
20990  * applications.  In addition to enabling drag and drop for specific elements,
20991  * the drag and drop elements are tracked by the manager class, and the
20992  * interactions between the various elements are tracked during the drag and
20993  * the implementing code is notified about these important moments.
20994  */
20995
20996 // Only load the library once.  Rewriting the manager class would orphan
20997 // existing drag and drop instances.
20998 if (!Roo.dd.DragDropMgr) {
20999
21000 /**
21001  * @class Roo.dd.DragDropMgr
21002  * DragDropMgr is a singleton that tracks the element interaction for
21003  * all DragDrop items in the window.  Generally, you will not call
21004  * this class directly, but it does have helper methods that could
21005  * be useful in your DragDrop implementations.
21006  * @static
21007  */
21008 Roo.dd.DragDropMgr = function() {
21009
21010     var Event = Roo.EventManager;
21011
21012     return {
21013
21014         /**
21015          * Two dimensional Array of registered DragDrop objects.  The first
21016          * dimension is the DragDrop item group, the second the DragDrop
21017          * object.
21018          * @property ids
21019          * @type {string: string}
21020          * @private
21021          * @static
21022          */
21023         ids: {},
21024
21025         /**
21026          * Array of element ids defined as drag handles.  Used to determine
21027          * if the element that generated the mousedown event is actually the
21028          * handle and not the html element itself.
21029          * @property handleIds
21030          * @type {string: string}
21031          * @private
21032          * @static
21033          */
21034         handleIds: {},
21035
21036         /**
21037          * the DragDrop object that is currently being dragged
21038          * @property dragCurrent
21039          * @type DragDrop
21040          * @private
21041          * @static
21042          **/
21043         dragCurrent: null,
21044
21045         /**
21046          * the DragDrop object(s) that are being hovered over
21047          * @property dragOvers
21048          * @type Array
21049          * @private
21050          * @static
21051          */
21052         dragOvers: {},
21053
21054         /**
21055          * the X distance between the cursor and the object being dragged
21056          * @property deltaX
21057          * @type int
21058          * @private
21059          * @static
21060          */
21061         deltaX: 0,
21062
21063         /**
21064          * the Y distance between the cursor and the object being dragged
21065          * @property deltaY
21066          * @type int
21067          * @private
21068          * @static
21069          */
21070         deltaY: 0,
21071
21072         /**
21073          * Flag to determine if we should prevent the default behavior of the
21074          * events we define. By default this is true, but this can be set to
21075          * false if you need the default behavior (not recommended)
21076          * @property preventDefault
21077          * @type boolean
21078          * @static
21079          */
21080         preventDefault: true,
21081
21082         /**
21083          * Flag to determine if we should stop the propagation of the events
21084          * we generate. This is true by default but you may want to set it to
21085          * false if the html element contains other features that require the
21086          * mouse click.
21087          * @property stopPropagation
21088          * @type boolean
21089          * @static
21090          */
21091         stopPropagation: true,
21092
21093         /**
21094          * Internal flag that is set to true when drag and drop has been
21095          * intialized
21096          * @property initialized
21097          * @private
21098          * @static
21099          */
21100         initalized: false,
21101
21102         /**
21103          * All drag and drop can be disabled.
21104          * @property locked
21105          * @private
21106          * @static
21107          */
21108         locked: false,
21109
21110         /**
21111          * Called the first time an element is registered.
21112          * @method init
21113          * @private
21114          * @static
21115          */
21116         init: function() {
21117             this.initialized = true;
21118         },
21119
21120         /**
21121          * In point mode, drag and drop interaction is defined by the
21122          * location of the cursor during the drag/drop
21123          * @property POINT
21124          * @type int
21125          * @static
21126          */
21127         POINT: 0,
21128
21129         /**
21130          * In intersect mode, drag and drop interactio nis defined by the
21131          * overlap of two or more drag and drop objects.
21132          * @property INTERSECT
21133          * @type int
21134          * @static
21135          */
21136         INTERSECT: 1,
21137
21138         /**
21139          * The current drag and drop mode.  Default: POINT
21140          * @property mode
21141          * @type int
21142          * @static
21143          */
21144         mode: 0,
21145
21146         /**
21147          * Runs method on all drag and drop objects
21148          * @method _execOnAll
21149          * @private
21150          * @static
21151          */
21152         _execOnAll: function(sMethod, args) {
21153             for (var i in this.ids) {
21154                 for (var j in this.ids[i]) {
21155                     var oDD = this.ids[i][j];
21156                     if (! this.isTypeOfDD(oDD)) {
21157                         continue;
21158                     }
21159                     oDD[sMethod].apply(oDD, args);
21160                 }
21161             }
21162         },
21163
21164         /**
21165          * Drag and drop initialization.  Sets up the global event handlers
21166          * @method _onLoad
21167          * @private
21168          * @static
21169          */
21170         _onLoad: function() {
21171
21172             this.init();
21173
21174             if (!Roo.isTouch) {
21175                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21176                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21177             }
21178             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21179             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21180             
21181             Event.on(window,   "unload",    this._onUnload, this, true);
21182             Event.on(window,   "resize",    this._onResize, this, true);
21183             // Event.on(window,   "mouseout",    this._test);
21184
21185         },
21186
21187         /**
21188          * Reset constraints on all drag and drop objs
21189          * @method _onResize
21190          * @private
21191          * @static
21192          */
21193         _onResize: function(e) {
21194             this._execOnAll("resetConstraints", []);
21195         },
21196
21197         /**
21198          * Lock all drag and drop functionality
21199          * @method lock
21200          * @static
21201          */
21202         lock: function() { this.locked = true; },
21203
21204         /**
21205          * Unlock all drag and drop functionality
21206          * @method unlock
21207          * @static
21208          */
21209         unlock: function() { this.locked = false; },
21210
21211         /**
21212          * Is drag and drop locked?
21213          * @method isLocked
21214          * @return {boolean} True if drag and drop is locked, false otherwise.
21215          * @static
21216          */
21217         isLocked: function() { return this.locked; },
21218
21219         /**
21220          * Location cache that is set for all drag drop objects when a drag is
21221          * initiated, cleared when the drag is finished.
21222          * @property locationCache
21223          * @private
21224          * @static
21225          */
21226         locationCache: {},
21227
21228         /**
21229          * Set useCache to false if you want to force object the lookup of each
21230          * drag and drop linked element constantly during a drag.
21231          * @property useCache
21232          * @type boolean
21233          * @static
21234          */
21235         useCache: true,
21236
21237         /**
21238          * The number of pixels that the mouse needs to move after the
21239          * mousedown before the drag is initiated.  Default=3;
21240          * @property clickPixelThresh
21241          * @type int
21242          * @static
21243          */
21244         clickPixelThresh: 3,
21245
21246         /**
21247          * The number of milliseconds after the mousedown event to initiate the
21248          * drag if we don't get a mouseup event. Default=1000
21249          * @property clickTimeThresh
21250          * @type int
21251          * @static
21252          */
21253         clickTimeThresh: 350,
21254
21255         /**
21256          * Flag that indicates that either the drag pixel threshold or the
21257          * mousdown time threshold has been met
21258          * @property dragThreshMet
21259          * @type boolean
21260          * @private
21261          * @static
21262          */
21263         dragThreshMet: false,
21264
21265         /**
21266          * Timeout used for the click time threshold
21267          * @property clickTimeout
21268          * @type Object
21269          * @private
21270          * @static
21271          */
21272         clickTimeout: null,
21273
21274         /**
21275          * The X position of the mousedown event stored for later use when a
21276          * drag threshold is met.
21277          * @property startX
21278          * @type int
21279          * @private
21280          * @static
21281          */
21282         startX: 0,
21283
21284         /**
21285          * The Y position of the mousedown event stored for later use when a
21286          * drag threshold is met.
21287          * @property startY
21288          * @type int
21289          * @private
21290          * @static
21291          */
21292         startY: 0,
21293
21294         /**
21295          * Each DragDrop instance must be registered with the DragDropMgr.
21296          * This is executed in DragDrop.init()
21297          * @method regDragDrop
21298          * @param {DragDrop} oDD the DragDrop object to register
21299          * @param {String} sGroup the name of the group this element belongs to
21300          * @static
21301          */
21302         regDragDrop: function(oDD, sGroup) {
21303             if (!this.initialized) { this.init(); }
21304
21305             if (!this.ids[sGroup]) {
21306                 this.ids[sGroup] = {};
21307             }
21308             this.ids[sGroup][oDD.id] = oDD;
21309         },
21310
21311         /**
21312          * Removes the supplied dd instance from the supplied group. Executed
21313          * by DragDrop.removeFromGroup, so don't call this function directly.
21314          * @method removeDDFromGroup
21315          * @private
21316          * @static
21317          */
21318         removeDDFromGroup: function(oDD, sGroup) {
21319             if (!this.ids[sGroup]) {
21320                 this.ids[sGroup] = {};
21321             }
21322
21323             var obj = this.ids[sGroup];
21324             if (obj && obj[oDD.id]) {
21325                 delete obj[oDD.id];
21326             }
21327         },
21328
21329         /**
21330          * Unregisters a drag and drop item.  This is executed in
21331          * DragDrop.unreg, use that method instead of calling this directly.
21332          * @method _remove
21333          * @private
21334          * @static
21335          */
21336         _remove: function(oDD) {
21337             for (var g in oDD.groups) {
21338                 if (g && this.ids[g][oDD.id]) {
21339                     delete this.ids[g][oDD.id];
21340                 }
21341             }
21342             delete this.handleIds[oDD.id];
21343         },
21344
21345         /**
21346          * Each DragDrop handle element must be registered.  This is done
21347          * automatically when executing DragDrop.setHandleElId()
21348          * @method regHandle
21349          * @param {String} sDDId the DragDrop id this element is a handle for
21350          * @param {String} sHandleId the id of the element that is the drag
21351          * handle
21352          * @static
21353          */
21354         regHandle: function(sDDId, sHandleId) {
21355             if (!this.handleIds[sDDId]) {
21356                 this.handleIds[sDDId] = {};
21357             }
21358             this.handleIds[sDDId][sHandleId] = sHandleId;
21359         },
21360
21361         /**
21362          * Utility function to determine if a given element has been
21363          * registered as a drag drop item.
21364          * @method isDragDrop
21365          * @param {String} id the element id to check
21366          * @return {boolean} true if this element is a DragDrop item,
21367          * false otherwise
21368          * @static
21369          */
21370         isDragDrop: function(id) {
21371             return ( this.getDDById(id) ) ? true : false;
21372         },
21373
21374         /**
21375          * Returns the drag and drop instances that are in all groups the
21376          * passed in instance belongs to.
21377          * @method getRelated
21378          * @param {DragDrop} p_oDD the obj to get related data for
21379          * @param {boolean} bTargetsOnly if true, only return targetable objs
21380          * @return {DragDrop[]} the related instances
21381          * @static
21382          */
21383         getRelated: function(p_oDD, bTargetsOnly) {
21384             var oDDs = [];
21385             for (var i in p_oDD.groups) {
21386                 for (j in this.ids[i]) {
21387                     var dd = this.ids[i][j];
21388                     if (! this.isTypeOfDD(dd)) {
21389                         continue;
21390                     }
21391                     if (!bTargetsOnly || dd.isTarget) {
21392                         oDDs[oDDs.length] = dd;
21393                     }
21394                 }
21395             }
21396
21397             return oDDs;
21398         },
21399
21400         /**
21401          * Returns true if the specified dd target is a legal target for
21402          * the specifice drag obj
21403          * @method isLegalTarget
21404          * @param {DragDrop} the drag obj
21405          * @param {DragDrop} the target
21406          * @return {boolean} true if the target is a legal target for the
21407          * dd obj
21408          * @static
21409          */
21410         isLegalTarget: function (oDD, oTargetDD) {
21411             var targets = this.getRelated(oDD, true);
21412             for (var i=0, len=targets.length;i<len;++i) {
21413                 if (targets[i].id == oTargetDD.id) {
21414                     return true;
21415                 }
21416             }
21417
21418             return false;
21419         },
21420
21421         /**
21422          * My goal is to be able to transparently determine if an object is
21423          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21424          * returns "object", oDD.constructor.toString() always returns
21425          * "DragDrop" and not the name of the subclass.  So for now it just
21426          * evaluates a well-known variable in DragDrop.
21427          * @method isTypeOfDD
21428          * @param {Object} the object to evaluate
21429          * @return {boolean} true if typeof oDD = DragDrop
21430          * @static
21431          */
21432         isTypeOfDD: function (oDD) {
21433             return (oDD && oDD.__ygDragDrop);
21434         },
21435
21436         /**
21437          * Utility function to determine if a given element has been
21438          * registered as a drag drop handle for the given Drag Drop object.
21439          * @method isHandle
21440          * @param {String} id the element id to check
21441          * @return {boolean} true if this element is a DragDrop handle, false
21442          * otherwise
21443          * @static
21444          */
21445         isHandle: function(sDDId, sHandleId) {
21446             return ( this.handleIds[sDDId] &&
21447                             this.handleIds[sDDId][sHandleId] );
21448         },
21449
21450         /**
21451          * Returns the DragDrop instance for a given id
21452          * @method getDDById
21453          * @param {String} id the id of the DragDrop object
21454          * @return {DragDrop} the drag drop object, null if it is not found
21455          * @static
21456          */
21457         getDDById: function(id) {
21458             for (var i in this.ids) {
21459                 if (this.ids[i][id]) {
21460                     return this.ids[i][id];
21461                 }
21462             }
21463             return null;
21464         },
21465
21466         /**
21467          * Fired after a registered DragDrop object gets the mousedown event.
21468          * Sets up the events required to track the object being dragged
21469          * @method handleMouseDown
21470          * @param {Event} e the event
21471          * @param oDD the DragDrop object being dragged
21472          * @private
21473          * @static
21474          */
21475         handleMouseDown: function(e, oDD) {
21476             if(Roo.QuickTips){
21477                 Roo.QuickTips.disable();
21478             }
21479             this.currentTarget = e.getTarget();
21480
21481             this.dragCurrent = oDD;
21482
21483             var el = oDD.getEl();
21484
21485             // track start position
21486             this.startX = e.getPageX();
21487             this.startY = e.getPageY();
21488
21489             this.deltaX = this.startX - el.offsetLeft;
21490             this.deltaY = this.startY - el.offsetTop;
21491
21492             this.dragThreshMet = false;
21493
21494             this.clickTimeout = setTimeout(
21495                     function() {
21496                         var DDM = Roo.dd.DDM;
21497                         DDM.startDrag(DDM.startX, DDM.startY);
21498                     },
21499                     this.clickTimeThresh );
21500         },
21501
21502         /**
21503          * Fired when either the drag pixel threshol or the mousedown hold
21504          * time threshold has been met.
21505          * @method startDrag
21506          * @param x {int} the X position of the original mousedown
21507          * @param y {int} the Y position of the original mousedown
21508          * @static
21509          */
21510         startDrag: function(x, y) {
21511             clearTimeout(this.clickTimeout);
21512             if (this.dragCurrent) {
21513                 this.dragCurrent.b4StartDrag(x, y);
21514                 this.dragCurrent.startDrag(x, y);
21515             }
21516             this.dragThreshMet = true;
21517         },
21518
21519         /**
21520          * Internal function to handle the mouseup event.  Will be invoked
21521          * from the context of the document.
21522          * @method handleMouseUp
21523          * @param {Event} e the event
21524          * @private
21525          * @static
21526          */
21527         handleMouseUp: function(e) {
21528
21529             if(Roo.QuickTips){
21530                 Roo.QuickTips.enable();
21531             }
21532             if (! this.dragCurrent) {
21533                 return;
21534             }
21535
21536             clearTimeout(this.clickTimeout);
21537
21538             if (this.dragThreshMet) {
21539                 this.fireEvents(e, true);
21540             } else {
21541             }
21542
21543             this.stopDrag(e);
21544
21545             this.stopEvent(e);
21546         },
21547
21548         /**
21549          * Utility to stop event propagation and event default, if these
21550          * features are turned on.
21551          * @method stopEvent
21552          * @param {Event} e the event as returned by this.getEvent()
21553          * @static
21554          */
21555         stopEvent: function(e){
21556             if(this.stopPropagation) {
21557                 e.stopPropagation();
21558             }
21559
21560             if (this.preventDefault) {
21561                 e.preventDefault();
21562             }
21563         },
21564
21565         /**
21566          * Internal function to clean up event handlers after the drag
21567          * operation is complete
21568          * @method stopDrag
21569          * @param {Event} e the event
21570          * @private
21571          * @static
21572          */
21573         stopDrag: function(e) {
21574             // Fire the drag end event for the item that was dragged
21575             if (this.dragCurrent) {
21576                 if (this.dragThreshMet) {
21577                     this.dragCurrent.b4EndDrag(e);
21578                     this.dragCurrent.endDrag(e);
21579                 }
21580
21581                 this.dragCurrent.onMouseUp(e);
21582             }
21583
21584             this.dragCurrent = null;
21585             this.dragOvers = {};
21586         },
21587
21588         /**
21589          * Internal function to handle the mousemove event.  Will be invoked
21590          * from the context of the html element.
21591          *
21592          * @TODO figure out what we can do about mouse events lost when the
21593          * user drags objects beyond the window boundary.  Currently we can
21594          * detect this in internet explorer by verifying that the mouse is
21595          * down during the mousemove event.  Firefox doesn't give us the
21596          * button state on the mousemove event.
21597          * @method handleMouseMove
21598          * @param {Event} e the event
21599          * @private
21600          * @static
21601          */
21602         handleMouseMove: function(e) {
21603             if (! this.dragCurrent) {
21604                 return true;
21605             }
21606
21607             // var button = e.which || e.button;
21608
21609             // check for IE mouseup outside of page boundary
21610             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21611                 this.stopEvent(e);
21612                 return this.handleMouseUp(e);
21613             }
21614
21615             if (!this.dragThreshMet) {
21616                 var diffX = Math.abs(this.startX - e.getPageX());
21617                 var diffY = Math.abs(this.startY - e.getPageY());
21618                 if (diffX > this.clickPixelThresh ||
21619                             diffY > this.clickPixelThresh) {
21620                     this.startDrag(this.startX, this.startY);
21621                 }
21622             }
21623
21624             if (this.dragThreshMet) {
21625                 this.dragCurrent.b4Drag(e);
21626                 this.dragCurrent.onDrag(e);
21627                 if(!this.dragCurrent.moveOnly){
21628                     this.fireEvents(e, false);
21629                 }
21630             }
21631
21632             this.stopEvent(e);
21633
21634             return true;
21635         },
21636
21637         /**
21638          * Iterates over all of the DragDrop elements to find ones we are
21639          * hovering over or dropping on
21640          * @method fireEvents
21641          * @param {Event} e the event
21642          * @param {boolean} isDrop is this a drop op or a mouseover op?
21643          * @private
21644          * @static
21645          */
21646         fireEvents: function(e, isDrop) {
21647             var dc = this.dragCurrent;
21648
21649             // If the user did the mouse up outside of the window, we could
21650             // get here even though we have ended the drag.
21651             if (!dc || dc.isLocked()) {
21652                 return;
21653             }
21654
21655             var pt = e.getPoint();
21656
21657             // cache the previous dragOver array
21658             var oldOvers = [];
21659
21660             var outEvts   = [];
21661             var overEvts  = [];
21662             var dropEvts  = [];
21663             var enterEvts = [];
21664
21665             // Check to see if the object(s) we were hovering over is no longer
21666             // being hovered over so we can fire the onDragOut event
21667             for (var i in this.dragOvers) {
21668
21669                 var ddo = this.dragOvers[i];
21670
21671                 if (! this.isTypeOfDD(ddo)) {
21672                     continue;
21673                 }
21674
21675                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21676                     outEvts.push( ddo );
21677                 }
21678
21679                 oldOvers[i] = true;
21680                 delete this.dragOvers[i];
21681             }
21682
21683             for (var sGroup in dc.groups) {
21684
21685                 if ("string" != typeof sGroup) {
21686                     continue;
21687                 }
21688
21689                 for (i in this.ids[sGroup]) {
21690                     var oDD = this.ids[sGroup][i];
21691                     if (! this.isTypeOfDD(oDD)) {
21692                         continue;
21693                     }
21694
21695                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21696                         if (this.isOverTarget(pt, oDD, this.mode)) {
21697                             // look for drop interactions
21698                             if (isDrop) {
21699                                 dropEvts.push( oDD );
21700                             // look for drag enter and drag over interactions
21701                             } else {
21702
21703                                 // initial drag over: dragEnter fires
21704                                 if (!oldOvers[oDD.id]) {
21705                                     enterEvts.push( oDD );
21706                                 // subsequent drag overs: dragOver fires
21707                                 } else {
21708                                     overEvts.push( oDD );
21709                                 }
21710
21711                                 this.dragOvers[oDD.id] = oDD;
21712                             }
21713                         }
21714                     }
21715                 }
21716             }
21717
21718             if (this.mode) {
21719                 if (outEvts.length) {
21720                     dc.b4DragOut(e, outEvts);
21721                     dc.onDragOut(e, outEvts);
21722                 }
21723
21724                 if (enterEvts.length) {
21725                     dc.onDragEnter(e, enterEvts);
21726                 }
21727
21728                 if (overEvts.length) {
21729                     dc.b4DragOver(e, overEvts);
21730                     dc.onDragOver(e, overEvts);
21731                 }
21732
21733                 if (dropEvts.length) {
21734                     dc.b4DragDrop(e, dropEvts);
21735                     dc.onDragDrop(e, dropEvts);
21736                 }
21737
21738             } else {
21739                 // fire dragout events
21740                 var len = 0;
21741                 for (i=0, len=outEvts.length; i<len; ++i) {
21742                     dc.b4DragOut(e, outEvts[i].id);
21743                     dc.onDragOut(e, outEvts[i].id);
21744                 }
21745
21746                 // fire enter events
21747                 for (i=0,len=enterEvts.length; i<len; ++i) {
21748                     // dc.b4DragEnter(e, oDD.id);
21749                     dc.onDragEnter(e, enterEvts[i].id);
21750                 }
21751
21752                 // fire over events
21753                 for (i=0,len=overEvts.length; i<len; ++i) {
21754                     dc.b4DragOver(e, overEvts[i].id);
21755                     dc.onDragOver(e, overEvts[i].id);
21756                 }
21757
21758                 // fire drop events
21759                 for (i=0, len=dropEvts.length; i<len; ++i) {
21760                     dc.b4DragDrop(e, dropEvts[i].id);
21761                     dc.onDragDrop(e, dropEvts[i].id);
21762                 }
21763
21764             }
21765
21766             // notify about a drop that did not find a target
21767             if (isDrop && !dropEvts.length) {
21768                 dc.onInvalidDrop(e);
21769             }
21770
21771         },
21772
21773         /**
21774          * Helper function for getting the best match from the list of drag
21775          * and drop objects returned by the drag and drop events when we are
21776          * in INTERSECT mode.  It returns either the first object that the
21777          * cursor is over, or the object that has the greatest overlap with
21778          * the dragged element.
21779          * @method getBestMatch
21780          * @param  {DragDrop[]} dds The array of drag and drop objects
21781          * targeted
21782          * @return {DragDrop}       The best single match
21783          * @static
21784          */
21785         getBestMatch: function(dds) {
21786             var winner = null;
21787             // Return null if the input is not what we expect
21788             //if (!dds || !dds.length || dds.length == 0) {
21789                // winner = null;
21790             // If there is only one item, it wins
21791             //} else if (dds.length == 1) {
21792
21793             var len = dds.length;
21794
21795             if (len == 1) {
21796                 winner = dds[0];
21797             } else {
21798                 // Loop through the targeted items
21799                 for (var i=0; i<len; ++i) {
21800                     var dd = dds[i];
21801                     // If the cursor is over the object, it wins.  If the
21802                     // cursor is over multiple matches, the first one we come
21803                     // to wins.
21804                     if (dd.cursorIsOver) {
21805                         winner = dd;
21806                         break;
21807                     // Otherwise the object with the most overlap wins
21808                     } else {
21809                         if (!winner ||
21810                             winner.overlap.getArea() < dd.overlap.getArea()) {
21811                             winner = dd;
21812                         }
21813                     }
21814                 }
21815             }
21816
21817             return winner;
21818         },
21819
21820         /**
21821          * Refreshes the cache of the top-left and bottom-right points of the
21822          * drag and drop objects in the specified group(s).  This is in the
21823          * format that is stored in the drag and drop instance, so typical
21824          * usage is:
21825          * <code>
21826          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21827          * </code>
21828          * Alternatively:
21829          * <code>
21830          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21831          * </code>
21832          * @TODO this really should be an indexed array.  Alternatively this
21833          * method could accept both.
21834          * @method refreshCache
21835          * @param {Object} groups an associative array of groups to refresh
21836          * @static
21837          */
21838         refreshCache: function(groups) {
21839             for (var sGroup in groups) {
21840                 if ("string" != typeof sGroup) {
21841                     continue;
21842                 }
21843                 for (var i in this.ids[sGroup]) {
21844                     var oDD = this.ids[sGroup][i];
21845
21846                     if (this.isTypeOfDD(oDD)) {
21847                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21848                         var loc = this.getLocation(oDD);
21849                         if (loc) {
21850                             this.locationCache[oDD.id] = loc;
21851                         } else {
21852                             delete this.locationCache[oDD.id];
21853                             // this will unregister the drag and drop object if
21854                             // the element is not in a usable state
21855                             // oDD.unreg();
21856                         }
21857                     }
21858                 }
21859             }
21860         },
21861
21862         /**
21863          * This checks to make sure an element exists and is in the DOM.  The
21864          * main purpose is to handle cases where innerHTML is used to remove
21865          * drag and drop objects from the DOM.  IE provides an 'unspecified
21866          * error' when trying to access the offsetParent of such an element
21867          * @method verifyEl
21868          * @param {HTMLElement} el the element to check
21869          * @return {boolean} true if the element looks usable
21870          * @static
21871          */
21872         verifyEl: function(el) {
21873             if (el) {
21874                 var parent;
21875                 if(Roo.isIE){
21876                     try{
21877                         parent = el.offsetParent;
21878                     }catch(e){}
21879                 }else{
21880                     parent = el.offsetParent;
21881                 }
21882                 if (parent) {
21883                     return true;
21884                 }
21885             }
21886
21887             return false;
21888         },
21889
21890         /**
21891          * Returns a Region object containing the drag and drop element's position
21892          * and size, including the padding configured for it
21893          * @method getLocation
21894          * @param {DragDrop} oDD the drag and drop object to get the
21895          *                       location for
21896          * @return {Roo.lib.Region} a Region object representing the total area
21897          *                             the element occupies, including any padding
21898          *                             the instance is configured for.
21899          * @static
21900          */
21901         getLocation: function(oDD) {
21902             if (! this.isTypeOfDD(oDD)) {
21903                 return null;
21904             }
21905
21906             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21907
21908             try {
21909                 pos= Roo.lib.Dom.getXY(el);
21910             } catch (e) { }
21911
21912             if (!pos) {
21913                 return null;
21914             }
21915
21916             x1 = pos[0];
21917             x2 = x1 + el.offsetWidth;
21918             y1 = pos[1];
21919             y2 = y1 + el.offsetHeight;
21920
21921             t = y1 - oDD.padding[0];
21922             r = x2 + oDD.padding[1];
21923             b = y2 + oDD.padding[2];
21924             l = x1 - oDD.padding[3];
21925
21926             return new Roo.lib.Region( t, r, b, l );
21927         },
21928
21929         /**
21930          * Checks the cursor location to see if it over the target
21931          * @method isOverTarget
21932          * @param {Roo.lib.Point} pt The point to evaluate
21933          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21934          * @return {boolean} true if the mouse is over the target
21935          * @private
21936          * @static
21937          */
21938         isOverTarget: function(pt, oTarget, intersect) {
21939             // use cache if available
21940             var loc = this.locationCache[oTarget.id];
21941             if (!loc || !this.useCache) {
21942                 loc = this.getLocation(oTarget);
21943                 this.locationCache[oTarget.id] = loc;
21944
21945             }
21946
21947             if (!loc) {
21948                 return false;
21949             }
21950
21951             oTarget.cursorIsOver = loc.contains( pt );
21952
21953             // DragDrop is using this as a sanity check for the initial mousedown
21954             // in this case we are done.  In POINT mode, if the drag obj has no
21955             // contraints, we are also done. Otherwise we need to evaluate the
21956             // location of the target as related to the actual location of the
21957             // dragged element.
21958             var dc = this.dragCurrent;
21959             if (!dc || !dc.getTargetCoord ||
21960                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21961                 return oTarget.cursorIsOver;
21962             }
21963
21964             oTarget.overlap = null;
21965
21966             // Get the current location of the drag element, this is the
21967             // location of the mouse event less the delta that represents
21968             // where the original mousedown happened on the element.  We
21969             // need to consider constraints and ticks as well.
21970             var pos = dc.getTargetCoord(pt.x, pt.y);
21971
21972             var el = dc.getDragEl();
21973             var curRegion = new Roo.lib.Region( pos.y,
21974                                                    pos.x + el.offsetWidth,
21975                                                    pos.y + el.offsetHeight,
21976                                                    pos.x );
21977
21978             var overlap = curRegion.intersect(loc);
21979
21980             if (overlap) {
21981                 oTarget.overlap = overlap;
21982                 return (intersect) ? true : oTarget.cursorIsOver;
21983             } else {
21984                 return false;
21985             }
21986         },
21987
21988         /**
21989          * unload event handler
21990          * @method _onUnload
21991          * @private
21992          * @static
21993          */
21994         _onUnload: function(e, me) {
21995             Roo.dd.DragDropMgr.unregAll();
21996         },
21997
21998         /**
21999          * Cleans up the drag and drop events and objects.
22000          * @method unregAll
22001          * @private
22002          * @static
22003          */
22004         unregAll: function() {
22005
22006             if (this.dragCurrent) {
22007                 this.stopDrag();
22008                 this.dragCurrent = null;
22009             }
22010
22011             this._execOnAll("unreg", []);
22012
22013             for (i in this.elementCache) {
22014                 delete this.elementCache[i];
22015             }
22016
22017             this.elementCache = {};
22018             this.ids = {};
22019         },
22020
22021         /**
22022          * A cache of DOM elements
22023          * @property elementCache
22024          * @private
22025          * @static
22026          */
22027         elementCache: {},
22028
22029         /**
22030          * Get the wrapper for the DOM element specified
22031          * @method getElWrapper
22032          * @param {String} id the id of the element to get
22033          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22034          * @private
22035          * @deprecated This wrapper isn't that useful
22036          * @static
22037          */
22038         getElWrapper: function(id) {
22039             var oWrapper = this.elementCache[id];
22040             if (!oWrapper || !oWrapper.el) {
22041                 oWrapper = this.elementCache[id] =
22042                     new this.ElementWrapper(Roo.getDom(id));
22043             }
22044             return oWrapper;
22045         },
22046
22047         /**
22048          * Returns the actual DOM element
22049          * @method getElement
22050          * @param {String} id the id of the elment to get
22051          * @return {Object} The element
22052          * @deprecated use Roo.getDom instead
22053          * @static
22054          */
22055         getElement: function(id) {
22056             return Roo.getDom(id);
22057         },
22058
22059         /**
22060          * Returns the style property for the DOM element (i.e.,
22061          * document.getElById(id).style)
22062          * @method getCss
22063          * @param {String} id the id of the elment to get
22064          * @return {Object} The style property of the element
22065          * @deprecated use Roo.getDom instead
22066          * @static
22067          */
22068         getCss: function(id) {
22069             var el = Roo.getDom(id);
22070             return (el) ? el.style : null;
22071         },
22072
22073         /**
22074          * Inner class for cached elements
22075          * @class DragDropMgr.ElementWrapper
22076          * @for DragDropMgr
22077          * @private
22078          * @deprecated
22079          */
22080         ElementWrapper: function(el) {
22081                 /**
22082                  * The element
22083                  * @property el
22084                  */
22085                 this.el = el || null;
22086                 /**
22087                  * The element id
22088                  * @property id
22089                  */
22090                 this.id = this.el && el.id;
22091                 /**
22092                  * A reference to the style property
22093                  * @property css
22094                  */
22095                 this.css = this.el && el.style;
22096             },
22097
22098         /**
22099          * Returns the X position of an html element
22100          * @method getPosX
22101          * @param el the element for which to get the position
22102          * @return {int} the X coordinate
22103          * @for DragDropMgr
22104          * @deprecated use Roo.lib.Dom.getX instead
22105          * @static
22106          */
22107         getPosX: function(el) {
22108             return Roo.lib.Dom.getX(el);
22109         },
22110
22111         /**
22112          * Returns the Y position of an html element
22113          * @method getPosY
22114          * @param el the element for which to get the position
22115          * @return {int} the Y coordinate
22116          * @deprecated use Roo.lib.Dom.getY instead
22117          * @static
22118          */
22119         getPosY: function(el) {
22120             return Roo.lib.Dom.getY(el);
22121         },
22122
22123         /**
22124          * Swap two nodes.  In IE, we use the native method, for others we
22125          * emulate the IE behavior
22126          * @method swapNode
22127          * @param n1 the first node to swap
22128          * @param n2 the other node to swap
22129          * @static
22130          */
22131         swapNode: function(n1, n2) {
22132             if (n1.swapNode) {
22133                 n1.swapNode(n2);
22134             } else {
22135                 var p = n2.parentNode;
22136                 var s = n2.nextSibling;
22137
22138                 if (s == n1) {
22139                     p.insertBefore(n1, n2);
22140                 } else if (n2 == n1.nextSibling) {
22141                     p.insertBefore(n2, n1);
22142                 } else {
22143                     n1.parentNode.replaceChild(n2, n1);
22144                     p.insertBefore(n1, s);
22145                 }
22146             }
22147         },
22148
22149         /**
22150          * Returns the current scroll position
22151          * @method getScroll
22152          * @private
22153          * @static
22154          */
22155         getScroll: function () {
22156             var t, l, dde=document.documentElement, db=document.body;
22157             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22158                 t = dde.scrollTop;
22159                 l = dde.scrollLeft;
22160             } else if (db) {
22161                 t = db.scrollTop;
22162                 l = db.scrollLeft;
22163             } else {
22164
22165             }
22166             return { top: t, left: l };
22167         },
22168
22169         /**
22170          * Returns the specified element style property
22171          * @method getStyle
22172          * @param {HTMLElement} el          the element
22173          * @param {string}      styleProp   the style property
22174          * @return {string} The value of the style property
22175          * @deprecated use Roo.lib.Dom.getStyle
22176          * @static
22177          */
22178         getStyle: function(el, styleProp) {
22179             return Roo.fly(el).getStyle(styleProp);
22180         },
22181
22182         /**
22183          * Gets the scrollTop
22184          * @method getScrollTop
22185          * @return {int} the document's scrollTop
22186          * @static
22187          */
22188         getScrollTop: function () { return this.getScroll().top; },
22189
22190         /**
22191          * Gets the scrollLeft
22192          * @method getScrollLeft
22193          * @return {int} the document's scrollTop
22194          * @static
22195          */
22196         getScrollLeft: function () { return this.getScroll().left; },
22197
22198         /**
22199          * Sets the x/y position of an element to the location of the
22200          * target element.
22201          * @method moveToEl
22202          * @param {HTMLElement} moveEl      The element to move
22203          * @param {HTMLElement} targetEl    The position reference element
22204          * @static
22205          */
22206         moveToEl: function (moveEl, targetEl) {
22207             var aCoord = Roo.lib.Dom.getXY(targetEl);
22208             Roo.lib.Dom.setXY(moveEl, aCoord);
22209         },
22210
22211         /**
22212          * Numeric array sort function
22213          * @method numericSort
22214          * @static
22215          */
22216         numericSort: function(a, b) { return (a - b); },
22217
22218         /**
22219          * Internal counter
22220          * @property _timeoutCount
22221          * @private
22222          * @static
22223          */
22224         _timeoutCount: 0,
22225
22226         /**
22227          * Trying to make the load order less important.  Without this we get
22228          * an error if this file is loaded before the Event Utility.
22229          * @method _addListeners
22230          * @private
22231          * @static
22232          */
22233         _addListeners: function() {
22234             var DDM = Roo.dd.DDM;
22235             if ( Roo.lib.Event && document ) {
22236                 DDM._onLoad();
22237             } else {
22238                 if (DDM._timeoutCount > 2000) {
22239                 } else {
22240                     setTimeout(DDM._addListeners, 10);
22241                     if (document && document.body) {
22242                         DDM._timeoutCount += 1;
22243                     }
22244                 }
22245             }
22246         },
22247
22248         /**
22249          * Recursively searches the immediate parent and all child nodes for
22250          * the handle element in order to determine wheter or not it was
22251          * clicked.
22252          * @method handleWasClicked
22253          * @param node the html element to inspect
22254          * @static
22255          */
22256         handleWasClicked: function(node, id) {
22257             if (this.isHandle(id, node.id)) {
22258                 return true;
22259             } else {
22260                 // check to see if this is a text node child of the one we want
22261                 var p = node.parentNode;
22262
22263                 while (p) {
22264                     if (this.isHandle(id, p.id)) {
22265                         return true;
22266                     } else {
22267                         p = p.parentNode;
22268                     }
22269                 }
22270             }
22271
22272             return false;
22273         }
22274
22275     };
22276
22277 }();
22278
22279 // shorter alias, save a few bytes
22280 Roo.dd.DDM = Roo.dd.DragDropMgr;
22281 Roo.dd.DDM._addListeners();
22282
22283 }/*
22284  * Based on:
22285  * Ext JS Library 1.1.1
22286  * Copyright(c) 2006-2007, Ext JS, LLC.
22287  *
22288  * Originally Released Under LGPL - original licence link has changed is not relivant.
22289  *
22290  * Fork - LGPL
22291  * <script type="text/javascript">
22292  */
22293
22294 /**
22295  * @class Roo.dd.DD
22296  * A DragDrop implementation where the linked element follows the
22297  * mouse cursor during a drag.
22298  * @extends Roo.dd.DragDrop
22299  * @constructor
22300  * @param {String} id the id of the linked element
22301  * @param {String} sGroup the group of related DragDrop items
22302  * @param {object} config an object containing configurable attributes
22303  *                Valid properties for DD:
22304  *                    scroll
22305  */
22306 Roo.dd.DD = function(id, sGroup, config) {
22307     if (id) {
22308         this.init(id, sGroup, config);
22309     }
22310 };
22311
22312 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22313
22314     /**
22315      * When set to true, the utility automatically tries to scroll the browser
22316      * window wehn a drag and drop element is dragged near the viewport boundary.
22317      * Defaults to true.
22318      * @property scroll
22319      * @type boolean
22320      */
22321     scroll: true,
22322
22323     /**
22324      * Sets the pointer offset to the distance between the linked element's top
22325      * left corner and the location the element was clicked
22326      * @method autoOffset
22327      * @param {int} iPageX the X coordinate of the click
22328      * @param {int} iPageY the Y coordinate of the click
22329      */
22330     autoOffset: function(iPageX, iPageY) {
22331         var x = iPageX - this.startPageX;
22332         var y = iPageY - this.startPageY;
22333         this.setDelta(x, y);
22334     },
22335
22336     /**
22337      * Sets the pointer offset.  You can call this directly to force the
22338      * offset to be in a particular location (e.g., pass in 0,0 to set it
22339      * to the center of the object)
22340      * @method setDelta
22341      * @param {int} iDeltaX the distance from the left
22342      * @param {int} iDeltaY the distance from the top
22343      */
22344     setDelta: function(iDeltaX, iDeltaY) {
22345         this.deltaX = iDeltaX;
22346         this.deltaY = iDeltaY;
22347     },
22348
22349     /**
22350      * Sets the drag element to the location of the mousedown or click event,
22351      * maintaining the cursor location relative to the location on the element
22352      * that was clicked.  Override this if you want to place the element in a
22353      * location other than where the cursor is.
22354      * @method setDragElPos
22355      * @param {int} iPageX the X coordinate of the mousedown or drag event
22356      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22357      */
22358     setDragElPos: function(iPageX, iPageY) {
22359         // the first time we do this, we are going to check to make sure
22360         // the element has css positioning
22361
22362         var el = this.getDragEl();
22363         this.alignElWithMouse(el, iPageX, iPageY);
22364     },
22365
22366     /**
22367      * Sets the element to the location of the mousedown or click event,
22368      * maintaining the cursor location relative to the location on the element
22369      * that was clicked.  Override this if you want to place the element in a
22370      * location other than where the cursor is.
22371      * @method alignElWithMouse
22372      * @param {HTMLElement} el the element to move
22373      * @param {int} iPageX the X coordinate of the mousedown or drag event
22374      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22375      */
22376     alignElWithMouse: function(el, iPageX, iPageY) {
22377         var oCoord = this.getTargetCoord(iPageX, iPageY);
22378         var fly = el.dom ? el : Roo.fly(el);
22379         if (!this.deltaSetXY) {
22380             var aCoord = [oCoord.x, oCoord.y];
22381             fly.setXY(aCoord);
22382             var newLeft = fly.getLeft(true);
22383             var newTop  = fly.getTop(true);
22384             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22385         } else {
22386             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22387         }
22388
22389         this.cachePosition(oCoord.x, oCoord.y);
22390         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22391         return oCoord;
22392     },
22393
22394     /**
22395      * Saves the most recent position so that we can reset the constraints and
22396      * tick marks on-demand.  We need to know this so that we can calculate the
22397      * number of pixels the element is offset from its original position.
22398      * @method cachePosition
22399      * @param iPageX the current x position (optional, this just makes it so we
22400      * don't have to look it up again)
22401      * @param iPageY the current y position (optional, this just makes it so we
22402      * don't have to look it up again)
22403      */
22404     cachePosition: function(iPageX, iPageY) {
22405         if (iPageX) {
22406             this.lastPageX = iPageX;
22407             this.lastPageY = iPageY;
22408         } else {
22409             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22410             this.lastPageX = aCoord[0];
22411             this.lastPageY = aCoord[1];
22412         }
22413     },
22414
22415     /**
22416      * Auto-scroll the window if the dragged object has been moved beyond the
22417      * visible window boundary.
22418      * @method autoScroll
22419      * @param {int} x the drag element's x position
22420      * @param {int} y the drag element's y position
22421      * @param {int} h the height of the drag element
22422      * @param {int} w the width of the drag element
22423      * @private
22424      */
22425     autoScroll: function(x, y, h, w) {
22426
22427         if (this.scroll) {
22428             // The client height
22429             var clientH = Roo.lib.Dom.getViewWidth();
22430
22431             // The client width
22432             var clientW = Roo.lib.Dom.getViewHeight();
22433
22434             // The amt scrolled down
22435             var st = this.DDM.getScrollTop();
22436
22437             // The amt scrolled right
22438             var sl = this.DDM.getScrollLeft();
22439
22440             // Location of the bottom of the element
22441             var bot = h + y;
22442
22443             // Location of the right of the element
22444             var right = w + x;
22445
22446             // The distance from the cursor to the bottom of the visible area,
22447             // adjusted so that we don't scroll if the cursor is beyond the
22448             // element drag constraints
22449             var toBot = (clientH + st - y - this.deltaY);
22450
22451             // The distance from the cursor to the right of the visible area
22452             var toRight = (clientW + sl - x - this.deltaX);
22453
22454
22455             // How close to the edge the cursor must be before we scroll
22456             // var thresh = (document.all) ? 100 : 40;
22457             var thresh = 40;
22458
22459             // How many pixels to scroll per autoscroll op.  This helps to reduce
22460             // clunky scrolling. IE is more sensitive about this ... it needs this
22461             // value to be higher.
22462             var scrAmt = (document.all) ? 80 : 30;
22463
22464             // Scroll down if we are near the bottom of the visible page and the
22465             // obj extends below the crease
22466             if ( bot > clientH && toBot < thresh ) {
22467                 window.scrollTo(sl, st + scrAmt);
22468             }
22469
22470             // Scroll up if the window is scrolled down and the top of the object
22471             // goes above the top border
22472             if ( y < st && st > 0 && y - st < thresh ) {
22473                 window.scrollTo(sl, st - scrAmt);
22474             }
22475
22476             // Scroll right if the obj is beyond the right border and the cursor is
22477             // near the border.
22478             if ( right > clientW && toRight < thresh ) {
22479                 window.scrollTo(sl + scrAmt, st);
22480             }
22481
22482             // Scroll left if the window has been scrolled to the right and the obj
22483             // extends past the left border
22484             if ( x < sl && sl > 0 && x - sl < thresh ) {
22485                 window.scrollTo(sl - scrAmt, st);
22486             }
22487         }
22488     },
22489
22490     /**
22491      * Finds the location the element should be placed if we want to move
22492      * it to where the mouse location less the click offset would place us.
22493      * @method getTargetCoord
22494      * @param {int} iPageX the X coordinate of the click
22495      * @param {int} iPageY the Y coordinate of the click
22496      * @return an object that contains the coordinates (Object.x and Object.y)
22497      * @private
22498      */
22499     getTargetCoord: function(iPageX, iPageY) {
22500
22501
22502         var x = iPageX - this.deltaX;
22503         var y = iPageY - this.deltaY;
22504
22505         if (this.constrainX) {
22506             if (x < this.minX) { x = this.minX; }
22507             if (x > this.maxX) { x = this.maxX; }
22508         }
22509
22510         if (this.constrainY) {
22511             if (y < this.minY) { y = this.minY; }
22512             if (y > this.maxY) { y = this.maxY; }
22513         }
22514
22515         x = this.getTick(x, this.xTicks);
22516         y = this.getTick(y, this.yTicks);
22517
22518
22519         return {x:x, y:y};
22520     },
22521
22522     /*
22523      * Sets up config options specific to this class. Overrides
22524      * Roo.dd.DragDrop, but all versions of this method through the
22525      * inheritance chain are called
22526      */
22527     applyConfig: function() {
22528         Roo.dd.DD.superclass.applyConfig.call(this);
22529         this.scroll = (this.config.scroll !== false);
22530     },
22531
22532     /*
22533      * Event that fires prior to the onMouseDown event.  Overrides
22534      * Roo.dd.DragDrop.
22535      */
22536     b4MouseDown: function(e) {
22537         // this.resetConstraints();
22538         this.autoOffset(e.getPageX(),
22539                             e.getPageY());
22540     },
22541
22542     /*
22543      * Event that fires prior to the onDrag event.  Overrides
22544      * Roo.dd.DragDrop.
22545      */
22546     b4Drag: function(e) {
22547         this.setDragElPos(e.getPageX(),
22548                             e.getPageY());
22549     },
22550
22551     toString: function() {
22552         return ("DD " + this.id);
22553     }
22554
22555     //////////////////////////////////////////////////////////////////////////
22556     // Debugging ygDragDrop events that can be overridden
22557     //////////////////////////////////////////////////////////////////////////
22558     /*
22559     startDrag: function(x, y) {
22560     },
22561
22562     onDrag: function(e) {
22563     },
22564
22565     onDragEnter: function(e, id) {
22566     },
22567
22568     onDragOver: function(e, id) {
22569     },
22570
22571     onDragOut: function(e, id) {
22572     },
22573
22574     onDragDrop: function(e, id) {
22575     },
22576
22577     endDrag: function(e) {
22578     }
22579
22580     */
22581
22582 });/*
22583  * Based on:
22584  * Ext JS Library 1.1.1
22585  * Copyright(c) 2006-2007, Ext JS, LLC.
22586  *
22587  * Originally Released Under LGPL - original licence link has changed is not relivant.
22588  *
22589  * Fork - LGPL
22590  * <script type="text/javascript">
22591  */
22592
22593 /**
22594  * @class Roo.dd.DDProxy
22595  * A DragDrop implementation that inserts an empty, bordered div into
22596  * the document that follows the cursor during drag operations.  At the time of
22597  * the click, the frame div is resized to the dimensions of the linked html
22598  * element, and moved to the exact location of the linked element.
22599  *
22600  * References to the "frame" element refer to the single proxy element that
22601  * was created to be dragged in place of all DDProxy elements on the
22602  * page.
22603  *
22604  * @extends Roo.dd.DD
22605  * @constructor
22606  * @param {String} id the id of the linked html element
22607  * @param {String} sGroup the group of related DragDrop objects
22608  * @param {object} config an object containing configurable attributes
22609  *                Valid properties for DDProxy in addition to those in DragDrop:
22610  *                   resizeFrame, centerFrame, dragElId
22611  */
22612 Roo.dd.DDProxy = function(id, sGroup, config) {
22613     if (id) {
22614         this.init(id, sGroup, config);
22615         this.initFrame();
22616     }
22617 };
22618
22619 /**
22620  * The default drag frame div id
22621  * @property Roo.dd.DDProxy.dragElId
22622  * @type String
22623  * @static
22624  */
22625 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22626
22627 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22628
22629     /**
22630      * By default we resize the drag frame to be the same size as the element
22631      * we want to drag (this is to get the frame effect).  We can turn it off
22632      * if we want a different behavior.
22633      * @property resizeFrame
22634      * @type boolean
22635      */
22636     resizeFrame: true,
22637
22638     /**
22639      * By default the frame is positioned exactly where the drag element is, so
22640      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22641      * you do not have constraints on the obj is to have the drag frame centered
22642      * around the cursor.  Set centerFrame to true for this effect.
22643      * @property centerFrame
22644      * @type boolean
22645      */
22646     centerFrame: false,
22647
22648     /**
22649      * Creates the proxy element if it does not yet exist
22650      * @method createFrame
22651      */
22652     createFrame: function() {
22653         var self = this;
22654         var body = document.body;
22655
22656         if (!body || !body.firstChild) {
22657             setTimeout( function() { self.createFrame(); }, 50 );
22658             return;
22659         }
22660
22661         var div = this.getDragEl();
22662
22663         if (!div) {
22664             div    = document.createElement("div");
22665             div.id = this.dragElId;
22666             var s  = div.style;
22667
22668             s.position   = "absolute";
22669             s.visibility = "hidden";
22670             s.cursor     = "move";
22671             s.border     = "2px solid #aaa";
22672             s.zIndex     = 999;
22673
22674             // appendChild can blow up IE if invoked prior to the window load event
22675             // while rendering a table.  It is possible there are other scenarios
22676             // that would cause this to happen as well.
22677             body.insertBefore(div, body.firstChild);
22678         }
22679     },
22680
22681     /**
22682      * Initialization for the drag frame element.  Must be called in the
22683      * constructor of all subclasses
22684      * @method initFrame
22685      */
22686     initFrame: function() {
22687         this.createFrame();
22688     },
22689
22690     applyConfig: function() {
22691         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22692
22693         this.resizeFrame = (this.config.resizeFrame !== false);
22694         this.centerFrame = (this.config.centerFrame);
22695         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22696     },
22697
22698     /**
22699      * Resizes the drag frame to the dimensions of the clicked object, positions
22700      * it over the object, and finally displays it
22701      * @method showFrame
22702      * @param {int} iPageX X click position
22703      * @param {int} iPageY Y click position
22704      * @private
22705      */
22706     showFrame: function(iPageX, iPageY) {
22707         var el = this.getEl();
22708         var dragEl = this.getDragEl();
22709         var s = dragEl.style;
22710
22711         this._resizeProxy();
22712
22713         if (this.centerFrame) {
22714             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22715                            Math.round(parseInt(s.height, 10)/2) );
22716         }
22717
22718         this.setDragElPos(iPageX, iPageY);
22719
22720         Roo.fly(dragEl).show();
22721     },
22722
22723     /**
22724      * The proxy is automatically resized to the dimensions of the linked
22725      * element when a drag is initiated, unless resizeFrame is set to false
22726      * @method _resizeProxy
22727      * @private
22728      */
22729     _resizeProxy: function() {
22730         if (this.resizeFrame) {
22731             var el = this.getEl();
22732             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22733         }
22734     },
22735
22736     // overrides Roo.dd.DragDrop
22737     b4MouseDown: function(e) {
22738         var x = e.getPageX();
22739         var y = e.getPageY();
22740         this.autoOffset(x, y);
22741         this.setDragElPos(x, y);
22742     },
22743
22744     // overrides Roo.dd.DragDrop
22745     b4StartDrag: function(x, y) {
22746         // show the drag frame
22747         this.showFrame(x, y);
22748     },
22749
22750     // overrides Roo.dd.DragDrop
22751     b4EndDrag: function(e) {
22752         Roo.fly(this.getDragEl()).hide();
22753     },
22754
22755     // overrides Roo.dd.DragDrop
22756     // By default we try to move the element to the last location of the frame.
22757     // This is so that the default behavior mirrors that of Roo.dd.DD.
22758     endDrag: function(e) {
22759
22760         var lel = this.getEl();
22761         var del = this.getDragEl();
22762
22763         // Show the drag frame briefly so we can get its position
22764         del.style.visibility = "";
22765
22766         this.beforeMove();
22767         // Hide the linked element before the move to get around a Safari
22768         // rendering bug.
22769         lel.style.visibility = "hidden";
22770         Roo.dd.DDM.moveToEl(lel, del);
22771         del.style.visibility = "hidden";
22772         lel.style.visibility = "";
22773
22774         this.afterDrag();
22775     },
22776
22777     beforeMove : function(){
22778
22779     },
22780
22781     afterDrag : function(){
22782
22783     },
22784
22785     toString: function() {
22786         return ("DDProxy " + this.id);
22787     }
22788
22789 });
22790 /*
22791  * Based on:
22792  * Ext JS Library 1.1.1
22793  * Copyright(c) 2006-2007, Ext JS, LLC.
22794  *
22795  * Originally Released Under LGPL - original licence link has changed is not relivant.
22796  *
22797  * Fork - LGPL
22798  * <script type="text/javascript">
22799  */
22800
22801  /**
22802  * @class Roo.dd.DDTarget
22803  * A DragDrop implementation that does not move, but can be a drop
22804  * target.  You would get the same result by simply omitting implementation
22805  * for the event callbacks, but this way we reduce the processing cost of the
22806  * event listener and the callbacks.
22807  * @extends Roo.dd.DragDrop
22808  * @constructor
22809  * @param {String} id the id of the element that is a drop target
22810  * @param {String} sGroup the group of related DragDrop objects
22811  * @param {object} config an object containing configurable attributes
22812  *                 Valid properties for DDTarget in addition to those in
22813  *                 DragDrop:
22814  *                    none
22815  */
22816 Roo.dd.DDTarget = function(id, sGroup, config) {
22817     if (id) {
22818         this.initTarget(id, sGroup, config);
22819     }
22820     if (config && (config.listeners || config.events)) { 
22821         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22822             listeners : config.listeners || {}, 
22823             events : config.events || {} 
22824         });    
22825     }
22826 };
22827
22828 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22829 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22830     toString: function() {
22831         return ("DDTarget " + this.id);
22832     }
22833 });
22834 /*
22835  * Based on:
22836  * Ext JS Library 1.1.1
22837  * Copyright(c) 2006-2007, Ext JS, LLC.
22838  *
22839  * Originally Released Under LGPL - original licence link has changed is not relivant.
22840  *
22841  * Fork - LGPL
22842  * <script type="text/javascript">
22843  */
22844  
22845
22846 /**
22847  * @class Roo.dd.ScrollManager
22848  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22849  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22850  * @static
22851  */
22852 Roo.dd.ScrollManager = function(){
22853     var ddm = Roo.dd.DragDropMgr;
22854     var els = {};
22855     var dragEl = null;
22856     var proc = {};
22857     
22858     
22859     
22860     var onStop = function(e){
22861         dragEl = null;
22862         clearProc();
22863     };
22864     
22865     var triggerRefresh = function(){
22866         if(ddm.dragCurrent){
22867              ddm.refreshCache(ddm.dragCurrent.groups);
22868         }
22869     };
22870     
22871     var doScroll = function(){
22872         if(ddm.dragCurrent){
22873             var dds = Roo.dd.ScrollManager;
22874             if(!dds.animate){
22875                 if(proc.el.scroll(proc.dir, dds.increment)){
22876                     triggerRefresh();
22877                 }
22878             }else{
22879                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22880             }
22881         }
22882     };
22883     
22884     var clearProc = function(){
22885         if(proc.id){
22886             clearInterval(proc.id);
22887         }
22888         proc.id = 0;
22889         proc.el = null;
22890         proc.dir = "";
22891     };
22892     
22893     var startProc = function(el, dir){
22894          Roo.log('scroll startproc');
22895         clearProc();
22896         proc.el = el;
22897         proc.dir = dir;
22898         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22899     };
22900     
22901     var onFire = function(e, isDrop){
22902        
22903         if(isDrop || !ddm.dragCurrent){ return; }
22904         var dds = Roo.dd.ScrollManager;
22905         if(!dragEl || dragEl != ddm.dragCurrent){
22906             dragEl = ddm.dragCurrent;
22907             // refresh regions on drag start
22908             dds.refreshCache();
22909         }
22910         
22911         var xy = Roo.lib.Event.getXY(e);
22912         var pt = new Roo.lib.Point(xy[0], xy[1]);
22913         for(var id in els){
22914             var el = els[id], r = el._region;
22915             if(r && r.contains(pt) && el.isScrollable()){
22916                 if(r.bottom - pt.y <= dds.thresh){
22917                     if(proc.el != el){
22918                         startProc(el, "down");
22919                     }
22920                     return;
22921                 }else if(r.right - pt.x <= dds.thresh){
22922                     if(proc.el != el){
22923                         startProc(el, "left");
22924                     }
22925                     return;
22926                 }else if(pt.y - r.top <= dds.thresh){
22927                     if(proc.el != el){
22928                         startProc(el, "up");
22929                     }
22930                     return;
22931                 }else if(pt.x - r.left <= dds.thresh){
22932                     if(proc.el != el){
22933                         startProc(el, "right");
22934                     }
22935                     return;
22936                 }
22937             }
22938         }
22939         clearProc();
22940     };
22941     
22942     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22943     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22944     
22945     return {
22946         /**
22947          * Registers new overflow element(s) to auto scroll
22948          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22949          */
22950         register : function(el){
22951             if(el instanceof Array){
22952                 for(var i = 0, len = el.length; i < len; i++) {
22953                         this.register(el[i]);
22954                 }
22955             }else{
22956                 el = Roo.get(el);
22957                 els[el.id] = el;
22958             }
22959             Roo.dd.ScrollManager.els = els;
22960         },
22961         
22962         /**
22963          * Unregisters overflow element(s) so they are no longer scrolled
22964          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22965          */
22966         unregister : function(el){
22967             if(el instanceof Array){
22968                 for(var i = 0, len = el.length; i < len; i++) {
22969                         this.unregister(el[i]);
22970                 }
22971             }else{
22972                 el = Roo.get(el);
22973                 delete els[el.id];
22974             }
22975         },
22976         
22977         /**
22978          * The number of pixels from the edge of a container the pointer needs to be to 
22979          * trigger scrolling (defaults to 25)
22980          * @type Number
22981          */
22982         thresh : 25,
22983         
22984         /**
22985          * The number of pixels to scroll in each scroll increment (defaults to 50)
22986          * @type Number
22987          */
22988         increment : 100,
22989         
22990         /**
22991          * The frequency of scrolls in milliseconds (defaults to 500)
22992          * @type Number
22993          */
22994         frequency : 500,
22995         
22996         /**
22997          * True to animate the scroll (defaults to true)
22998          * @type Boolean
22999          */
23000         animate: true,
23001         
23002         /**
23003          * The animation duration in seconds - 
23004          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23005          * @type Number
23006          */
23007         animDuration: .4,
23008         
23009         /**
23010          * Manually trigger a cache refresh.
23011          */
23012         refreshCache : function(){
23013             for(var id in els){
23014                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23015                     els[id]._region = els[id].getRegion();
23016                 }
23017             }
23018         }
23019     };
23020 }();/*
23021  * Based on:
23022  * Ext JS Library 1.1.1
23023  * Copyright(c) 2006-2007, Ext JS, LLC.
23024  *
23025  * Originally Released Under LGPL - original licence link has changed is not relivant.
23026  *
23027  * Fork - LGPL
23028  * <script type="text/javascript">
23029  */
23030  
23031
23032 /**
23033  * @class Roo.dd.Registry
23034  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23035  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23036  * @static
23037  */
23038 Roo.dd.Registry = function(){
23039     var elements = {}; 
23040     var handles = {}; 
23041     var autoIdSeed = 0;
23042
23043     var getId = function(el, autogen){
23044         if(typeof el == "string"){
23045             return el;
23046         }
23047         var id = el.id;
23048         if(!id && autogen !== false){
23049             id = "roodd-" + (++autoIdSeed);
23050             el.id = id;
23051         }
23052         return id;
23053     };
23054     
23055     return {
23056     /**
23057      * Register a drag drop element
23058      * @param {String|HTMLElement} element The id or DOM node to register
23059      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23060      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23061      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23062      * populated in the data object (if applicable):
23063      * <pre>
23064 Value      Description<br />
23065 ---------  ------------------------------------------<br />
23066 handles    Array of DOM nodes that trigger dragging<br />
23067            for the element being registered<br />
23068 isHandle   True if the element passed in triggers<br />
23069            dragging itself, else false
23070 </pre>
23071      */
23072         register : function(el, data){
23073             data = data || {};
23074             if(typeof el == "string"){
23075                 el = document.getElementById(el);
23076             }
23077             data.ddel = el;
23078             elements[getId(el)] = data;
23079             if(data.isHandle !== false){
23080                 handles[data.ddel.id] = data;
23081             }
23082             if(data.handles){
23083                 var hs = data.handles;
23084                 for(var i = 0, len = hs.length; i < len; i++){
23085                         handles[getId(hs[i])] = data;
23086                 }
23087             }
23088         },
23089
23090     /**
23091      * Unregister a drag drop element
23092      * @param {String|HTMLElement}  element The id or DOM node to unregister
23093      */
23094         unregister : function(el){
23095             var id = getId(el, false);
23096             var data = elements[id];
23097             if(data){
23098                 delete elements[id];
23099                 if(data.handles){
23100                     var hs = data.handles;
23101                     for(var i = 0, len = hs.length; i < len; i++){
23102                         delete handles[getId(hs[i], false)];
23103                     }
23104                 }
23105             }
23106         },
23107
23108     /**
23109      * Returns the handle registered for a DOM Node by id
23110      * @param {String|HTMLElement} id The DOM node or id to look up
23111      * @return {Object} handle The custom handle data
23112      */
23113         getHandle : function(id){
23114             if(typeof id != "string"){ // must be element?
23115                 id = id.id;
23116             }
23117             return handles[id];
23118         },
23119
23120     /**
23121      * Returns the handle that is registered for the DOM node that is the target of the event
23122      * @param {Event} e The event
23123      * @return {Object} handle The custom handle data
23124      */
23125         getHandleFromEvent : function(e){
23126             var t = Roo.lib.Event.getTarget(e);
23127             return t ? handles[t.id] : null;
23128         },
23129
23130     /**
23131      * Returns a custom data object that is registered for a DOM node by id
23132      * @param {String|HTMLElement} id The DOM node or id to look up
23133      * @return {Object} data The custom data
23134      */
23135         getTarget : function(id){
23136             if(typeof id != "string"){ // must be element?
23137                 id = id.id;
23138             }
23139             return elements[id];
23140         },
23141
23142     /**
23143      * Returns a custom data object that is registered for the DOM node that is the target of the event
23144      * @param {Event} e The event
23145      * @return {Object} data The custom data
23146      */
23147         getTargetFromEvent : function(e){
23148             var t = Roo.lib.Event.getTarget(e);
23149             return t ? elements[t.id] || handles[t.id] : null;
23150         }
23151     };
23152 }();/*
23153  * Based on:
23154  * Ext JS Library 1.1.1
23155  * Copyright(c) 2006-2007, Ext JS, LLC.
23156  *
23157  * Originally Released Under LGPL - original licence link has changed is not relivant.
23158  *
23159  * Fork - LGPL
23160  * <script type="text/javascript">
23161  */
23162  
23163
23164 /**
23165  * @class Roo.dd.StatusProxy
23166  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23167  * default drag proxy used by all Roo.dd components.
23168  * @constructor
23169  * @param {Object} config
23170  */
23171 Roo.dd.StatusProxy = function(config){
23172     Roo.apply(this, config);
23173     this.id = this.id || Roo.id();
23174     this.el = new Roo.Layer({
23175         dh: {
23176             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23177                 {tag: "div", cls: "x-dd-drop-icon"},
23178                 {tag: "div", cls: "x-dd-drag-ghost"}
23179             ]
23180         }, 
23181         shadow: !config || config.shadow !== false
23182     });
23183     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23184     this.dropStatus = this.dropNotAllowed;
23185 };
23186
23187 Roo.dd.StatusProxy.prototype = {
23188     /**
23189      * @cfg {String} dropAllowed
23190      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23191      */
23192     dropAllowed : "x-dd-drop-ok",
23193     /**
23194      * @cfg {String} dropNotAllowed
23195      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23196      */
23197     dropNotAllowed : "x-dd-drop-nodrop",
23198
23199     /**
23200      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23201      * over the current target element.
23202      * @param {String} cssClass The css class for the new drop status indicator image
23203      */
23204     setStatus : function(cssClass){
23205         cssClass = cssClass || this.dropNotAllowed;
23206         if(this.dropStatus != cssClass){
23207             this.el.replaceClass(this.dropStatus, cssClass);
23208             this.dropStatus = cssClass;
23209         }
23210     },
23211
23212     /**
23213      * Resets the status indicator to the default dropNotAllowed value
23214      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23215      */
23216     reset : function(clearGhost){
23217         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23218         this.dropStatus = this.dropNotAllowed;
23219         if(clearGhost){
23220             this.ghost.update("");
23221         }
23222     },
23223
23224     /**
23225      * Updates the contents of the ghost element
23226      * @param {String} html The html that will replace the current innerHTML of the ghost element
23227      */
23228     update : function(html){
23229         if(typeof html == "string"){
23230             this.ghost.update(html);
23231         }else{
23232             this.ghost.update("");
23233             html.style.margin = "0";
23234             this.ghost.dom.appendChild(html);
23235         }
23236         // ensure float = none set?? cant remember why though.
23237         var el = this.ghost.dom.firstChild;
23238                 if(el){
23239                         Roo.fly(el).setStyle('float', 'none');
23240                 }
23241     },
23242     
23243     /**
23244      * Returns the underlying proxy {@link Roo.Layer}
23245      * @return {Roo.Layer} el
23246     */
23247     getEl : function(){
23248         return this.el;
23249     },
23250
23251     /**
23252      * Returns the ghost element
23253      * @return {Roo.Element} el
23254      */
23255     getGhost : function(){
23256         return this.ghost;
23257     },
23258
23259     /**
23260      * Hides the proxy
23261      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23262      */
23263     hide : function(clear){
23264         this.el.hide();
23265         if(clear){
23266             this.reset(true);
23267         }
23268     },
23269
23270     /**
23271      * Stops the repair animation if it's currently running
23272      */
23273     stop : function(){
23274         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23275             this.anim.stop();
23276         }
23277     },
23278
23279     /**
23280      * Displays this proxy
23281      */
23282     show : function(){
23283         this.el.show();
23284     },
23285
23286     /**
23287      * Force the Layer to sync its shadow and shim positions to the element
23288      */
23289     sync : function(){
23290         this.el.sync();
23291     },
23292
23293     /**
23294      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23295      * invalid drop operation by the item being dragged.
23296      * @param {Array} xy The XY position of the element ([x, y])
23297      * @param {Function} callback The function to call after the repair is complete
23298      * @param {Object} scope The scope in which to execute the callback
23299      */
23300     repair : function(xy, callback, scope){
23301         this.callback = callback;
23302         this.scope = scope;
23303         if(xy && this.animRepair !== false){
23304             this.el.addClass("x-dd-drag-repair");
23305             this.el.hideUnders(true);
23306             this.anim = this.el.shift({
23307                 duration: this.repairDuration || .5,
23308                 easing: 'easeOut',
23309                 xy: xy,
23310                 stopFx: true,
23311                 callback: this.afterRepair,
23312                 scope: this
23313             });
23314         }else{
23315             this.afterRepair();
23316         }
23317     },
23318
23319     // private
23320     afterRepair : function(){
23321         this.hide(true);
23322         if(typeof this.callback == "function"){
23323             this.callback.call(this.scope || this);
23324         }
23325         this.callback = null;
23326         this.scope = null;
23327     }
23328 };/*
23329  * Based on:
23330  * Ext JS Library 1.1.1
23331  * Copyright(c) 2006-2007, Ext JS, LLC.
23332  *
23333  * Originally Released Under LGPL - original licence link has changed is not relivant.
23334  *
23335  * Fork - LGPL
23336  * <script type="text/javascript">
23337  */
23338
23339 /**
23340  * @class Roo.dd.DragSource
23341  * @extends Roo.dd.DDProxy
23342  * A simple class that provides the basic implementation needed to make any element draggable.
23343  * @constructor
23344  * @param {String/HTMLElement/Element} el The container element
23345  * @param {Object} config
23346  */
23347 Roo.dd.DragSource = function(el, config){
23348     this.el = Roo.get(el);
23349     this.dragData = {};
23350     
23351     Roo.apply(this, config);
23352     
23353     if(!this.proxy){
23354         this.proxy = new Roo.dd.StatusProxy();
23355     }
23356
23357     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23358           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23359     
23360     this.dragging = false;
23361 };
23362
23363 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23364     /**
23365      * @cfg {String} dropAllowed
23366      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23367      */
23368     dropAllowed : "x-dd-drop-ok",
23369     /**
23370      * @cfg {String} dropNotAllowed
23371      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23372      */
23373     dropNotAllowed : "x-dd-drop-nodrop",
23374
23375     /**
23376      * Returns the data object associated with this drag source
23377      * @return {Object} data An object containing arbitrary data
23378      */
23379     getDragData : function(e){
23380         return this.dragData;
23381     },
23382
23383     // private
23384     onDragEnter : function(e, id){
23385         var target = Roo.dd.DragDropMgr.getDDById(id);
23386         this.cachedTarget = target;
23387         if(this.beforeDragEnter(target, e, id) !== false){
23388             if(target.isNotifyTarget){
23389                 var status = target.notifyEnter(this, e, this.dragData);
23390                 this.proxy.setStatus(status);
23391             }else{
23392                 this.proxy.setStatus(this.dropAllowed);
23393             }
23394             
23395             if(this.afterDragEnter){
23396                 /**
23397                  * An empty function by default, but provided so that you can perform a custom action
23398                  * when the dragged item enters the drop target by providing an implementation.
23399                  * @param {Roo.dd.DragDrop} target The drop target
23400                  * @param {Event} e The event object
23401                  * @param {String} id The id of the dragged element
23402                  * @method afterDragEnter
23403                  */
23404                 this.afterDragEnter(target, e, id);
23405             }
23406         }
23407     },
23408
23409     /**
23410      * An empty function by default, but provided so that you can perform a custom action
23411      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23412      * @param {Roo.dd.DragDrop} target The drop target
23413      * @param {Event} e The event object
23414      * @param {String} id The id of the dragged element
23415      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23416      */
23417     beforeDragEnter : function(target, e, id){
23418         return true;
23419     },
23420
23421     // private
23422     alignElWithMouse: function() {
23423         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23424         this.proxy.sync();
23425     },
23426
23427     // private
23428     onDragOver : function(e, id){
23429         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23430         if(this.beforeDragOver(target, e, id) !== false){
23431             if(target.isNotifyTarget){
23432                 var status = target.notifyOver(this, e, this.dragData);
23433                 this.proxy.setStatus(status);
23434             }
23435
23436             if(this.afterDragOver){
23437                 /**
23438                  * An empty function by default, but provided so that you can perform a custom action
23439                  * while the dragged item is over the drop target by providing an implementation.
23440                  * @param {Roo.dd.DragDrop} target The drop target
23441                  * @param {Event} e The event object
23442                  * @param {String} id The id of the dragged element
23443                  * @method afterDragOver
23444                  */
23445                 this.afterDragOver(target, e, id);
23446             }
23447         }
23448     },
23449
23450     /**
23451      * An empty function by default, but provided so that you can perform a custom action
23452      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23453      * @param {Roo.dd.DragDrop} target The drop target
23454      * @param {Event} e The event object
23455      * @param {String} id The id of the dragged element
23456      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23457      */
23458     beforeDragOver : function(target, e, id){
23459         return true;
23460     },
23461
23462     // private
23463     onDragOut : function(e, id){
23464         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23465         if(this.beforeDragOut(target, e, id) !== false){
23466             if(target.isNotifyTarget){
23467                 target.notifyOut(this, e, this.dragData);
23468             }
23469             this.proxy.reset();
23470             if(this.afterDragOut){
23471                 /**
23472                  * An empty function by default, but provided so that you can perform a custom action
23473                  * after the dragged item is dragged out of the target without dropping.
23474                  * @param {Roo.dd.DragDrop} target The drop target
23475                  * @param {Event} e The event object
23476                  * @param {String} id The id of the dragged element
23477                  * @method afterDragOut
23478                  */
23479                 this.afterDragOut(target, e, id);
23480             }
23481         }
23482         this.cachedTarget = null;
23483     },
23484
23485     /**
23486      * An empty function by default, but provided so that you can perform a custom action before the dragged
23487      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23488      * @param {Roo.dd.DragDrop} target The drop target
23489      * @param {Event} e The event object
23490      * @param {String} id The id of the dragged element
23491      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23492      */
23493     beforeDragOut : function(target, e, id){
23494         return true;
23495     },
23496     
23497     // private
23498     onDragDrop : function(e, id){
23499         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23500         if(this.beforeDragDrop(target, e, id) !== false){
23501             if(target.isNotifyTarget){
23502                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23503                     this.onValidDrop(target, e, id);
23504                 }else{
23505                     this.onInvalidDrop(target, e, id);
23506                 }
23507             }else{
23508                 this.onValidDrop(target, e, id);
23509             }
23510             
23511             if(this.afterDragDrop){
23512                 /**
23513                  * An empty function by default, but provided so that you can perform a custom action
23514                  * after a valid drag drop has occurred by providing an implementation.
23515                  * @param {Roo.dd.DragDrop} target The drop target
23516                  * @param {Event} e The event object
23517                  * @param {String} id The id of the dropped element
23518                  * @method afterDragDrop
23519                  */
23520                 this.afterDragDrop(target, e, id);
23521             }
23522         }
23523         delete this.cachedTarget;
23524     },
23525
23526     /**
23527      * An empty function by default, but provided so that you can perform a custom action before the dragged
23528      * item is dropped onto the target and optionally cancel the onDragDrop.
23529      * @param {Roo.dd.DragDrop} target The drop target
23530      * @param {Event} e The event object
23531      * @param {String} id The id of the dragged element
23532      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23533      */
23534     beforeDragDrop : function(target, e, id){
23535         return true;
23536     },
23537
23538     // private
23539     onValidDrop : function(target, e, id){
23540         this.hideProxy();
23541         if(this.afterValidDrop){
23542             /**
23543              * An empty function by default, but provided so that you can perform a custom action
23544              * after a valid drop has occurred by providing an implementation.
23545              * @param {Object} target The target DD 
23546              * @param {Event} e The event object
23547              * @param {String} id The id of the dropped element
23548              * @method afterInvalidDrop
23549              */
23550             this.afterValidDrop(target, e, id);
23551         }
23552     },
23553
23554     // private
23555     getRepairXY : function(e, data){
23556         return this.el.getXY();  
23557     },
23558
23559     // private
23560     onInvalidDrop : function(target, e, id){
23561         this.beforeInvalidDrop(target, e, id);
23562         if(this.cachedTarget){
23563             if(this.cachedTarget.isNotifyTarget){
23564                 this.cachedTarget.notifyOut(this, e, this.dragData);
23565             }
23566             this.cacheTarget = null;
23567         }
23568         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23569
23570         if(this.afterInvalidDrop){
23571             /**
23572              * An empty function by default, but provided so that you can perform a custom action
23573              * after an invalid drop has occurred by providing an implementation.
23574              * @param {Event} e The event object
23575              * @param {String} id The id of the dropped element
23576              * @method afterInvalidDrop
23577              */
23578             this.afterInvalidDrop(e, id);
23579         }
23580     },
23581
23582     // private
23583     afterRepair : function(){
23584         if(Roo.enableFx){
23585             this.el.highlight(this.hlColor || "c3daf9");
23586         }
23587         this.dragging = false;
23588     },
23589
23590     /**
23591      * An empty function by default, but provided so that you can perform a custom action after an invalid
23592      * drop has occurred.
23593      * @param {Roo.dd.DragDrop} target The drop target
23594      * @param {Event} e The event object
23595      * @param {String} id The id of the dragged element
23596      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23597      */
23598     beforeInvalidDrop : function(target, e, id){
23599         return true;
23600     },
23601
23602     // private
23603     handleMouseDown : function(e){
23604         if(this.dragging) {
23605             return;
23606         }
23607         var data = this.getDragData(e);
23608         if(data && this.onBeforeDrag(data, e) !== false){
23609             this.dragData = data;
23610             this.proxy.stop();
23611             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23612         } 
23613     },
23614
23615     /**
23616      * An empty function by default, but provided so that you can perform a custom action before the initial
23617      * drag event begins and optionally cancel it.
23618      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23619      * @param {Event} e The event object
23620      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23621      */
23622     onBeforeDrag : function(data, e){
23623         return true;
23624     },
23625
23626     /**
23627      * An empty function by default, but provided so that you can perform a custom action once the initial
23628      * drag event has begun.  The drag cannot be canceled from this function.
23629      * @param {Number} x The x position of the click on the dragged object
23630      * @param {Number} y The y position of the click on the dragged object
23631      */
23632     onStartDrag : Roo.emptyFn,
23633
23634     // private - YUI override
23635     startDrag : function(x, y){
23636         this.proxy.reset();
23637         this.dragging = true;
23638         this.proxy.update("");
23639         this.onInitDrag(x, y);
23640         this.proxy.show();
23641     },
23642
23643     // private
23644     onInitDrag : function(x, y){
23645         var clone = this.el.dom.cloneNode(true);
23646         clone.id = Roo.id(); // prevent duplicate ids
23647         this.proxy.update(clone);
23648         this.onStartDrag(x, y);
23649         return true;
23650     },
23651
23652     /**
23653      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23654      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23655      */
23656     getProxy : function(){
23657         return this.proxy;  
23658     },
23659
23660     /**
23661      * Hides the drag source's {@link Roo.dd.StatusProxy}
23662      */
23663     hideProxy : function(){
23664         this.proxy.hide();  
23665         this.proxy.reset(true);
23666         this.dragging = false;
23667     },
23668
23669     // private
23670     triggerCacheRefresh : function(){
23671         Roo.dd.DDM.refreshCache(this.groups);
23672     },
23673
23674     // private - override to prevent hiding
23675     b4EndDrag: function(e) {
23676     },
23677
23678     // private - override to prevent moving
23679     endDrag : function(e){
23680         this.onEndDrag(this.dragData, e);
23681     },
23682
23683     // private
23684     onEndDrag : function(data, e){
23685     },
23686     
23687     // private - pin to cursor
23688     autoOffset : function(x, y) {
23689         this.setDelta(-12, -20);
23690     }    
23691 });/*
23692  * Based on:
23693  * Ext JS Library 1.1.1
23694  * Copyright(c) 2006-2007, Ext JS, LLC.
23695  *
23696  * Originally Released Under LGPL - original licence link has changed is not relivant.
23697  *
23698  * Fork - LGPL
23699  * <script type="text/javascript">
23700  */
23701
23702
23703 /**
23704  * @class Roo.dd.DropTarget
23705  * @extends Roo.dd.DDTarget
23706  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23707  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23708  * @constructor
23709  * @param {String/HTMLElement/Element} el The container element
23710  * @param {Object} config
23711  */
23712 Roo.dd.DropTarget = function(el, config){
23713     this.el = Roo.get(el);
23714     
23715     var listeners = false; ;
23716     if (config && config.listeners) {
23717         listeners= config.listeners;
23718         delete config.listeners;
23719     }
23720     Roo.apply(this, config);
23721     
23722     if(this.containerScroll){
23723         Roo.dd.ScrollManager.register(this.el);
23724     }
23725     this.addEvents( {
23726          /**
23727          * @scope Roo.dd.DropTarget
23728          */
23729          
23730          /**
23731          * @event enter
23732          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23733          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23734          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23735          * 
23736          * IMPORTANT : it should set  this.valid to true|false
23737          * 
23738          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23739          * @param {Event} e The event
23740          * @param {Object} data An object containing arbitrary data supplied by the drag source
23741          */
23742         "enter" : true,
23743         
23744          /**
23745          * @event over
23746          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23747          * This method will be called on every mouse movement while the drag source is over the drop target.
23748          * This default implementation simply returns the dropAllowed config value.
23749          * 
23750          * IMPORTANT : it should set  this.valid to true|false
23751          * 
23752          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23753          * @param {Event} e The event
23754          * @param {Object} data An object containing arbitrary data supplied by the drag source
23755          
23756          */
23757         "over" : true,
23758         /**
23759          * @event out
23760          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23761          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23762          * overClass (if any) from the drop element.
23763          * 
23764          * 
23765          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23766          * @param {Event} e The event
23767          * @param {Object} data An object containing arbitrary data supplied by the drag source
23768          */
23769          "out" : true,
23770          
23771         /**
23772          * @event drop
23773          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23774          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23775          * implementation that does something to process the drop event and returns true so that the drag source's
23776          * repair action does not run.
23777          * 
23778          * IMPORTANT : it should set this.success
23779          * 
23780          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23781          * @param {Event} e The event
23782          * @param {Object} data An object containing arbitrary data supplied by the drag source
23783         */
23784          "drop" : true
23785     });
23786             
23787      
23788     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23789         this.el.dom, 
23790         this.ddGroup || this.group,
23791         {
23792             isTarget: true,
23793             listeners : listeners || {} 
23794            
23795         
23796         }
23797     );
23798
23799 };
23800
23801 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23802     /**
23803      * @cfg {String} overClass
23804      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23805      */
23806      /**
23807      * @cfg {String} ddGroup
23808      * The drag drop group to handle drop events for
23809      */
23810      
23811     /**
23812      * @cfg {String} dropAllowed
23813      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23814      */
23815     dropAllowed : "x-dd-drop-ok",
23816     /**
23817      * @cfg {String} dropNotAllowed
23818      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23819      */
23820     dropNotAllowed : "x-dd-drop-nodrop",
23821     /**
23822      * @cfg {boolean} success
23823      * set this after drop listener.. 
23824      */
23825     success : false,
23826     /**
23827      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23828      * if the drop point is valid for over/enter..
23829      */
23830     valid : false,
23831     // private
23832     isTarget : true,
23833
23834     // private
23835     isNotifyTarget : true,
23836     
23837     /**
23838      * @hide
23839      */
23840     notifyEnter : function(dd, e, data)
23841     {
23842         this.valid = true;
23843         this.fireEvent('enter', dd, e, data);
23844         if(this.overClass){
23845             this.el.addClass(this.overClass);
23846         }
23847         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23848             this.valid ? this.dropAllowed : this.dropNotAllowed
23849         );
23850     },
23851
23852     /**
23853      * @hide
23854      */
23855     notifyOver : function(dd, e, data)
23856     {
23857         this.valid = true;
23858         this.fireEvent('over', dd, e, data);
23859         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23860             this.valid ? this.dropAllowed : this.dropNotAllowed
23861         );
23862     },
23863
23864     /**
23865      * @hide
23866      */
23867     notifyOut : function(dd, e, data)
23868     {
23869         this.fireEvent('out', dd, e, data);
23870         if(this.overClass){
23871             this.el.removeClass(this.overClass);
23872         }
23873     },
23874
23875     /**
23876      * @hide
23877      */
23878     notifyDrop : function(dd, e, data)
23879     {
23880         this.success = false;
23881         this.fireEvent('drop', dd, e, data);
23882         return this.success;
23883     }
23884 });/*
23885  * Based on:
23886  * Ext JS Library 1.1.1
23887  * Copyright(c) 2006-2007, Ext JS, LLC.
23888  *
23889  * Originally Released Under LGPL - original licence link has changed is not relivant.
23890  *
23891  * Fork - LGPL
23892  * <script type="text/javascript">
23893  */
23894
23895
23896 /**
23897  * @class Roo.dd.DragZone
23898  * @extends Roo.dd.DragSource
23899  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23900  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23901  * @constructor
23902  * @param {String/HTMLElement/Element} el The container element
23903  * @param {Object} config
23904  */
23905 Roo.dd.DragZone = function(el, config){
23906     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23907     if(this.containerScroll){
23908         Roo.dd.ScrollManager.register(this.el);
23909     }
23910 };
23911
23912 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23913     /**
23914      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23915      * for auto scrolling during drag operations.
23916      */
23917     /**
23918      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23919      * method after a failed drop (defaults to "c3daf9" - light blue)
23920      */
23921
23922     /**
23923      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23924      * for a valid target to drag based on the mouse down. Override this method
23925      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23926      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23927      * @param {EventObject} e The mouse down event
23928      * @return {Object} The dragData
23929      */
23930     getDragData : function(e){
23931         return Roo.dd.Registry.getHandleFromEvent(e);
23932     },
23933     
23934     /**
23935      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23936      * this.dragData.ddel
23937      * @param {Number} x The x position of the click on the dragged object
23938      * @param {Number} y The y position of the click on the dragged object
23939      * @return {Boolean} true to continue the drag, false to cancel
23940      */
23941     onInitDrag : function(x, y){
23942         this.proxy.update(this.dragData.ddel.cloneNode(true));
23943         this.onStartDrag(x, y);
23944         return true;
23945     },
23946     
23947     /**
23948      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23949      */
23950     afterRepair : function(){
23951         if(Roo.enableFx){
23952             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23953         }
23954         this.dragging = false;
23955     },
23956
23957     /**
23958      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23959      * the XY of this.dragData.ddel
23960      * @param {EventObject} e The mouse up event
23961      * @return {Array} The xy location (e.g. [100, 200])
23962      */
23963     getRepairXY : function(e){
23964         return Roo.Element.fly(this.dragData.ddel).getXY();  
23965     }
23966 });/*
23967  * Based on:
23968  * Ext JS Library 1.1.1
23969  * Copyright(c) 2006-2007, Ext JS, LLC.
23970  *
23971  * Originally Released Under LGPL - original licence link has changed is not relivant.
23972  *
23973  * Fork - LGPL
23974  * <script type="text/javascript">
23975  */
23976 /**
23977  * @class Roo.dd.DropZone
23978  * @extends Roo.dd.DropTarget
23979  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23980  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23981  * @constructor
23982  * @param {String/HTMLElement/Element} el The container element
23983  * @param {Object} config
23984  */
23985 Roo.dd.DropZone = function(el, config){
23986     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23987 };
23988
23989 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23990     /**
23991      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23992      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23993      * provide your own custom lookup.
23994      * @param {Event} e The event
23995      * @return {Object} data The custom data
23996      */
23997     getTargetFromEvent : function(e){
23998         return Roo.dd.Registry.getTargetFromEvent(e);
23999     },
24000
24001     /**
24002      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24003      * that it has registered.  This method has no default implementation and should be overridden to provide
24004      * node-specific processing if necessary.
24005      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24006      * {@link #getTargetFromEvent} for this node)
24007      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24008      * @param {Event} e The event
24009      * @param {Object} data An object containing arbitrary data supplied by the drag source
24010      */
24011     onNodeEnter : function(n, dd, e, data){
24012         
24013     },
24014
24015     /**
24016      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24017      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24018      * overridden to provide the proper feedback.
24019      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24020      * {@link #getTargetFromEvent} for this node)
24021      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24022      * @param {Event} e The event
24023      * @param {Object} data An object containing arbitrary data supplied by the drag source
24024      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24025      * underlying {@link Roo.dd.StatusProxy} can be updated
24026      */
24027     onNodeOver : function(n, dd, e, data){
24028         return this.dropAllowed;
24029     },
24030
24031     /**
24032      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24033      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24034      * node-specific processing if necessary.
24035      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24036      * {@link #getTargetFromEvent} for this node)
24037      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24038      * @param {Event} e The event
24039      * @param {Object} data An object containing arbitrary data supplied by the drag source
24040      */
24041     onNodeOut : function(n, dd, e, data){
24042         
24043     },
24044
24045     /**
24046      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24047      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24048      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24049      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24050      * {@link #getTargetFromEvent} for this node)
24051      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24052      * @param {Event} e The event
24053      * @param {Object} data An object containing arbitrary data supplied by the drag source
24054      * @return {Boolean} True if the drop was valid, else false
24055      */
24056     onNodeDrop : function(n, dd, e, data){
24057         return false;
24058     },
24059
24060     /**
24061      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24062      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24063      * it should be overridden to provide the proper feedback if necessary.
24064      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24065      * @param {Event} e The event
24066      * @param {Object} data An object containing arbitrary data supplied by the drag source
24067      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24068      * underlying {@link Roo.dd.StatusProxy} can be updated
24069      */
24070     onContainerOver : function(dd, e, data){
24071         return this.dropNotAllowed;
24072     },
24073
24074     /**
24075      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24076      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24077      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24078      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24079      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24080      * @param {Event} e The event
24081      * @param {Object} data An object containing arbitrary data supplied by the drag source
24082      * @return {Boolean} True if the drop was valid, else false
24083      */
24084     onContainerDrop : function(dd, e, data){
24085         return false;
24086     },
24087
24088     /**
24089      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24090      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24091      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24092      * you should override this method and provide a custom implementation.
24093      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24094      * @param {Event} e The event
24095      * @param {Object} data An object containing arbitrary data supplied by the drag source
24096      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24097      * underlying {@link Roo.dd.StatusProxy} can be updated
24098      */
24099     notifyEnter : function(dd, e, data){
24100         return this.dropNotAllowed;
24101     },
24102
24103     /**
24104      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24105      * This method will be called on every mouse movement while the drag source is over the drop zone.
24106      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24107      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24108      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24109      * registered node, it will call {@link #onContainerOver}.
24110      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24111      * @param {Event} e The event
24112      * @param {Object} data An object containing arbitrary data supplied by the drag source
24113      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24114      * underlying {@link Roo.dd.StatusProxy} can be updated
24115      */
24116     notifyOver : function(dd, e, data){
24117         var n = this.getTargetFromEvent(e);
24118         if(!n){ // not over valid drop target
24119             if(this.lastOverNode){
24120                 this.onNodeOut(this.lastOverNode, dd, e, data);
24121                 this.lastOverNode = null;
24122             }
24123             return this.onContainerOver(dd, e, data);
24124         }
24125         if(this.lastOverNode != n){
24126             if(this.lastOverNode){
24127                 this.onNodeOut(this.lastOverNode, dd, e, data);
24128             }
24129             this.onNodeEnter(n, dd, e, data);
24130             this.lastOverNode = n;
24131         }
24132         return this.onNodeOver(n, dd, e, data);
24133     },
24134
24135     /**
24136      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24137      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24138      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24139      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24140      * @param {Event} e The event
24141      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24142      */
24143     notifyOut : function(dd, e, data){
24144         if(this.lastOverNode){
24145             this.onNodeOut(this.lastOverNode, dd, e, data);
24146             this.lastOverNode = null;
24147         }
24148     },
24149
24150     /**
24151      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24152      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24153      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24154      * otherwise it will call {@link #onContainerDrop}.
24155      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24156      * @param {Event} e The event
24157      * @param {Object} data An object containing arbitrary data supplied by the drag source
24158      * @return {Boolean} True if the drop was valid, else false
24159      */
24160     notifyDrop : function(dd, e, data){
24161         if(this.lastOverNode){
24162             this.onNodeOut(this.lastOverNode, dd, e, data);
24163             this.lastOverNode = null;
24164         }
24165         var n = this.getTargetFromEvent(e);
24166         return n ?
24167             this.onNodeDrop(n, dd, e, data) :
24168             this.onContainerDrop(dd, e, data);
24169     },
24170
24171     // private
24172     triggerCacheRefresh : function(){
24173         Roo.dd.DDM.refreshCache(this.groups);
24174     }  
24175 });/*
24176  * Based on:
24177  * Ext JS Library 1.1.1
24178  * Copyright(c) 2006-2007, Ext JS, LLC.
24179  *
24180  * Originally Released Under LGPL - original licence link has changed is not relivant.
24181  *
24182  * Fork - LGPL
24183  * <script type="text/javascript">
24184  */
24185
24186
24187 /**
24188  * @class Roo.data.SortTypes
24189  * @static
24190  * Defines the default sorting (casting?) comparison functions used when sorting data.
24191  */
24192 Roo.data.SortTypes = {
24193     /**
24194      * Default sort that does nothing
24195      * @param {Mixed} s The value being converted
24196      * @return {Mixed} The comparison value
24197      */
24198     none : function(s){
24199         return s;
24200     },
24201     
24202     /**
24203      * The regular expression used to strip tags
24204      * @type {RegExp}
24205      * @property
24206      */
24207     stripTagsRE : /<\/?[^>]+>/gi,
24208     
24209     /**
24210      * Strips all HTML tags to sort on text only
24211      * @param {Mixed} s The value being converted
24212      * @return {String} The comparison value
24213      */
24214     asText : function(s){
24215         return String(s).replace(this.stripTagsRE, "");
24216     },
24217     
24218     /**
24219      * Strips all HTML tags to sort on text only - Case insensitive
24220      * @param {Mixed} s The value being converted
24221      * @return {String} The comparison value
24222      */
24223     asUCText : function(s){
24224         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24225     },
24226     
24227     /**
24228      * Case insensitive string
24229      * @param {Mixed} s The value being converted
24230      * @return {String} The comparison value
24231      */
24232     asUCString : function(s) {
24233         return String(s).toUpperCase();
24234     },
24235     
24236     /**
24237      * Date sorting
24238      * @param {Mixed} s The value being converted
24239      * @return {Number} The comparison value
24240      */
24241     asDate : function(s) {
24242         if(!s){
24243             return 0;
24244         }
24245         if(s instanceof Date){
24246             return s.getTime();
24247         }
24248         return Date.parse(String(s));
24249     },
24250     
24251     /**
24252      * Float sorting
24253      * @param {Mixed} s The value being converted
24254      * @return {Float} The comparison value
24255      */
24256     asFloat : function(s) {
24257         var val = parseFloat(String(s).replace(/,/g, ""));
24258         if(isNaN(val)) {
24259             val = 0;
24260         }
24261         return val;
24262     },
24263     
24264     /**
24265      * Integer sorting
24266      * @param {Mixed} s The value being converted
24267      * @return {Number} The comparison value
24268      */
24269     asInt : function(s) {
24270         var val = parseInt(String(s).replace(/,/g, ""));
24271         if(isNaN(val)) {
24272             val = 0;
24273         }
24274         return val;
24275     }
24276 };/*
24277  * Based on:
24278  * Ext JS Library 1.1.1
24279  * Copyright(c) 2006-2007, Ext JS, LLC.
24280  *
24281  * Originally Released Under LGPL - original licence link has changed is not relivant.
24282  *
24283  * Fork - LGPL
24284  * <script type="text/javascript">
24285  */
24286
24287 /**
24288 * @class Roo.data.Record
24289  * Instances of this class encapsulate both record <em>definition</em> information, and record
24290  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24291  * to access Records cached in an {@link Roo.data.Store} object.<br>
24292  * <p>
24293  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24294  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24295  * objects.<br>
24296  * <p>
24297  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24298  * @constructor
24299  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24300  * {@link #create}. The parameters are the same.
24301  * @param {Array} data An associative Array of data values keyed by the field name.
24302  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24303  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24304  * not specified an integer id is generated.
24305  */
24306 Roo.data.Record = function(data, id){
24307     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24308     this.data = data;
24309 };
24310
24311 /**
24312  * Generate a constructor for a specific record layout.
24313  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24314  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24315  * Each field definition object may contain the following properties: <ul>
24316  * <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,
24317  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24318  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24319  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24320  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24321  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24322  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24323  * this may be omitted.</p></li>
24324  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24325  * <ul><li>auto (Default, implies no conversion)</li>
24326  * <li>string</li>
24327  * <li>int</li>
24328  * <li>float</li>
24329  * <li>boolean</li>
24330  * <li>date</li></ul></p></li>
24331  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24332  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24333  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24334  * by the Reader into an object that will be stored in the Record. It is passed the
24335  * following parameters:<ul>
24336  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24337  * </ul></p></li>
24338  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24339  * </ul>
24340  * <br>usage:<br><pre><code>
24341 var TopicRecord = Roo.data.Record.create(
24342     {name: 'title', mapping: 'topic_title'},
24343     {name: 'author', mapping: 'username'},
24344     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24345     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24346     {name: 'lastPoster', mapping: 'user2'},
24347     {name: 'excerpt', mapping: 'post_text'}
24348 );
24349
24350 var myNewRecord = new TopicRecord({
24351     title: 'Do my job please',
24352     author: 'noobie',
24353     totalPosts: 1,
24354     lastPost: new Date(),
24355     lastPoster: 'Animal',
24356     excerpt: 'No way dude!'
24357 });
24358 myStore.add(myNewRecord);
24359 </code></pre>
24360  * @method create
24361  * @static
24362  */
24363 Roo.data.Record.create = function(o){
24364     var f = function(){
24365         f.superclass.constructor.apply(this, arguments);
24366     };
24367     Roo.extend(f, Roo.data.Record);
24368     var p = f.prototype;
24369     p.fields = new Roo.util.MixedCollection(false, function(field){
24370         return field.name;
24371     });
24372     for(var i = 0, len = o.length; i < len; i++){
24373         p.fields.add(new Roo.data.Field(o[i]));
24374     }
24375     f.getField = function(name){
24376         return p.fields.get(name);  
24377     };
24378     return f;
24379 };
24380
24381 Roo.data.Record.AUTO_ID = 1000;
24382 Roo.data.Record.EDIT = 'edit';
24383 Roo.data.Record.REJECT = 'reject';
24384 Roo.data.Record.COMMIT = 'commit';
24385
24386 Roo.data.Record.prototype = {
24387     /**
24388      * Readonly flag - true if this record has been modified.
24389      * @type Boolean
24390      */
24391     dirty : false,
24392     editing : false,
24393     error: null,
24394     modified: null,
24395
24396     // private
24397     join : function(store){
24398         this.store = store;
24399     },
24400
24401     /**
24402      * Set the named field to the specified value.
24403      * @param {String} name The name of the field to set.
24404      * @param {Object} value The value to set the field to.
24405      */
24406     set : function(name, value){
24407         if(this.data[name] == value){
24408             return;
24409         }
24410         this.dirty = true;
24411         if(!this.modified){
24412             this.modified = {};
24413         }
24414         if(typeof this.modified[name] == 'undefined'){
24415             this.modified[name] = this.data[name];
24416         }
24417         this.data[name] = value;
24418         if(!this.editing && this.store){
24419             this.store.afterEdit(this);
24420         }       
24421     },
24422
24423     /**
24424      * Get the value of the named field.
24425      * @param {String} name The name of the field to get the value of.
24426      * @return {Object} The value of the field.
24427      */
24428     get : function(name){
24429         return this.data[name]; 
24430     },
24431
24432     // private
24433     beginEdit : function(){
24434         this.editing = true;
24435         this.modified = {}; 
24436     },
24437
24438     // private
24439     cancelEdit : function(){
24440         this.editing = false;
24441         delete this.modified;
24442     },
24443
24444     // private
24445     endEdit : function(){
24446         this.editing = false;
24447         if(this.dirty && this.store){
24448             this.store.afterEdit(this);
24449         }
24450     },
24451
24452     /**
24453      * Usually called by the {@link Roo.data.Store} which owns the Record.
24454      * Rejects all changes made to the Record since either creation, or the last commit operation.
24455      * Modified fields are reverted to their original values.
24456      * <p>
24457      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24458      * of reject operations.
24459      */
24460     reject : function(){
24461         var m = this.modified;
24462         for(var n in m){
24463             if(typeof m[n] != "function"){
24464                 this.data[n] = m[n];
24465             }
24466         }
24467         this.dirty = false;
24468         delete this.modified;
24469         this.editing = false;
24470         if(this.store){
24471             this.store.afterReject(this);
24472         }
24473     },
24474
24475     /**
24476      * Usually called by the {@link Roo.data.Store} which owns the Record.
24477      * Commits all changes made to the Record since either creation, or the last commit operation.
24478      * <p>
24479      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24480      * of commit operations.
24481      */
24482     commit : function(){
24483         this.dirty = false;
24484         delete this.modified;
24485         this.editing = false;
24486         if(this.store){
24487             this.store.afterCommit(this);
24488         }
24489     },
24490
24491     // private
24492     hasError : function(){
24493         return this.error != null;
24494     },
24495
24496     // private
24497     clearError : function(){
24498         this.error = null;
24499     },
24500
24501     /**
24502      * Creates a copy of this record.
24503      * @param {String} id (optional) A new record id if you don't want to use this record's id
24504      * @return {Record}
24505      */
24506     copy : function(newId) {
24507         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24508     }
24509 };/*
24510  * Based on:
24511  * Ext JS Library 1.1.1
24512  * Copyright(c) 2006-2007, Ext JS, LLC.
24513  *
24514  * Originally Released Under LGPL - original licence link has changed is not relivant.
24515  *
24516  * Fork - LGPL
24517  * <script type="text/javascript">
24518  */
24519
24520
24521
24522 /**
24523  * @class Roo.data.Store
24524  * @extends Roo.util.Observable
24525  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24526  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24527  * <p>
24528  * 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
24529  * has no knowledge of the format of the data returned by the Proxy.<br>
24530  * <p>
24531  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24532  * instances from the data object. These records are cached and made available through accessor functions.
24533  * @constructor
24534  * Creates a new Store.
24535  * @param {Object} config A config object containing the objects needed for the Store to access data,
24536  * and read the data into Records.
24537  */
24538 Roo.data.Store = function(config){
24539     this.data = new Roo.util.MixedCollection(false);
24540     this.data.getKey = function(o){
24541         return o.id;
24542     };
24543     this.baseParams = {};
24544     // private
24545     this.paramNames = {
24546         "start" : "start",
24547         "limit" : "limit",
24548         "sort" : "sort",
24549         "dir" : "dir",
24550         "multisort" : "_multisort"
24551     };
24552
24553     if(config && config.data){
24554         this.inlineData = config.data;
24555         delete config.data;
24556     }
24557
24558     Roo.apply(this, config);
24559     
24560     if(this.reader){ // reader passed
24561         this.reader = Roo.factory(this.reader, Roo.data);
24562         this.reader.xmodule = this.xmodule || false;
24563         if(!this.recordType){
24564             this.recordType = this.reader.recordType;
24565         }
24566         if(this.reader.onMetaChange){
24567             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24568         }
24569     }
24570
24571     if(this.recordType){
24572         this.fields = this.recordType.prototype.fields;
24573     }
24574     this.modified = [];
24575
24576     this.addEvents({
24577         /**
24578          * @event datachanged
24579          * Fires when the data cache has changed, and a widget which is using this Store
24580          * as a Record cache should refresh its view.
24581          * @param {Store} this
24582          */
24583         datachanged : true,
24584         /**
24585          * @event metachange
24586          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24587          * @param {Store} this
24588          * @param {Object} meta The JSON metadata
24589          */
24590         metachange : true,
24591         /**
24592          * @event add
24593          * Fires when Records have been added to the Store
24594          * @param {Store} this
24595          * @param {Roo.data.Record[]} records The array of Records added
24596          * @param {Number} index The index at which the record(s) were added
24597          */
24598         add : true,
24599         /**
24600          * @event remove
24601          * Fires when a Record has been removed from the Store
24602          * @param {Store} this
24603          * @param {Roo.data.Record} record The Record that was removed
24604          * @param {Number} index The index at which the record was removed
24605          */
24606         remove : true,
24607         /**
24608          * @event update
24609          * Fires when a Record has been updated
24610          * @param {Store} this
24611          * @param {Roo.data.Record} record The Record that was updated
24612          * @param {String} operation The update operation being performed.  Value may be one of:
24613          * <pre><code>
24614  Roo.data.Record.EDIT
24615  Roo.data.Record.REJECT
24616  Roo.data.Record.COMMIT
24617          * </code></pre>
24618          */
24619         update : true,
24620         /**
24621          * @event clear
24622          * Fires when the data cache has been cleared.
24623          * @param {Store} this
24624          */
24625         clear : true,
24626         /**
24627          * @event beforeload
24628          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24629          * the load action will be canceled.
24630          * @param {Store} this
24631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24632          */
24633         beforeload : true,
24634         /**
24635          * @event beforeloadadd
24636          * Fires after a new set of Records has been loaded.
24637          * @param {Store} this
24638          * @param {Roo.data.Record[]} records The Records that were loaded
24639          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24640          */
24641         beforeloadadd : true,
24642         /**
24643          * @event load
24644          * Fires after a new set of Records has been loaded, before they are added to the store.
24645          * @param {Store} this
24646          * @param {Roo.data.Record[]} records The Records that were loaded
24647          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24648          * @params {Object} return from reader
24649          */
24650         load : true,
24651         /**
24652          * @event loadexception
24653          * Fires if an exception occurs in the Proxy during loading.
24654          * Called with the signature of the Proxy's "loadexception" event.
24655          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24656          * 
24657          * @param {Proxy} 
24658          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24659          * @param {Object} load options 
24660          * @param {Object} jsonData from your request (normally this contains the Exception)
24661          */
24662         loadexception : true
24663     });
24664     
24665     if(this.proxy){
24666         this.proxy = Roo.factory(this.proxy, Roo.data);
24667         this.proxy.xmodule = this.xmodule || false;
24668         this.relayEvents(this.proxy,  ["loadexception"]);
24669     }
24670     this.sortToggle = {};
24671     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24672
24673     Roo.data.Store.superclass.constructor.call(this);
24674
24675     if(this.inlineData){
24676         this.loadData(this.inlineData);
24677         delete this.inlineData;
24678     }
24679 };
24680
24681 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24682      /**
24683     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24684     * without a remote query - used by combo/forms at present.
24685     */
24686     
24687     /**
24688     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24689     */
24690     /**
24691     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24692     */
24693     /**
24694     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24695     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24696     */
24697     /**
24698     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24699     * on any HTTP request
24700     */
24701     /**
24702     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24703     */
24704     /**
24705     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24706     */
24707     multiSort: false,
24708     /**
24709     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24710     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24711     */
24712     remoteSort : false,
24713
24714     /**
24715     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24716      * loaded or when a record is removed. (defaults to false).
24717     */
24718     pruneModifiedRecords : false,
24719
24720     // private
24721     lastOptions : null,
24722
24723     /**
24724      * Add Records to the Store and fires the add event.
24725      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24726      */
24727     add : function(records){
24728         records = [].concat(records);
24729         for(var i = 0, len = records.length; i < len; i++){
24730             records[i].join(this);
24731         }
24732         var index = this.data.length;
24733         this.data.addAll(records);
24734         this.fireEvent("add", this, records, index);
24735     },
24736
24737     /**
24738      * Remove a Record from the Store and fires the remove event.
24739      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24740      */
24741     remove : function(record){
24742         var index = this.data.indexOf(record);
24743         this.data.removeAt(index);
24744  
24745         if(this.pruneModifiedRecords){
24746             this.modified.remove(record);
24747         }
24748         this.fireEvent("remove", this, record, index);
24749     },
24750
24751     /**
24752      * Remove all Records from the Store and fires the clear event.
24753      */
24754     removeAll : function(){
24755         this.data.clear();
24756         if(this.pruneModifiedRecords){
24757             this.modified = [];
24758         }
24759         this.fireEvent("clear", this);
24760     },
24761
24762     /**
24763      * Inserts Records to the Store at the given index and fires the add event.
24764      * @param {Number} index The start index at which to insert the passed Records.
24765      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24766      */
24767     insert : function(index, records){
24768         records = [].concat(records);
24769         for(var i = 0, len = records.length; i < len; i++){
24770             this.data.insert(index, records[i]);
24771             records[i].join(this);
24772         }
24773         this.fireEvent("add", this, records, index);
24774     },
24775
24776     /**
24777      * Get the index within the cache of the passed Record.
24778      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24779      * @return {Number} The index of the passed Record. Returns -1 if not found.
24780      */
24781     indexOf : function(record){
24782         return this.data.indexOf(record);
24783     },
24784
24785     /**
24786      * Get the index within the cache of the Record with the passed id.
24787      * @param {String} id The id of the Record to find.
24788      * @return {Number} The index of the Record. Returns -1 if not found.
24789      */
24790     indexOfId : function(id){
24791         return this.data.indexOfKey(id);
24792     },
24793
24794     /**
24795      * Get the Record with the specified id.
24796      * @param {String} id The id of the Record to find.
24797      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24798      */
24799     getById : function(id){
24800         return this.data.key(id);
24801     },
24802
24803     /**
24804      * Get the Record at the specified index.
24805      * @param {Number} index The index of the Record to find.
24806      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24807      */
24808     getAt : function(index){
24809         return this.data.itemAt(index);
24810     },
24811
24812     /**
24813      * Returns a range of Records between specified indices.
24814      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24815      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24816      * @return {Roo.data.Record[]} An array of Records
24817      */
24818     getRange : function(start, end){
24819         return this.data.getRange(start, end);
24820     },
24821
24822     // private
24823     storeOptions : function(o){
24824         o = Roo.apply({}, o);
24825         delete o.callback;
24826         delete o.scope;
24827         this.lastOptions = o;
24828     },
24829
24830     /**
24831      * Loads the Record cache from the configured Proxy using the configured Reader.
24832      * <p>
24833      * If using remote paging, then the first load call must specify the <em>start</em>
24834      * and <em>limit</em> properties in the options.params property to establish the initial
24835      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24836      * <p>
24837      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24838      * and this call will return before the new data has been loaded. Perform any post-processing
24839      * in a callback function, or in a "load" event handler.</strong>
24840      * <p>
24841      * @param {Object} options An object containing properties which control loading options:<ul>
24842      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24843      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24844      * <pre>
24845                 {
24846                     data : data,  // array of key=>value data like JsonReader
24847                     total : data.length,
24848                     success : true
24849                     
24850                 }
24851         </pre>
24852             }.</li>
24853      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24854      * passed the following arguments:<ul>
24855      * <li>r : Roo.data.Record[]</li>
24856      * <li>options: Options object from the load call</li>
24857      * <li>success: Boolean success indicator</li></ul></li>
24858      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24859      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24860      * </ul>
24861      */
24862     load : function(options){
24863         options = options || {};
24864         if(this.fireEvent("beforeload", this, options) !== false){
24865             this.storeOptions(options);
24866             var p = Roo.apply(options.params || {}, this.baseParams);
24867             // if meta was not loaded from remote source.. try requesting it.
24868             if (!this.reader.metaFromRemote) {
24869                 p._requestMeta = 1;
24870             }
24871             if(this.sortInfo && this.remoteSort){
24872                 var pn = this.paramNames;
24873                 p[pn["sort"]] = this.sortInfo.field;
24874                 p[pn["dir"]] = this.sortInfo.direction;
24875             }
24876             if (this.multiSort) {
24877                 var pn = this.paramNames;
24878                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24879             }
24880             
24881             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24882         }
24883     },
24884
24885     /**
24886      * Reloads the Record cache from the configured Proxy using the configured Reader and
24887      * the options from the last load operation performed.
24888      * @param {Object} options (optional) An object containing properties which may override the options
24889      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24890      * the most recently used options are reused).
24891      */
24892     reload : function(options){
24893         this.load(Roo.applyIf(options||{}, this.lastOptions));
24894     },
24895
24896     // private
24897     // Called as a callback by the Reader during a load operation.
24898     loadRecords : function(o, options, success){
24899          
24900         if(!o){
24901             if(success !== false){
24902                 this.fireEvent("load", this, [], options, o);
24903             }
24904             if(options.callback){
24905                 options.callback.call(options.scope || this, [], options, false);
24906             }
24907             return;
24908         }
24909         // if data returned failure - throw an exception.
24910         if (o.success === false) {
24911             // show a message if no listener is registered.
24912             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24913                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24914             }
24915             // loadmask wil be hooked into this..
24916             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24917             return;
24918         }
24919         var r = o.records, t = o.totalRecords || r.length;
24920         
24921         this.fireEvent("beforeloadadd", this, r, options, o);
24922         
24923         if(!options || options.add !== true){
24924             if(this.pruneModifiedRecords){
24925                 this.modified = [];
24926             }
24927             for(var i = 0, len = r.length; i < len; i++){
24928                 r[i].join(this);
24929             }
24930             if(this.snapshot){
24931                 this.data = this.snapshot;
24932                 delete this.snapshot;
24933             }
24934             this.data.clear();
24935             this.data.addAll(r);
24936             this.totalLength = t;
24937             this.applySort();
24938             this.fireEvent("datachanged", this);
24939         }else{
24940             this.totalLength = Math.max(t, this.data.length+r.length);
24941             this.add(r);
24942         }
24943         
24944         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24945                 
24946             var e = new Roo.data.Record({});
24947
24948             e.set(this.parent.displayField, this.parent.emptyTitle);
24949             e.set(this.parent.valueField, '');
24950
24951             this.insert(0, e);
24952         }
24953             
24954         this.fireEvent("load", this, r, options, o);
24955         if(options.callback){
24956             options.callback.call(options.scope || this, r, options, true);
24957         }
24958     },
24959
24960
24961     /**
24962      * Loads data from a passed data block. A Reader which understands the format of the data
24963      * must have been configured in the constructor.
24964      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24965      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24966      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24967      */
24968     loadData : function(o, append){
24969         var r = this.reader.readRecords(o);
24970         this.loadRecords(r, {add: append}, true);
24971     },
24972     
24973      /**
24974      * using 'cn' the nested child reader read the child array into it's child stores.
24975      * @param {Object} rec The record with a 'children array
24976      */
24977     loadDataFromChildren : function(rec)
24978     {
24979         this.loadData(this.reader.toLoadData(rec));
24980     },
24981     
24982
24983     /**
24984      * Gets the number of cached records.
24985      * <p>
24986      * <em>If using paging, this may not be the total size of the dataset. If the data object
24987      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24988      * the data set size</em>
24989      */
24990     getCount : function(){
24991         return this.data.length || 0;
24992     },
24993
24994     /**
24995      * Gets the total number of records in the dataset as returned by the server.
24996      * <p>
24997      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24998      * the dataset size</em>
24999      */
25000     getTotalCount : function(){
25001         return this.totalLength || 0;
25002     },
25003
25004     /**
25005      * Returns the sort state of the Store as an object with two properties:
25006      * <pre><code>
25007  field {String} The name of the field by which the Records are sorted
25008  direction {String} The sort order, "ASC" or "DESC"
25009      * </code></pre>
25010      */
25011     getSortState : function(){
25012         return this.sortInfo;
25013     },
25014
25015     // private
25016     applySort : function(){
25017         if(this.sortInfo && !this.remoteSort){
25018             var s = this.sortInfo, f = s.field;
25019             var st = this.fields.get(f).sortType;
25020             var fn = function(r1, r2){
25021                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25022                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25023             };
25024             this.data.sort(s.direction, fn);
25025             if(this.snapshot && this.snapshot != this.data){
25026                 this.snapshot.sort(s.direction, fn);
25027             }
25028         }
25029     },
25030
25031     /**
25032      * Sets the default sort column and order to be used by the next load operation.
25033      * @param {String} fieldName The name of the field to sort by.
25034      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25035      */
25036     setDefaultSort : function(field, dir){
25037         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25038     },
25039
25040     /**
25041      * Sort the Records.
25042      * If remote sorting is used, the sort is performed on the server, and the cache is
25043      * reloaded. If local sorting is used, the cache is sorted internally.
25044      * @param {String} fieldName The name of the field to sort by.
25045      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25046      */
25047     sort : function(fieldName, dir){
25048         var f = this.fields.get(fieldName);
25049         if(!dir){
25050             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25051             
25052             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25053                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25054             }else{
25055                 dir = f.sortDir;
25056             }
25057         }
25058         this.sortToggle[f.name] = dir;
25059         this.sortInfo = {field: f.name, direction: dir};
25060         if(!this.remoteSort){
25061             this.applySort();
25062             this.fireEvent("datachanged", this);
25063         }else{
25064             this.load(this.lastOptions);
25065         }
25066     },
25067
25068     /**
25069      * Calls the specified function for each of the Records in the cache.
25070      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25071      * Returning <em>false</em> aborts and exits the iteration.
25072      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25073      */
25074     each : function(fn, scope){
25075         this.data.each(fn, scope);
25076     },
25077
25078     /**
25079      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25080      * (e.g., during paging).
25081      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25082      */
25083     getModifiedRecords : function(){
25084         return this.modified;
25085     },
25086
25087     // private
25088     createFilterFn : function(property, value, anyMatch){
25089         if(!value.exec){ // not a regex
25090             value = String(value);
25091             if(value.length == 0){
25092                 return false;
25093             }
25094             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25095         }
25096         return function(r){
25097             return value.test(r.data[property]);
25098         };
25099     },
25100
25101     /**
25102      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25103      * @param {String} property A field on your records
25104      * @param {Number} start The record index to start at (defaults to 0)
25105      * @param {Number} end The last record index to include (defaults to length - 1)
25106      * @return {Number} The sum
25107      */
25108     sum : function(property, start, end){
25109         var rs = this.data.items, v = 0;
25110         start = start || 0;
25111         end = (end || end === 0) ? end : rs.length-1;
25112
25113         for(var i = start; i <= end; i++){
25114             v += (rs[i].data[property] || 0);
25115         }
25116         return v;
25117     },
25118
25119     /**
25120      * Filter the records by a specified property.
25121      * @param {String} field A field on your records
25122      * @param {String/RegExp} value Either a string that the field
25123      * should start with or a RegExp to test against the field
25124      * @param {Boolean} anyMatch True to match any part not just the beginning
25125      */
25126     filter : function(property, value, anyMatch){
25127         var fn = this.createFilterFn(property, value, anyMatch);
25128         return fn ? this.filterBy(fn) : this.clearFilter();
25129     },
25130
25131     /**
25132      * Filter by a function. The specified function will be called with each
25133      * record in this data source. If the function returns true the record is included,
25134      * otherwise it is filtered.
25135      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25136      * @param {Object} scope (optional) The scope of the function (defaults to this)
25137      */
25138     filterBy : function(fn, scope){
25139         this.snapshot = this.snapshot || this.data;
25140         this.data = this.queryBy(fn, scope||this);
25141         this.fireEvent("datachanged", this);
25142     },
25143
25144     /**
25145      * Query the records by a specified property.
25146      * @param {String} field A field on your records
25147      * @param {String/RegExp} value Either a string that the field
25148      * should start with or a RegExp to test against the field
25149      * @param {Boolean} anyMatch True to match any part not just the beginning
25150      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25151      */
25152     query : function(property, value, anyMatch){
25153         var fn = this.createFilterFn(property, value, anyMatch);
25154         return fn ? this.queryBy(fn) : this.data.clone();
25155     },
25156
25157     /**
25158      * Query by a function. The specified function will be called with each
25159      * record in this data source. If the function returns true the record is included
25160      * in the results.
25161      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25162      * @param {Object} scope (optional) The scope of the function (defaults to this)
25163       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25164      **/
25165     queryBy : function(fn, scope){
25166         var data = this.snapshot || this.data;
25167         return data.filterBy(fn, scope||this);
25168     },
25169
25170     /**
25171      * Collects unique values for a particular dataIndex from this store.
25172      * @param {String} dataIndex The property to collect
25173      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25174      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25175      * @return {Array} An array of the unique values
25176      **/
25177     collect : function(dataIndex, allowNull, bypassFilter){
25178         var d = (bypassFilter === true && this.snapshot) ?
25179                 this.snapshot.items : this.data.items;
25180         var v, sv, r = [], l = {};
25181         for(var i = 0, len = d.length; i < len; i++){
25182             v = d[i].data[dataIndex];
25183             sv = String(v);
25184             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25185                 l[sv] = true;
25186                 r[r.length] = v;
25187             }
25188         }
25189         return r;
25190     },
25191
25192     /**
25193      * Revert to a view of the Record cache with no filtering applied.
25194      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25195      */
25196     clearFilter : function(suppressEvent){
25197         if(this.snapshot && this.snapshot != this.data){
25198             this.data = this.snapshot;
25199             delete this.snapshot;
25200             if(suppressEvent !== true){
25201                 this.fireEvent("datachanged", this);
25202             }
25203         }
25204     },
25205
25206     // private
25207     afterEdit : function(record){
25208         if(this.modified.indexOf(record) == -1){
25209             this.modified.push(record);
25210         }
25211         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25212     },
25213     
25214     // private
25215     afterReject : function(record){
25216         this.modified.remove(record);
25217         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25218     },
25219
25220     // private
25221     afterCommit : function(record){
25222         this.modified.remove(record);
25223         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25224     },
25225
25226     /**
25227      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25228      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25229      */
25230     commitChanges : function(){
25231         var m = this.modified.slice(0);
25232         this.modified = [];
25233         for(var i = 0, len = m.length; i < len; i++){
25234             m[i].commit();
25235         }
25236     },
25237
25238     /**
25239      * Cancel outstanding changes on all changed records.
25240      */
25241     rejectChanges : function(){
25242         var m = this.modified.slice(0);
25243         this.modified = [];
25244         for(var i = 0, len = m.length; i < len; i++){
25245             m[i].reject();
25246         }
25247     },
25248
25249     onMetaChange : function(meta, rtype, o){
25250         this.recordType = rtype;
25251         this.fields = rtype.prototype.fields;
25252         delete this.snapshot;
25253         this.sortInfo = meta.sortInfo || this.sortInfo;
25254         this.modified = [];
25255         this.fireEvent('metachange', this, this.reader.meta);
25256     },
25257     
25258     moveIndex : function(data, type)
25259     {
25260         var index = this.indexOf(data);
25261         
25262         var newIndex = index + type;
25263         
25264         this.remove(data);
25265         
25266         this.insert(newIndex, data);
25267         
25268     }
25269 });/*
25270  * Based on:
25271  * Ext JS Library 1.1.1
25272  * Copyright(c) 2006-2007, Ext JS, LLC.
25273  *
25274  * Originally Released Under LGPL - original licence link has changed is not relivant.
25275  *
25276  * Fork - LGPL
25277  * <script type="text/javascript">
25278  */
25279
25280 /**
25281  * @class Roo.data.SimpleStore
25282  * @extends Roo.data.Store
25283  * Small helper class to make creating Stores from Array data easier.
25284  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25285  * @cfg {Array} fields An array of field definition objects, or field name strings.
25286  * @cfg {Object} an existing reader (eg. copied from another store)
25287  * @cfg {Array} data The multi-dimensional array of data
25288  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25289  * @cfg {Roo.data.Reader} reader  [not-required] 
25290  * @constructor
25291  * @param {Object} config
25292  */
25293 Roo.data.SimpleStore = function(config)
25294 {
25295     Roo.data.SimpleStore.superclass.constructor.call(this, {
25296         isLocal : true,
25297         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25298                 id: config.id
25299             },
25300             Roo.data.Record.create(config.fields)
25301         ),
25302         proxy : new Roo.data.MemoryProxy(config.data)
25303     });
25304     this.load();
25305 };
25306 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25307  * Based on:
25308  * Ext JS Library 1.1.1
25309  * Copyright(c) 2006-2007, Ext JS, LLC.
25310  *
25311  * Originally Released Under LGPL - original licence link has changed is not relivant.
25312  *
25313  * Fork - LGPL
25314  * <script type="text/javascript">
25315  */
25316
25317 /**
25318 /**
25319  * @extends Roo.data.Store
25320  * @class Roo.data.JsonStore
25321  * Small helper class to make creating Stores for JSON data easier. <br/>
25322 <pre><code>
25323 var store = new Roo.data.JsonStore({
25324     url: 'get-images.php',
25325     root: 'images',
25326     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25327 });
25328 </code></pre>
25329  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25330  * JsonReader and HttpProxy (unless inline data is provided).</b>
25331  * @cfg {Array} fields An array of field definition objects, or field name strings.
25332  * @constructor
25333  * @param {Object} config
25334  */
25335 Roo.data.JsonStore = function(c){
25336     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25337         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25338         reader: new Roo.data.JsonReader(c, c.fields)
25339     }));
25340 };
25341 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25342  * Based on:
25343  * Ext JS Library 1.1.1
25344  * Copyright(c) 2006-2007, Ext JS, LLC.
25345  *
25346  * Originally Released Under LGPL - original licence link has changed is not relivant.
25347  *
25348  * Fork - LGPL
25349  * <script type="text/javascript">
25350  */
25351
25352  
25353 Roo.data.Field = function(config){
25354     if(typeof config == "string"){
25355         config = {name: config};
25356     }
25357     Roo.apply(this, config);
25358     
25359     if(!this.type){
25360         this.type = "auto";
25361     }
25362     
25363     var st = Roo.data.SortTypes;
25364     // named sortTypes are supported, here we look them up
25365     if(typeof this.sortType == "string"){
25366         this.sortType = st[this.sortType];
25367     }
25368     
25369     // set default sortType for strings and dates
25370     if(!this.sortType){
25371         switch(this.type){
25372             case "string":
25373                 this.sortType = st.asUCString;
25374                 break;
25375             case "date":
25376                 this.sortType = st.asDate;
25377                 break;
25378             default:
25379                 this.sortType = st.none;
25380         }
25381     }
25382
25383     // define once
25384     var stripRe = /[\$,%]/g;
25385
25386     // prebuilt conversion function for this field, instead of
25387     // switching every time we're reading a value
25388     if(!this.convert){
25389         var cv, dateFormat = this.dateFormat;
25390         switch(this.type){
25391             case "":
25392             case "auto":
25393             case undefined:
25394                 cv = function(v){ return v; };
25395                 break;
25396             case "string":
25397                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25398                 break;
25399             case "int":
25400                 cv = function(v){
25401                     return v !== undefined && v !== null && v !== '' ?
25402                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25403                     };
25404                 break;
25405             case "float":
25406                 cv = function(v){
25407                     return v !== undefined && v !== null && v !== '' ?
25408                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25409                     };
25410                 break;
25411             case "bool":
25412             case "boolean":
25413                 cv = function(v){ return v === true || v === "true" || v == 1; };
25414                 break;
25415             case "date":
25416                 cv = function(v){
25417                     if(!v){
25418                         return '';
25419                     }
25420                     if(v instanceof Date){
25421                         return v;
25422                     }
25423                     if(dateFormat){
25424                         if(dateFormat == "timestamp"){
25425                             return new Date(v*1000);
25426                         }
25427                         return Date.parseDate(v, dateFormat);
25428                     }
25429                     var parsed = Date.parse(v);
25430                     return parsed ? new Date(parsed) : null;
25431                 };
25432              break;
25433             
25434         }
25435         this.convert = cv;
25436     }
25437 };
25438
25439 Roo.data.Field.prototype = {
25440     dateFormat: null,
25441     defaultValue: "",
25442     mapping: null,
25443     sortType : null,
25444     sortDir : "ASC"
25445 };/*
25446  * Based on:
25447  * Ext JS Library 1.1.1
25448  * Copyright(c) 2006-2007, Ext JS, LLC.
25449  *
25450  * Originally Released Under LGPL - original licence link has changed is not relivant.
25451  *
25452  * Fork - LGPL
25453  * <script type="text/javascript">
25454  */
25455  
25456 // Base class for reading structured data from a data source.  This class is intended to be
25457 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25458
25459 /**
25460  * @class Roo.data.DataReader
25461  * @abstract
25462  * Base class for reading structured data from a data source.  This class is intended to be
25463  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25464  */
25465
25466 Roo.data.DataReader = function(meta, recordType){
25467     
25468     this.meta = meta;
25469     
25470     this.recordType = recordType instanceof Array ? 
25471         Roo.data.Record.create(recordType) : recordType;
25472 };
25473
25474 Roo.data.DataReader.prototype = {
25475     
25476     
25477     readerType : 'Data',
25478      /**
25479      * Create an empty record
25480      * @param {Object} data (optional) - overlay some values
25481      * @return {Roo.data.Record} record created.
25482      */
25483     newRow :  function(d) {
25484         var da =  {};
25485         this.recordType.prototype.fields.each(function(c) {
25486             switch( c.type) {
25487                 case 'int' : da[c.name] = 0; break;
25488                 case 'date' : da[c.name] = new Date(); break;
25489                 case 'float' : da[c.name] = 0.0; break;
25490                 case 'boolean' : da[c.name] = false; break;
25491                 default : da[c.name] = ""; break;
25492             }
25493             
25494         });
25495         return new this.recordType(Roo.apply(da, d));
25496     }
25497     
25498     
25499 };/*
25500  * Based on:
25501  * Ext JS Library 1.1.1
25502  * Copyright(c) 2006-2007, Ext JS, LLC.
25503  *
25504  * Originally Released Under LGPL - original licence link has changed is not relivant.
25505  *
25506  * Fork - LGPL
25507  * <script type="text/javascript">
25508  */
25509
25510 /**
25511  * @class Roo.data.DataProxy
25512  * @extends Roo.util.Observable
25513  * @abstract
25514  * This class is an abstract base class for implementations which provide retrieval of
25515  * unformatted data objects.<br>
25516  * <p>
25517  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25518  * (of the appropriate type which knows how to parse the data object) to provide a block of
25519  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25520  * <p>
25521  * Custom implementations must implement the load method as described in
25522  * {@link Roo.data.HttpProxy#load}.
25523  */
25524 Roo.data.DataProxy = function(){
25525     this.addEvents({
25526         /**
25527          * @event beforeload
25528          * Fires before a network request is made to retrieve a data object.
25529          * @param {Object} This DataProxy object.
25530          * @param {Object} params The params parameter to the load function.
25531          */
25532         beforeload : true,
25533         /**
25534          * @event load
25535          * Fires before the load method's callback is called.
25536          * @param {Object} This DataProxy object.
25537          * @param {Object} o The data object.
25538          * @param {Object} arg The callback argument object passed to the load function.
25539          */
25540         load : true,
25541         /**
25542          * @event loadexception
25543          * Fires if an Exception occurs during data retrieval.
25544          * @param {Object} This DataProxy object.
25545          * @param {Object} o The data object.
25546          * @param {Object} arg The callback argument object passed to the load function.
25547          * @param {Object} e The Exception.
25548          */
25549         loadexception : true
25550     });
25551     Roo.data.DataProxy.superclass.constructor.call(this);
25552 };
25553
25554 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25555
25556     /**
25557      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25558      */
25559 /*
25560  * Based on:
25561  * Ext JS Library 1.1.1
25562  * Copyright(c) 2006-2007, Ext JS, LLC.
25563  *
25564  * Originally Released Under LGPL - original licence link has changed is not relivant.
25565  *
25566  * Fork - LGPL
25567  * <script type="text/javascript">
25568  */
25569 /**
25570  * @class Roo.data.MemoryProxy
25571  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25572  * to the Reader when its load method is called.
25573  * @constructor
25574  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25575  */
25576 Roo.data.MemoryProxy = function(data){
25577     if (data.data) {
25578         data = data.data;
25579     }
25580     Roo.data.MemoryProxy.superclass.constructor.call(this);
25581     this.data = data;
25582 };
25583
25584 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25585     
25586     /**
25587      * Load data from the requested source (in this case an in-memory
25588      * data object passed to the constructor), read the data object into
25589      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25590      * process that block using the passed callback.
25591      * @param {Object} params This parameter is not used by the MemoryProxy class.
25592      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25593      * object into a block of Roo.data.Records.
25594      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25595      * The function must be passed <ul>
25596      * <li>The Record block object</li>
25597      * <li>The "arg" argument from the load function</li>
25598      * <li>A boolean success indicator</li>
25599      * </ul>
25600      * @param {Object} scope The scope in which to call the callback
25601      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25602      */
25603     load : function(params, reader, callback, scope, arg){
25604         params = params || {};
25605         var result;
25606         try {
25607             result = reader.readRecords(params.data ? params.data :this.data);
25608         }catch(e){
25609             this.fireEvent("loadexception", this, arg, null, e);
25610             callback.call(scope, null, arg, false);
25611             return;
25612         }
25613         callback.call(scope, result, arg, true);
25614     },
25615     
25616     // private
25617     update : function(params, records){
25618         
25619     }
25620 });/*
25621  * Based on:
25622  * Ext JS Library 1.1.1
25623  * Copyright(c) 2006-2007, Ext JS, LLC.
25624  *
25625  * Originally Released Under LGPL - original licence link has changed is not relivant.
25626  *
25627  * Fork - LGPL
25628  * <script type="text/javascript">
25629  */
25630 /**
25631  * @class Roo.data.HttpProxy
25632  * @extends Roo.data.DataProxy
25633  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25634  * configured to reference a certain URL.<br><br>
25635  * <p>
25636  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25637  * from which the running page was served.<br><br>
25638  * <p>
25639  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25640  * <p>
25641  * Be aware that to enable the browser to parse an XML document, the server must set
25642  * the Content-Type header in the HTTP response to "text/xml".
25643  * @constructor
25644  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25645  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25646  * will be used to make the request.
25647  */
25648 Roo.data.HttpProxy = function(conn){
25649     Roo.data.HttpProxy.superclass.constructor.call(this);
25650     // is conn a conn config or a real conn?
25651     this.conn = conn;
25652     this.useAjax = !conn || !conn.events;
25653   
25654 };
25655
25656 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25657     // thse are take from connection...
25658     
25659     /**
25660      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25661      */
25662     /**
25663      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25664      * extra parameters to each request made by this object. (defaults to undefined)
25665      */
25666     /**
25667      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25668      *  to each request made by this object. (defaults to undefined)
25669      */
25670     /**
25671      * @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)
25672      */
25673     /**
25674      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25675      */
25676      /**
25677      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25678      * @type Boolean
25679      */
25680   
25681
25682     /**
25683      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25684      * @type Boolean
25685      */
25686     /**
25687      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25688      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25689      * a finer-grained basis than the DataProxy events.
25690      */
25691     getConnection : function(){
25692         return this.useAjax ? Roo.Ajax : this.conn;
25693     },
25694
25695     /**
25696      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25697      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25698      * process that block using the passed callback.
25699      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25700      * for the request to the remote server.
25701      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25702      * object into a block of Roo.data.Records.
25703      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25704      * The function must be passed <ul>
25705      * <li>The Record block object</li>
25706      * <li>The "arg" argument from the load function</li>
25707      * <li>A boolean success indicator</li>
25708      * </ul>
25709      * @param {Object} scope The scope in which to call the callback
25710      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25711      */
25712     load : function(params, reader, callback, scope, arg){
25713         if(this.fireEvent("beforeload", this, params) !== false){
25714             var  o = {
25715                 params : params || {},
25716                 request: {
25717                     callback : callback,
25718                     scope : scope,
25719                     arg : arg
25720                 },
25721                 reader: reader,
25722                 callback : this.loadResponse,
25723                 scope: this
25724             };
25725             if(this.useAjax){
25726                 Roo.applyIf(o, this.conn);
25727                 if(this.activeRequest){
25728                     Roo.Ajax.abort(this.activeRequest);
25729                 }
25730                 this.activeRequest = Roo.Ajax.request(o);
25731             }else{
25732                 this.conn.request(o);
25733             }
25734         }else{
25735             callback.call(scope||this, null, arg, false);
25736         }
25737     },
25738
25739     // private
25740     loadResponse : function(o, success, response){
25741         delete this.activeRequest;
25742         if(!success){
25743             this.fireEvent("loadexception", this, o, response);
25744             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25745             return;
25746         }
25747         var result;
25748         try {
25749             result = o.reader.read(response);
25750         }catch(e){
25751             o.success = false;
25752             o.raw = { errorMsg : response.responseText };
25753             this.fireEvent("loadexception", this, o, response, e);
25754             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25755             return;
25756         }
25757         
25758         this.fireEvent("load", this, o, o.request.arg);
25759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25760     },
25761
25762     // private
25763     update : function(dataSet){
25764
25765     },
25766
25767     // private
25768     updateResponse : function(dataSet){
25769
25770     }
25771 });/*
25772  * Based on:
25773  * Ext JS Library 1.1.1
25774  * Copyright(c) 2006-2007, Ext JS, LLC.
25775  *
25776  * Originally Released Under LGPL - original licence link has changed is not relivant.
25777  *
25778  * Fork - LGPL
25779  * <script type="text/javascript">
25780  */
25781
25782 /**
25783  * @class Roo.data.ScriptTagProxy
25784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25785  * other than the originating domain of the running page.<br><br>
25786  * <p>
25787  * <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
25788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25789  * <p>
25790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25791  * source code that is used as the source inside a &lt;script> tag.<br><br>
25792  * <p>
25793  * In order for the browser to process the returned data, the server must wrap the data object
25794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25796  * depending on whether the callback name was passed:
25797  * <p>
25798  * <pre><code>
25799 boolean scriptTag = false;
25800 String cb = request.getParameter("callback");
25801 if (cb != null) {
25802     scriptTag = true;
25803     response.setContentType("text/javascript");
25804 } else {
25805     response.setContentType("application/x-json");
25806 }
25807 Writer out = response.getWriter();
25808 if (scriptTag) {
25809     out.write(cb + "(");
25810 }
25811 out.print(dataBlock.toJsonString());
25812 if (scriptTag) {
25813     out.write(");");
25814 }
25815 </pre></code>
25816  *
25817  * @constructor
25818  * @param {Object} config A configuration object.
25819  */
25820 Roo.data.ScriptTagProxy = function(config){
25821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25822     Roo.apply(this, config);
25823     this.head = document.getElementsByTagName("head")[0];
25824 };
25825
25826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25827
25828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25829     /**
25830      * @cfg {String} url The URL from which to request the data object.
25831      */
25832     /**
25833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25834      */
25835     timeout : 30000,
25836     /**
25837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25838      * the server the name of the callback function set up by the load call to process the returned data object.
25839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25840      * javascript output which calls this named function passing the data object as its only parameter.
25841      */
25842     callbackParam : "callback",
25843     /**
25844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25845      * name to the request.
25846      */
25847     nocache : true,
25848
25849     /**
25850      * Load data from the configured URL, read the data object into
25851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25852      * process that block using the passed callback.
25853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25854      * for the request to the remote server.
25855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25856      * object into a block of Roo.data.Records.
25857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25858      * The function must be passed <ul>
25859      * <li>The Record block object</li>
25860      * <li>The "arg" argument from the load function</li>
25861      * <li>A boolean success indicator</li>
25862      * </ul>
25863      * @param {Object} scope The scope in which to call the callback
25864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25865      */
25866     load : function(params, reader, callback, scope, arg){
25867         if(this.fireEvent("beforeload", this, params) !== false){
25868
25869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25870
25871             var url = this.url;
25872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25873             if(this.nocache){
25874                 url += "&_dc=" + (new Date().getTime());
25875             }
25876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25877             var trans = {
25878                 id : transId,
25879                 cb : "stcCallback"+transId,
25880                 scriptId : "stcScript"+transId,
25881                 params : params,
25882                 arg : arg,
25883                 url : url,
25884                 callback : callback,
25885                 scope : scope,
25886                 reader : reader
25887             };
25888             var conn = this;
25889
25890             window[trans.cb] = function(o){
25891                 conn.handleResponse(o, trans);
25892             };
25893
25894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25895
25896             if(this.autoAbort !== false){
25897                 this.abort();
25898             }
25899
25900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25901
25902             var script = document.createElement("script");
25903             script.setAttribute("src", url);
25904             script.setAttribute("type", "text/javascript");
25905             script.setAttribute("id", trans.scriptId);
25906             this.head.appendChild(script);
25907
25908             this.trans = trans;
25909         }else{
25910             callback.call(scope||this, null, arg, false);
25911         }
25912     },
25913
25914     // private
25915     isLoading : function(){
25916         return this.trans ? true : false;
25917     },
25918
25919     /**
25920      * Abort the current server request.
25921      */
25922     abort : function(){
25923         if(this.isLoading()){
25924             this.destroyTrans(this.trans);
25925         }
25926     },
25927
25928     // private
25929     destroyTrans : function(trans, isLoaded){
25930         this.head.removeChild(document.getElementById(trans.scriptId));
25931         clearTimeout(trans.timeoutId);
25932         if(isLoaded){
25933             window[trans.cb] = undefined;
25934             try{
25935                 delete window[trans.cb];
25936             }catch(e){}
25937         }else{
25938             // if hasn't been loaded, wait for load to remove it to prevent script error
25939             window[trans.cb] = function(){
25940                 window[trans.cb] = undefined;
25941                 try{
25942                     delete window[trans.cb];
25943                 }catch(e){}
25944             };
25945         }
25946     },
25947
25948     // private
25949     handleResponse : function(o, trans){
25950         this.trans = false;
25951         this.destroyTrans(trans, true);
25952         var result;
25953         try {
25954             result = trans.reader.readRecords(o);
25955         }catch(e){
25956             this.fireEvent("loadexception", this, o, trans.arg, e);
25957             trans.callback.call(trans.scope||window, null, trans.arg, false);
25958             return;
25959         }
25960         this.fireEvent("load", this, o, trans.arg);
25961         trans.callback.call(trans.scope||window, result, trans.arg, true);
25962     },
25963
25964     // private
25965     handleFailure : function(trans){
25966         this.trans = false;
25967         this.destroyTrans(trans, false);
25968         this.fireEvent("loadexception", this, null, trans.arg);
25969         trans.callback.call(trans.scope||window, null, trans.arg, false);
25970     }
25971 });/*
25972  * Based on:
25973  * Ext JS Library 1.1.1
25974  * Copyright(c) 2006-2007, Ext JS, LLC.
25975  *
25976  * Originally Released Under LGPL - original licence link has changed is not relivant.
25977  *
25978  * Fork - LGPL
25979  * <script type="text/javascript">
25980  */
25981
25982 /**
25983  * @class Roo.data.JsonReader
25984  * @extends Roo.data.DataReader
25985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25986  * based on mappings in a provided Roo.data.Record constructor.
25987  * 
25988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25989  * in the reply previously. 
25990  * 
25991  * <p>
25992  * Example code:
25993  * <pre><code>
25994 var RecordDef = Roo.data.Record.create([
25995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25997 ]);
25998 var myReader = new Roo.data.JsonReader({
25999     totalProperty: "results",    // The property which contains the total dataset size (optional)
26000     root: "rows",                // The property which contains an Array of row objects
26001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26002 }, RecordDef);
26003 </code></pre>
26004  * <p>
26005  * This would consume a JSON file like this:
26006  * <pre><code>
26007 { 'results': 2, 'rows': [
26008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26010 }
26011 </code></pre>
26012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26014  * paged from the remote server.
26015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26016  * @cfg {String} root name of the property which contains the Array of row objects.
26017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26018  * @cfg {Array} fields Array of field definition objects
26019  * @constructor
26020  * Create a new JsonReader
26021  * @param {Object} meta Metadata configuration options
26022  * @param {Object} recordType Either an Array of field definition objects,
26023  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26024  */
26025 Roo.data.JsonReader = function(meta, recordType){
26026     
26027     meta = meta || {};
26028     // set some defaults:
26029     Roo.applyIf(meta, {
26030         totalProperty: 'total',
26031         successProperty : 'success',
26032         root : 'data',
26033         id : 'id'
26034     });
26035     
26036     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26037 };
26038 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26039     
26040     readerType : 'Json',
26041     
26042     /**
26043      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26044      * Used by Store query builder to append _requestMeta to params.
26045      * 
26046      */
26047     metaFromRemote : false,
26048     /**
26049      * This method is only used by a DataProxy which has retrieved data from a remote server.
26050      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26051      * @return {Object} data A data block which is used by an Roo.data.Store object as
26052      * a cache of Roo.data.Records.
26053      */
26054     read : function(response){
26055         var json = response.responseText;
26056        
26057         var o = /* eval:var:o */ eval("("+json+")");
26058         if(!o) {
26059             throw {message: "JsonReader.read: Json object not found"};
26060         }
26061         
26062         if(o.metaData){
26063             
26064             delete this.ef;
26065             this.metaFromRemote = true;
26066             this.meta = o.metaData;
26067             this.recordType = Roo.data.Record.create(o.metaData.fields);
26068             this.onMetaChange(this.meta, this.recordType, o);
26069         }
26070         return this.readRecords(o);
26071     },
26072
26073     // private function a store will implement
26074     onMetaChange : function(meta, recordType, o){
26075
26076     },
26077
26078     /**
26079          * @ignore
26080          */
26081     simpleAccess: function(obj, subsc) {
26082         return obj[subsc];
26083     },
26084
26085         /**
26086          * @ignore
26087          */
26088     getJsonAccessor: function(){
26089         var re = /[\[\.]/;
26090         return function(expr) {
26091             try {
26092                 return(re.test(expr))
26093                     ? new Function("obj", "return obj." + expr)
26094                     : function(obj){
26095                         return obj[expr];
26096                     };
26097             } catch(e){}
26098             return Roo.emptyFn;
26099         };
26100     }(),
26101
26102     /**
26103      * Create a data block containing Roo.data.Records from an XML document.
26104      * @param {Object} o An object which contains an Array of row objects in the property specified
26105      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26106      * which contains the total size of the dataset.
26107      * @return {Object} data A data block which is used by an Roo.data.Store object as
26108      * a cache of Roo.data.Records.
26109      */
26110     readRecords : function(o){
26111         /**
26112          * After any data loads, the raw JSON data is available for further custom processing.
26113          * @type Object
26114          */
26115         this.o = o;
26116         var s = this.meta, Record = this.recordType,
26117             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26118
26119 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26120         if (!this.ef) {
26121             if(s.totalProperty) {
26122                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26123                 }
26124                 if(s.successProperty) {
26125                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26126                 }
26127                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26128                 if (s.id) {
26129                         var g = this.getJsonAccessor(s.id);
26130                         this.getId = function(rec) {
26131                                 var r = g(rec);  
26132                                 return (r === undefined || r === "") ? null : r;
26133                         };
26134                 } else {
26135                         this.getId = function(){return null;};
26136                 }
26137             this.ef = [];
26138             for(var jj = 0; jj < fl; jj++){
26139                 f = fi[jj];
26140                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26141                 this.ef[jj] = this.getJsonAccessor(map);
26142             }
26143         }
26144
26145         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26146         if(s.totalProperty){
26147             var vt = parseInt(this.getTotal(o), 10);
26148             if(!isNaN(vt)){
26149                 totalRecords = vt;
26150             }
26151         }
26152         if(s.successProperty){
26153             var vs = this.getSuccess(o);
26154             if(vs === false || vs === 'false'){
26155                 success = false;
26156             }
26157         }
26158         var records = [];
26159         for(var i = 0; i < c; i++){
26160             var n = root[i];
26161             var values = {};
26162             var id = this.getId(n);
26163             for(var j = 0; j < fl; j++){
26164                 f = fi[j];
26165                                 var v = this.ef[j](n);
26166                                 if (!f.convert) {
26167                                         Roo.log('missing convert for ' + f.name);
26168                                         Roo.log(f);
26169                                         continue;
26170                                 }
26171                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26172             }
26173                         if (!Record) {
26174                                 return {
26175                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26176                                         success : false,
26177                                         records : [],
26178                                         totalRecords : 0
26179                                 };
26180                         }
26181             var record = new Record(values, id);
26182             record.json = n;
26183             records[i] = record;
26184         }
26185         return {
26186             raw : o,
26187             success : success,
26188             records : records,
26189             totalRecords : totalRecords
26190         };
26191     },
26192     // used when loading children.. @see loadDataFromChildren
26193     toLoadData: function(rec)
26194     {
26195         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26196         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26197         return { data : data, total : data.length };
26198         
26199     }
26200 });/*
26201  * Based on:
26202  * Ext JS Library 1.1.1
26203  * Copyright(c) 2006-2007, Ext JS, LLC.
26204  *
26205  * Originally Released Under LGPL - original licence link has changed is not relivant.
26206  *
26207  * Fork - LGPL
26208  * <script type="text/javascript">
26209  */
26210
26211 /**
26212  * @class Roo.data.XmlReader
26213  * @extends Roo.data.DataReader
26214  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26215  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26216  * <p>
26217  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26218  * header in the HTTP response must be set to "text/xml".</em>
26219  * <p>
26220  * Example code:
26221  * <pre><code>
26222 var RecordDef = Roo.data.Record.create([
26223    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26224    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26225 ]);
26226 var myReader = new Roo.data.XmlReader({
26227    totalRecords: "results", // The element which contains the total dataset size (optional)
26228    record: "row",           // The repeated element which contains row information
26229    id: "id"                 // The element within the row that provides an ID for the record (optional)
26230 }, RecordDef);
26231 </code></pre>
26232  * <p>
26233  * This would consume an XML file like this:
26234  * <pre><code>
26235 &lt;?xml?>
26236 &lt;dataset>
26237  &lt;results>2&lt;/results>
26238  &lt;row>
26239    &lt;id>1&lt;/id>
26240    &lt;name>Bill&lt;/name>
26241    &lt;occupation>Gardener&lt;/occupation>
26242  &lt;/row>
26243  &lt;row>
26244    &lt;id>2&lt;/id>
26245    &lt;name>Ben&lt;/name>
26246    &lt;occupation>Horticulturalist&lt;/occupation>
26247  &lt;/row>
26248 &lt;/dataset>
26249 </code></pre>
26250  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26251  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26252  * paged from the remote server.
26253  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26254  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26255  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26256  * a record identifier value.
26257  * @constructor
26258  * Create a new XmlReader
26259  * @param {Object} meta Metadata configuration options
26260  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26261  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26262  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26263  */
26264 Roo.data.XmlReader = function(meta, recordType){
26265     meta = meta || {};
26266     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26267 };
26268 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26269     
26270     readerType : 'Xml',
26271     
26272     /**
26273      * This method is only used by a DataProxy which has retrieved data from a remote server.
26274          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26275          * to contain a method called 'responseXML' that returns an XML document object.
26276      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26277      * a cache of Roo.data.Records.
26278      */
26279     read : function(response){
26280         var doc = response.responseXML;
26281         if(!doc) {
26282             throw {message: "XmlReader.read: XML Document not available"};
26283         }
26284         return this.readRecords(doc);
26285     },
26286
26287     /**
26288      * Create a data block containing Roo.data.Records from an XML document.
26289          * @param {Object} doc A parsed XML document.
26290      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26291      * a cache of Roo.data.Records.
26292      */
26293     readRecords : function(doc){
26294         /**
26295          * After any data loads/reads, the raw XML Document is available for further custom processing.
26296          * @type XMLDocument
26297          */
26298         this.xmlData = doc;
26299         var root = doc.documentElement || doc;
26300         var q = Roo.DomQuery;
26301         var recordType = this.recordType, fields = recordType.prototype.fields;
26302         var sid = this.meta.id;
26303         var totalRecords = 0, success = true;
26304         if(this.meta.totalRecords){
26305             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26306         }
26307         
26308         if(this.meta.success){
26309             var sv = q.selectValue(this.meta.success, root, true);
26310             success = sv !== false && sv !== 'false';
26311         }
26312         var records = [];
26313         var ns = q.select(this.meta.record, root);
26314         for(var i = 0, len = ns.length; i < len; i++) {
26315                 var n = ns[i];
26316                 var values = {};
26317                 var id = sid ? q.selectValue(sid, n) : undefined;
26318                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26319                     var f = fields.items[j];
26320                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26321                     v = f.convert(v);
26322                     values[f.name] = v;
26323                 }
26324                 var record = new recordType(values, id);
26325                 record.node = n;
26326                 records[records.length] = record;
26327             }
26328
26329             return {
26330                 success : success,
26331                 records : records,
26332                 totalRecords : totalRecords || records.length
26333             };
26334     }
26335 });/*
26336  * Based on:
26337  * Ext JS Library 1.1.1
26338  * Copyright(c) 2006-2007, Ext JS, LLC.
26339  *
26340  * Originally Released Under LGPL - original licence link has changed is not relivant.
26341  *
26342  * Fork - LGPL
26343  * <script type="text/javascript">
26344  */
26345
26346 /**
26347  * @class Roo.data.ArrayReader
26348  * @extends Roo.data.DataReader
26349  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26350  * Each element of that Array represents a row of data fields. The
26351  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26352  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26353  * <p>
26354  * Example code:.
26355  * <pre><code>
26356 var RecordDef = Roo.data.Record.create([
26357     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26358     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26359 ]);
26360 var myReader = new Roo.data.ArrayReader({
26361     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26362 }, RecordDef);
26363 </code></pre>
26364  * <p>
26365  * This would consume an Array like this:
26366  * <pre><code>
26367 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26368   </code></pre>
26369  
26370  * @constructor
26371  * Create a new JsonReader
26372  * @param {Object} meta Metadata configuration options.
26373  * @param {Object|Array} recordType Either an Array of field definition objects
26374  * 
26375  * @cfg {Array} fields Array of field definition objects
26376  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26377  * as specified to {@link Roo.data.Record#create},
26378  * or an {@link Roo.data.Record} object
26379  *
26380  * 
26381  * created using {@link Roo.data.Record#create}.
26382  */
26383 Roo.data.ArrayReader = function(meta, recordType)
26384 {    
26385     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26386 };
26387
26388 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26389     
26390       /**
26391      * Create a data block containing Roo.data.Records from an XML document.
26392      * @param {Object} o An Array of row objects which represents the dataset.
26393      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26394      * a cache of Roo.data.Records.
26395      */
26396     readRecords : function(o)
26397     {
26398         var sid = this.meta ? this.meta.id : null;
26399         var recordType = this.recordType, fields = recordType.prototype.fields;
26400         var records = [];
26401         var root = o;
26402         for(var i = 0; i < root.length; i++){
26403             var n = root[i];
26404             var values = {};
26405             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26406             for(var j = 0, jlen = fields.length; j < jlen; j++){
26407                 var f = fields.items[j];
26408                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26409                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26410                 v = f.convert(v);
26411                 values[f.name] = v;
26412             }
26413             var record = new recordType(values, id);
26414             record.json = n;
26415             records[records.length] = record;
26416         }
26417         return {
26418             records : records,
26419             totalRecords : records.length
26420         };
26421     },
26422     // used when loading children.. @see loadDataFromChildren
26423     toLoadData: function(rec)
26424     {
26425         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26426         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26427         
26428     }
26429     
26430     
26431 });/*
26432  * Based on:
26433  * Ext JS Library 1.1.1
26434  * Copyright(c) 2006-2007, Ext JS, LLC.
26435  *
26436  * Originally Released Under LGPL - original licence link has changed is not relivant.
26437  *
26438  * Fork - LGPL
26439  * <script type="text/javascript">
26440  */
26441
26442
26443 /**
26444  * @class Roo.data.Tree
26445  * @extends Roo.util.Observable
26446  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26447  * in the tree have most standard DOM functionality.
26448  * @constructor
26449  * @param {Node} root (optional) The root node
26450  */
26451 Roo.data.Tree = function(root){
26452    this.nodeHash = {};
26453    /**
26454     * The root node for this tree
26455     * @type Node
26456     */
26457    this.root = null;
26458    if(root){
26459        this.setRootNode(root);
26460    }
26461    this.addEvents({
26462        /**
26463         * @event append
26464         * Fires when a new child node is appended to a node in this tree.
26465         * @param {Tree} tree The owner tree
26466         * @param {Node} parent The parent node
26467         * @param {Node} node The newly appended node
26468         * @param {Number} index The index of the newly appended node
26469         */
26470        "append" : true,
26471        /**
26472         * @event remove
26473         * Fires when a child node is removed from a node in this tree.
26474         * @param {Tree} tree The owner tree
26475         * @param {Node} parent The parent node
26476         * @param {Node} node The child node removed
26477         */
26478        "remove" : true,
26479        /**
26480         * @event move
26481         * Fires when a node is moved to a new location in the tree
26482         * @param {Tree} tree The owner tree
26483         * @param {Node} node The node moved
26484         * @param {Node} oldParent The old parent of this node
26485         * @param {Node} newParent The new parent of this node
26486         * @param {Number} index The index it was moved to
26487         */
26488        "move" : true,
26489        /**
26490         * @event insert
26491         * Fires when a new child node is inserted in a node in this tree.
26492         * @param {Tree} tree The owner tree
26493         * @param {Node} parent The parent node
26494         * @param {Node} node The child node inserted
26495         * @param {Node} refNode The child node the node was inserted before
26496         */
26497        "insert" : true,
26498        /**
26499         * @event beforeappend
26500         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26501         * @param {Tree} tree The owner tree
26502         * @param {Node} parent The parent node
26503         * @param {Node} node The child node to be appended
26504         */
26505        "beforeappend" : true,
26506        /**
26507         * @event beforeremove
26508         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26509         * @param {Tree} tree The owner tree
26510         * @param {Node} parent The parent node
26511         * @param {Node} node The child node to be removed
26512         */
26513        "beforeremove" : true,
26514        /**
26515         * @event beforemove
26516         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26517         * @param {Tree} tree The owner tree
26518         * @param {Node} node The node being moved
26519         * @param {Node} oldParent The parent of the node
26520         * @param {Node} newParent The new parent the node is moving to
26521         * @param {Number} index The index it is being moved to
26522         */
26523        "beforemove" : true,
26524        /**
26525         * @event beforeinsert
26526         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26527         * @param {Tree} tree The owner tree
26528         * @param {Node} parent The parent node
26529         * @param {Node} node The child node to be inserted
26530         * @param {Node} refNode The child node the node is being inserted before
26531         */
26532        "beforeinsert" : true
26533    });
26534
26535     Roo.data.Tree.superclass.constructor.call(this);
26536 };
26537
26538 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26539     pathSeparator: "/",
26540
26541     proxyNodeEvent : function(){
26542         return this.fireEvent.apply(this, arguments);
26543     },
26544
26545     /**
26546      * Returns the root node for this tree.
26547      * @return {Node}
26548      */
26549     getRootNode : function(){
26550         return this.root;
26551     },
26552
26553     /**
26554      * Sets the root node for this tree.
26555      * @param {Node} node
26556      * @return {Node}
26557      */
26558     setRootNode : function(node){
26559         this.root = node;
26560         node.ownerTree = this;
26561         node.isRoot = true;
26562         this.registerNode(node);
26563         return node;
26564     },
26565
26566     /**
26567      * Gets a node in this tree by its id.
26568      * @param {String} id
26569      * @return {Node}
26570      */
26571     getNodeById : function(id){
26572         return this.nodeHash[id];
26573     },
26574
26575     registerNode : function(node){
26576         this.nodeHash[node.id] = node;
26577     },
26578
26579     unregisterNode : function(node){
26580         delete this.nodeHash[node.id];
26581     },
26582
26583     toString : function(){
26584         return "[Tree"+(this.id?" "+this.id:"")+"]";
26585     }
26586 });
26587
26588 /**
26589  * @class Roo.data.Node
26590  * @extends Roo.util.Observable
26591  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26592  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26593  * @constructor
26594  * @param {Object} attributes The attributes/config for the node
26595  */
26596 Roo.data.Node = function(attributes){
26597     /**
26598      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26599      * @type {Object}
26600      */
26601     this.attributes = attributes || {};
26602     this.leaf = this.attributes.leaf;
26603     /**
26604      * The node id. @type String
26605      */
26606     this.id = this.attributes.id;
26607     if(!this.id){
26608         this.id = Roo.id(null, "ynode-");
26609         this.attributes.id = this.id;
26610     }
26611      
26612     
26613     /**
26614      * All child nodes of this node. @type Array
26615      */
26616     this.childNodes = [];
26617     if(!this.childNodes.indexOf){ // indexOf is a must
26618         this.childNodes.indexOf = function(o){
26619             for(var i = 0, len = this.length; i < len; i++){
26620                 if(this[i] == o) {
26621                     return i;
26622                 }
26623             }
26624             return -1;
26625         };
26626     }
26627     /**
26628      * The parent node for this node. @type Node
26629      */
26630     this.parentNode = null;
26631     /**
26632      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26633      */
26634     this.firstChild = null;
26635     /**
26636      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26637      */
26638     this.lastChild = null;
26639     /**
26640      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26641      */
26642     this.previousSibling = null;
26643     /**
26644      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26645      */
26646     this.nextSibling = null;
26647
26648     this.addEvents({
26649        /**
26650         * @event append
26651         * Fires when a new child node is appended
26652         * @param {Tree} tree The owner tree
26653         * @param {Node} this This node
26654         * @param {Node} node The newly appended node
26655         * @param {Number} index The index of the newly appended node
26656         */
26657        "append" : true,
26658        /**
26659         * @event remove
26660         * Fires when a child node is removed
26661         * @param {Tree} tree The owner tree
26662         * @param {Node} this This node
26663         * @param {Node} node The removed node
26664         */
26665        "remove" : true,
26666        /**
26667         * @event move
26668         * Fires when this node is moved to a new location in the tree
26669         * @param {Tree} tree The owner tree
26670         * @param {Node} this This node
26671         * @param {Node} oldParent The old parent of this node
26672         * @param {Node} newParent The new parent of this node
26673         * @param {Number} index The index it was moved to
26674         */
26675        "move" : true,
26676        /**
26677         * @event insert
26678         * Fires when a new child node is inserted.
26679         * @param {Tree} tree The owner tree
26680         * @param {Node} this This node
26681         * @param {Node} node The child node inserted
26682         * @param {Node} refNode The child node the node was inserted before
26683         */
26684        "insert" : true,
26685        /**
26686         * @event beforeappend
26687         * Fires before a new child is appended, return false to cancel the append.
26688         * @param {Tree} tree The owner tree
26689         * @param {Node} this This node
26690         * @param {Node} node The child node to be appended
26691         */
26692        "beforeappend" : true,
26693        /**
26694         * @event beforeremove
26695         * Fires before a child is removed, return false to cancel the remove.
26696         * @param {Tree} tree The owner tree
26697         * @param {Node} this This node
26698         * @param {Node} node The child node to be removed
26699         */
26700        "beforeremove" : true,
26701        /**
26702         * @event beforemove
26703         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26704         * @param {Tree} tree The owner tree
26705         * @param {Node} this This node
26706         * @param {Node} oldParent The parent of this node
26707         * @param {Node} newParent The new parent this node is moving to
26708         * @param {Number} index The index it is being moved to
26709         */
26710        "beforemove" : true,
26711        /**
26712         * @event beforeinsert
26713         * Fires before a new child is inserted, return false to cancel the insert.
26714         * @param {Tree} tree The owner tree
26715         * @param {Node} this This node
26716         * @param {Node} node The child node to be inserted
26717         * @param {Node} refNode The child node the node is being inserted before
26718         */
26719        "beforeinsert" : true
26720    });
26721     this.listeners = this.attributes.listeners;
26722     Roo.data.Node.superclass.constructor.call(this);
26723 };
26724
26725 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26726     fireEvent : function(evtName){
26727         // first do standard event for this node
26728         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26729             return false;
26730         }
26731         // then bubble it up to the tree if the event wasn't cancelled
26732         var ot = this.getOwnerTree();
26733         if(ot){
26734             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26735                 return false;
26736             }
26737         }
26738         return true;
26739     },
26740
26741     /**
26742      * Returns true if this node is a leaf
26743      * @return {Boolean}
26744      */
26745     isLeaf : function(){
26746         return this.leaf === true;
26747     },
26748
26749     // private
26750     setFirstChild : function(node){
26751         this.firstChild = node;
26752     },
26753
26754     //private
26755     setLastChild : function(node){
26756         this.lastChild = node;
26757     },
26758
26759
26760     /**
26761      * Returns true if this node is the last child of its parent
26762      * @return {Boolean}
26763      */
26764     isLast : function(){
26765        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26766     },
26767
26768     /**
26769      * Returns true if this node is the first child of its parent
26770      * @return {Boolean}
26771      */
26772     isFirst : function(){
26773        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26774     },
26775
26776     hasChildNodes : function(){
26777         return !this.isLeaf() && this.childNodes.length > 0;
26778     },
26779
26780     /**
26781      * Insert node(s) as the last child node of this node.
26782      * @param {Node/Array} node The node or Array of nodes to append
26783      * @return {Node} The appended node if single append, or null if an array was passed
26784      */
26785     appendChild : function(node){
26786         var multi = false;
26787         if(node instanceof Array){
26788             multi = node;
26789         }else if(arguments.length > 1){
26790             multi = arguments;
26791         }
26792         
26793         // if passed an array or multiple args do them one by one
26794         if(multi){
26795             for(var i = 0, len = multi.length; i < len; i++) {
26796                 this.appendChild(multi[i]);
26797             }
26798         }else{
26799             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26800                 return false;
26801             }
26802             var index = this.childNodes.length;
26803             var oldParent = node.parentNode;
26804             // it's a move, make sure we move it cleanly
26805             if(oldParent){
26806                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26807                     return false;
26808                 }
26809                 oldParent.removeChild(node);
26810             }
26811             
26812             index = this.childNodes.length;
26813             if(index == 0){
26814                 this.setFirstChild(node);
26815             }
26816             this.childNodes.push(node);
26817             node.parentNode = this;
26818             var ps = this.childNodes[index-1];
26819             if(ps){
26820                 node.previousSibling = ps;
26821                 ps.nextSibling = node;
26822             }else{
26823                 node.previousSibling = null;
26824             }
26825             node.nextSibling = null;
26826             this.setLastChild(node);
26827             node.setOwnerTree(this.getOwnerTree());
26828             this.fireEvent("append", this.ownerTree, this, node, index);
26829             if(this.ownerTree) {
26830                 this.ownerTree.fireEvent("appendnode", this, node, index);
26831             }
26832             if(oldParent){
26833                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26834             }
26835             return node;
26836         }
26837     },
26838
26839     /**
26840      * Removes a child node from this node.
26841      * @param {Node} node The node to remove
26842      * @return {Node} The removed node
26843      */
26844     removeChild : function(node){
26845         var index = this.childNodes.indexOf(node);
26846         if(index == -1){
26847             return false;
26848         }
26849         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26850             return false;
26851         }
26852
26853         // remove it from childNodes collection
26854         this.childNodes.splice(index, 1);
26855
26856         // update siblings
26857         if(node.previousSibling){
26858             node.previousSibling.nextSibling = node.nextSibling;
26859         }
26860         if(node.nextSibling){
26861             node.nextSibling.previousSibling = node.previousSibling;
26862         }
26863
26864         // update child refs
26865         if(this.firstChild == node){
26866             this.setFirstChild(node.nextSibling);
26867         }
26868         if(this.lastChild == node){
26869             this.setLastChild(node.previousSibling);
26870         }
26871
26872         node.setOwnerTree(null);
26873         // clear any references from the node
26874         node.parentNode = null;
26875         node.previousSibling = null;
26876         node.nextSibling = null;
26877         this.fireEvent("remove", this.ownerTree, this, node);
26878         return node;
26879     },
26880
26881     /**
26882      * Inserts the first node before the second node in this nodes childNodes collection.
26883      * @param {Node} node The node to insert
26884      * @param {Node} refNode The node to insert before (if null the node is appended)
26885      * @return {Node} The inserted node
26886      */
26887     insertBefore : function(node, refNode){
26888         if(!refNode){ // like standard Dom, refNode can be null for append
26889             return this.appendChild(node);
26890         }
26891         // nothing to do
26892         if(node == refNode){
26893             return false;
26894         }
26895
26896         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26897             return false;
26898         }
26899         var index = this.childNodes.indexOf(refNode);
26900         var oldParent = node.parentNode;
26901         var refIndex = index;
26902
26903         // when moving internally, indexes will change after remove
26904         if(oldParent == this && this.childNodes.indexOf(node) < index){
26905             refIndex--;
26906         }
26907
26908         // it's a move, make sure we move it cleanly
26909         if(oldParent){
26910             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26911                 return false;
26912             }
26913             oldParent.removeChild(node);
26914         }
26915         if(refIndex == 0){
26916             this.setFirstChild(node);
26917         }
26918         this.childNodes.splice(refIndex, 0, node);
26919         node.parentNode = this;
26920         var ps = this.childNodes[refIndex-1];
26921         if(ps){
26922             node.previousSibling = ps;
26923             ps.nextSibling = node;
26924         }else{
26925             node.previousSibling = null;
26926         }
26927         node.nextSibling = refNode;
26928         refNode.previousSibling = node;
26929         node.setOwnerTree(this.getOwnerTree());
26930         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26931         if(oldParent){
26932             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26933         }
26934         return node;
26935     },
26936
26937     /**
26938      * Returns the child node at the specified index.
26939      * @param {Number} index
26940      * @return {Node}
26941      */
26942     item : function(index){
26943         return this.childNodes[index];
26944     },
26945
26946     /**
26947      * Replaces one child node in this node with another.
26948      * @param {Node} newChild The replacement node
26949      * @param {Node} oldChild The node to replace
26950      * @return {Node} The replaced node
26951      */
26952     replaceChild : function(newChild, oldChild){
26953         this.insertBefore(newChild, oldChild);
26954         this.removeChild(oldChild);
26955         return oldChild;
26956     },
26957
26958     /**
26959      * Returns the index of a child node
26960      * @param {Node} node
26961      * @return {Number} The index of the node or -1 if it was not found
26962      */
26963     indexOf : function(child){
26964         return this.childNodes.indexOf(child);
26965     },
26966
26967     /**
26968      * Returns the tree this node is in.
26969      * @return {Tree}
26970      */
26971     getOwnerTree : function(){
26972         // if it doesn't have one, look for one
26973         if(!this.ownerTree){
26974             var p = this;
26975             while(p){
26976                 if(p.ownerTree){
26977                     this.ownerTree = p.ownerTree;
26978                     break;
26979                 }
26980                 p = p.parentNode;
26981             }
26982         }
26983         return this.ownerTree;
26984     },
26985
26986     /**
26987      * Returns depth of this node (the root node has a depth of 0)
26988      * @return {Number}
26989      */
26990     getDepth : function(){
26991         var depth = 0;
26992         var p = this;
26993         while(p.parentNode){
26994             ++depth;
26995             p = p.parentNode;
26996         }
26997         return depth;
26998     },
26999
27000     // private
27001     setOwnerTree : function(tree){
27002         // if it's move, we need to update everyone
27003         if(tree != this.ownerTree){
27004             if(this.ownerTree){
27005                 this.ownerTree.unregisterNode(this);
27006             }
27007             this.ownerTree = tree;
27008             var cs = this.childNodes;
27009             for(var i = 0, len = cs.length; i < len; i++) {
27010                 cs[i].setOwnerTree(tree);
27011             }
27012             if(tree){
27013                 tree.registerNode(this);
27014             }
27015         }
27016     },
27017
27018     /**
27019      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27020      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27021      * @return {String} The path
27022      */
27023     getPath : function(attr){
27024         attr = attr || "id";
27025         var p = this.parentNode;
27026         var b = [this.attributes[attr]];
27027         while(p){
27028             b.unshift(p.attributes[attr]);
27029             p = p.parentNode;
27030         }
27031         var sep = this.getOwnerTree().pathSeparator;
27032         return sep + b.join(sep);
27033     },
27034
27035     /**
27036      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27037      * function call will be the scope provided or the current node. The arguments to the function
27038      * will be the args provided or the current node. If the function returns false at any point,
27039      * the bubble is stopped.
27040      * @param {Function} fn The function to call
27041      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27042      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27043      */
27044     bubble : function(fn, scope, args){
27045         var p = this;
27046         while(p){
27047             if(fn.call(scope || p, args || p) === false){
27048                 break;
27049             }
27050             p = p.parentNode;
27051         }
27052     },
27053
27054     /**
27055      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27056      * function call will be the scope provided or the current node. The arguments to the function
27057      * will be the args provided or the current node. If the function returns false at any point,
27058      * the cascade is stopped on that branch.
27059      * @param {Function} fn The function to call
27060      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27061      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27062      */
27063     cascade : function(fn, scope, args){
27064         if(fn.call(scope || this, args || this) !== false){
27065             var cs = this.childNodes;
27066             for(var i = 0, len = cs.length; i < len; i++) {
27067                 cs[i].cascade(fn, scope, args);
27068             }
27069         }
27070     },
27071
27072     /**
27073      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27074      * function call will be the scope provided or the current node. The arguments to the function
27075      * will be the args provided or the current node. If the function returns false at any point,
27076      * the iteration stops.
27077      * @param {Function} fn The function to call
27078      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27079      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27080      */
27081     eachChild : function(fn, scope, args){
27082         var cs = this.childNodes;
27083         for(var i = 0, len = cs.length; i < len; i++) {
27084                 if(fn.call(scope || this, args || cs[i]) === false){
27085                     break;
27086                 }
27087         }
27088     },
27089
27090     /**
27091      * Finds the first child that has the attribute with the specified value.
27092      * @param {String} attribute The attribute name
27093      * @param {Mixed} value The value to search for
27094      * @return {Node} The found child or null if none was found
27095      */
27096     findChild : function(attribute, value){
27097         var cs = this.childNodes;
27098         for(var i = 0, len = cs.length; i < len; i++) {
27099                 if(cs[i].attributes[attribute] == value){
27100                     return cs[i];
27101                 }
27102         }
27103         return null;
27104     },
27105
27106     /**
27107      * Finds the first child by a custom function. The child matches if the function passed
27108      * returns true.
27109      * @param {Function} fn
27110      * @param {Object} scope (optional)
27111      * @return {Node} The found child or null if none was found
27112      */
27113     findChildBy : function(fn, scope){
27114         var cs = this.childNodes;
27115         for(var i = 0, len = cs.length; i < len; i++) {
27116                 if(fn.call(scope||cs[i], cs[i]) === true){
27117                     return cs[i];
27118                 }
27119         }
27120         return null;
27121     },
27122
27123     /**
27124      * Sorts this nodes children using the supplied sort function
27125      * @param {Function} fn
27126      * @param {Object} scope (optional)
27127      */
27128     sort : function(fn, scope){
27129         var cs = this.childNodes;
27130         var len = cs.length;
27131         if(len > 0){
27132             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27133             cs.sort(sortFn);
27134             for(var i = 0; i < len; i++){
27135                 var n = cs[i];
27136                 n.previousSibling = cs[i-1];
27137                 n.nextSibling = cs[i+1];
27138                 if(i == 0){
27139                     this.setFirstChild(n);
27140                 }
27141                 if(i == len-1){
27142                     this.setLastChild(n);
27143                 }
27144             }
27145         }
27146     },
27147
27148     /**
27149      * Returns true if this node is an ancestor (at any point) of the passed node.
27150      * @param {Node} node
27151      * @return {Boolean}
27152      */
27153     contains : function(node){
27154         return node.isAncestor(this);
27155     },
27156
27157     /**
27158      * Returns true if the passed node is an ancestor (at any point) of this node.
27159      * @param {Node} node
27160      * @return {Boolean}
27161      */
27162     isAncestor : function(node){
27163         var p = this.parentNode;
27164         while(p){
27165             if(p == node){
27166                 return true;
27167             }
27168             p = p.parentNode;
27169         }
27170         return false;
27171     },
27172
27173     toString : function(){
27174         return "[Node"+(this.id?" "+this.id:"")+"]";
27175     }
27176 });/*
27177  * Based on:
27178  * Ext JS Library 1.1.1
27179  * Copyright(c) 2006-2007, Ext JS, LLC.
27180  *
27181  * Originally Released Under LGPL - original licence link has changed is not relivant.
27182  *
27183  * Fork - LGPL
27184  * <script type="text/javascript">
27185  */
27186
27187
27188 /**
27189  * @class Roo.Shadow
27190  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27191  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27192  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27193  * @constructor
27194  * Create a new Shadow
27195  * @param {Object} config The config object
27196  */
27197 Roo.Shadow = function(config){
27198     Roo.apply(this, config);
27199     if(typeof this.mode != "string"){
27200         this.mode = this.defaultMode;
27201     }
27202     var o = this.offset, a = {h: 0};
27203     var rad = Math.floor(this.offset/2);
27204     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27205         case "drop":
27206             a.w = 0;
27207             a.l = a.t = o;
27208             a.t -= 1;
27209             if(Roo.isIE){
27210                 a.l -= this.offset + rad;
27211                 a.t -= this.offset + rad;
27212                 a.w -= rad;
27213                 a.h -= rad;
27214                 a.t += 1;
27215             }
27216         break;
27217         case "sides":
27218             a.w = (o*2);
27219             a.l = -o;
27220             a.t = o-1;
27221             if(Roo.isIE){
27222                 a.l -= (this.offset - rad);
27223                 a.t -= this.offset + rad;
27224                 a.l += 1;
27225                 a.w -= (this.offset - rad)*2;
27226                 a.w -= rad + 1;
27227                 a.h -= 1;
27228             }
27229         break;
27230         case "frame":
27231             a.w = a.h = (o*2);
27232             a.l = a.t = -o;
27233             a.t += 1;
27234             a.h -= 2;
27235             if(Roo.isIE){
27236                 a.l -= (this.offset - rad);
27237                 a.t -= (this.offset - rad);
27238                 a.l += 1;
27239                 a.w -= (this.offset + rad + 1);
27240                 a.h -= (this.offset + rad);
27241                 a.h += 1;
27242             }
27243         break;
27244     };
27245
27246     this.adjusts = a;
27247 };
27248
27249 Roo.Shadow.prototype = {
27250     /**
27251      * @cfg {String} mode
27252      * The shadow display mode.  Supports the following options:<br />
27253      * sides: Shadow displays on both sides and bottom only<br />
27254      * frame: Shadow displays equally on all four sides<br />
27255      * drop: Traditional bottom-right drop shadow (default)
27256      */
27257     mode: false,
27258     /**
27259      * @cfg {String} offset
27260      * The number of pixels to offset the shadow from the element (defaults to 4)
27261      */
27262     offset: 4,
27263
27264     // private
27265     defaultMode: "drop",
27266
27267     /**
27268      * Displays the shadow under the target element
27269      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27270      */
27271     show : function(target){
27272         target = Roo.get(target);
27273         if(!this.el){
27274             this.el = Roo.Shadow.Pool.pull();
27275             if(this.el.dom.nextSibling != target.dom){
27276                 this.el.insertBefore(target);
27277             }
27278         }
27279         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27280         if(Roo.isIE){
27281             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27282         }
27283         this.realign(
27284             target.getLeft(true),
27285             target.getTop(true),
27286             target.getWidth(),
27287             target.getHeight()
27288         );
27289         this.el.dom.style.display = "block";
27290     },
27291
27292     /**
27293      * Returns true if the shadow is visible, else false
27294      */
27295     isVisible : function(){
27296         return this.el ? true : false;  
27297     },
27298
27299     /**
27300      * Direct alignment when values are already available. Show must be called at least once before
27301      * calling this method to ensure it is initialized.
27302      * @param {Number} left The target element left position
27303      * @param {Number} top The target element top position
27304      * @param {Number} width The target element width
27305      * @param {Number} height The target element height
27306      */
27307     realign : function(l, t, w, h){
27308         if(!this.el){
27309             return;
27310         }
27311         var a = this.adjusts, d = this.el.dom, s = d.style;
27312         var iea = 0;
27313         s.left = (l+a.l)+"px";
27314         s.top = (t+a.t)+"px";
27315         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27316  
27317         if(s.width != sws || s.height != shs){
27318             s.width = sws;
27319             s.height = shs;
27320             if(!Roo.isIE){
27321                 var cn = d.childNodes;
27322                 var sww = Math.max(0, (sw-12))+"px";
27323                 cn[0].childNodes[1].style.width = sww;
27324                 cn[1].childNodes[1].style.width = sww;
27325                 cn[2].childNodes[1].style.width = sww;
27326                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27327             }
27328         }
27329     },
27330
27331     /**
27332      * Hides this shadow
27333      */
27334     hide : function(){
27335         if(this.el){
27336             this.el.dom.style.display = "none";
27337             Roo.Shadow.Pool.push(this.el);
27338             delete this.el;
27339         }
27340     },
27341
27342     /**
27343      * Adjust the z-index of this shadow
27344      * @param {Number} zindex The new z-index
27345      */
27346     setZIndex : function(z){
27347         this.zIndex = z;
27348         if(this.el){
27349             this.el.setStyle("z-index", z);
27350         }
27351     }
27352 };
27353
27354 // Private utility class that manages the internal Shadow cache
27355 Roo.Shadow.Pool = function(){
27356     var p = [];
27357     var markup = Roo.isIE ?
27358                  '<div class="x-ie-shadow"></div>' :
27359                  '<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>';
27360     return {
27361         pull : function(){
27362             var sh = p.shift();
27363             if(!sh){
27364                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27365                 sh.autoBoxAdjust = false;
27366             }
27367             return sh;
27368         },
27369
27370         push : function(sh){
27371             p.push(sh);
27372         }
27373     };
27374 }();/*
27375  * Based on:
27376  * Ext JS Library 1.1.1
27377  * Copyright(c) 2006-2007, Ext JS, LLC.
27378  *
27379  * Originally Released Under LGPL - original licence link has changed is not relivant.
27380  *
27381  * Fork - LGPL
27382  * <script type="text/javascript">
27383  */
27384
27385
27386 /**
27387  * @class Roo.SplitBar
27388  * @extends Roo.util.Observable
27389  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27390  * <br><br>
27391  * Usage:
27392  * <pre><code>
27393 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27394                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27395 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27396 split.minSize = 100;
27397 split.maxSize = 600;
27398 split.animate = true;
27399 split.on('moved', splitterMoved);
27400 </code></pre>
27401  * @constructor
27402  * Create a new SplitBar
27403  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27404  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27405  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27406  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27407                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27408                         position of the SplitBar).
27409  */
27410 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27411     
27412     /** @private */
27413     this.el = Roo.get(dragElement, true);
27414     this.el.dom.unselectable = "on";
27415     /** @private */
27416     this.resizingEl = Roo.get(resizingElement, true);
27417
27418     /**
27419      * @private
27420      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27421      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27422      * @type Number
27423      */
27424     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27425     
27426     /**
27427      * The minimum size of the resizing element. (Defaults to 0)
27428      * @type Number
27429      */
27430     this.minSize = 0;
27431     
27432     /**
27433      * The maximum size of the resizing element. (Defaults to 2000)
27434      * @type Number
27435      */
27436     this.maxSize = 2000;
27437     
27438     /**
27439      * Whether to animate the transition to the new size
27440      * @type Boolean
27441      */
27442     this.animate = false;
27443     
27444     /**
27445      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27446      * @type Boolean
27447      */
27448     this.useShim = false;
27449     
27450     /** @private */
27451     this.shim = null;
27452     
27453     if(!existingProxy){
27454         /** @private */
27455         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27456     }else{
27457         this.proxy = Roo.get(existingProxy).dom;
27458     }
27459     /** @private */
27460     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27461     
27462     /** @private */
27463     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27464     
27465     /** @private */
27466     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27467     
27468     /** @private */
27469     this.dragSpecs = {};
27470     
27471     /**
27472      * @private The adapter to use to positon and resize elements
27473      */
27474     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27475     this.adapter.init(this);
27476     
27477     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27478         /** @private */
27479         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27480         this.el.addClass("x-splitbar-h");
27481     }else{
27482         /** @private */
27483         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27484         this.el.addClass("x-splitbar-v");
27485     }
27486     
27487     this.addEvents({
27488         /**
27489          * @event resize
27490          * Fires when the splitter is moved (alias for {@link #event-moved})
27491          * @param {Roo.SplitBar} this
27492          * @param {Number} newSize the new width or height
27493          */
27494         "resize" : true,
27495         /**
27496          * @event moved
27497          * Fires when the splitter is moved
27498          * @param {Roo.SplitBar} this
27499          * @param {Number} newSize the new width or height
27500          */
27501         "moved" : true,
27502         /**
27503          * @event beforeresize
27504          * Fires before the splitter is dragged
27505          * @param {Roo.SplitBar} this
27506          */
27507         "beforeresize" : true,
27508
27509         "beforeapply" : true
27510     });
27511
27512     Roo.util.Observable.call(this);
27513 };
27514
27515 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27516     onStartProxyDrag : function(x, y){
27517         this.fireEvent("beforeresize", this);
27518         if(!this.overlay){
27519             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27520             o.unselectable();
27521             o.enableDisplayMode("block");
27522             // all splitbars share the same overlay
27523             Roo.SplitBar.prototype.overlay = o;
27524         }
27525         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27526         this.overlay.show();
27527         Roo.get(this.proxy).setDisplayed("block");
27528         var size = this.adapter.getElementSize(this);
27529         this.activeMinSize = this.getMinimumSize();;
27530         this.activeMaxSize = this.getMaximumSize();;
27531         var c1 = size - this.activeMinSize;
27532         var c2 = Math.max(this.activeMaxSize - size, 0);
27533         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27534             this.dd.resetConstraints();
27535             this.dd.setXConstraint(
27536                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27537                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27538             );
27539             this.dd.setYConstraint(0, 0);
27540         }else{
27541             this.dd.resetConstraints();
27542             this.dd.setXConstraint(0, 0);
27543             this.dd.setYConstraint(
27544                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27545                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27546             );
27547          }
27548         this.dragSpecs.startSize = size;
27549         this.dragSpecs.startPoint = [x, y];
27550         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27551     },
27552     
27553     /** 
27554      * @private Called after the drag operation by the DDProxy
27555      */
27556     onEndProxyDrag : function(e){
27557         Roo.get(this.proxy).setDisplayed(false);
27558         var endPoint = Roo.lib.Event.getXY(e);
27559         if(this.overlay){
27560             this.overlay.hide();
27561         }
27562         var newSize;
27563         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27564             newSize = this.dragSpecs.startSize + 
27565                 (this.placement == Roo.SplitBar.LEFT ?
27566                     endPoint[0] - this.dragSpecs.startPoint[0] :
27567                     this.dragSpecs.startPoint[0] - endPoint[0]
27568                 );
27569         }else{
27570             newSize = this.dragSpecs.startSize + 
27571                 (this.placement == Roo.SplitBar.TOP ?
27572                     endPoint[1] - this.dragSpecs.startPoint[1] :
27573                     this.dragSpecs.startPoint[1] - endPoint[1]
27574                 );
27575         }
27576         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27577         if(newSize != this.dragSpecs.startSize){
27578             if(this.fireEvent('beforeapply', this, newSize) !== false){
27579                 this.adapter.setElementSize(this, newSize);
27580                 this.fireEvent("moved", this, newSize);
27581                 this.fireEvent("resize", this, newSize);
27582             }
27583         }
27584     },
27585     
27586     /**
27587      * Get the adapter this SplitBar uses
27588      * @return The adapter object
27589      */
27590     getAdapter : function(){
27591         return this.adapter;
27592     },
27593     
27594     /**
27595      * Set the adapter this SplitBar uses
27596      * @param {Object} adapter A SplitBar adapter object
27597      */
27598     setAdapter : function(adapter){
27599         this.adapter = adapter;
27600         this.adapter.init(this);
27601     },
27602     
27603     /**
27604      * Gets the minimum size for the resizing element
27605      * @return {Number} The minimum size
27606      */
27607     getMinimumSize : function(){
27608         return this.minSize;
27609     },
27610     
27611     /**
27612      * Sets the minimum size for the resizing element
27613      * @param {Number} minSize The minimum size
27614      */
27615     setMinimumSize : function(minSize){
27616         this.minSize = minSize;
27617     },
27618     
27619     /**
27620      * Gets the maximum size for the resizing element
27621      * @return {Number} The maximum size
27622      */
27623     getMaximumSize : function(){
27624         return this.maxSize;
27625     },
27626     
27627     /**
27628      * Sets the maximum size for the resizing element
27629      * @param {Number} maxSize The maximum size
27630      */
27631     setMaximumSize : function(maxSize){
27632         this.maxSize = maxSize;
27633     },
27634     
27635     /**
27636      * Sets the initialize size for the resizing element
27637      * @param {Number} size The initial size
27638      */
27639     setCurrentSize : function(size){
27640         var oldAnimate = this.animate;
27641         this.animate = false;
27642         this.adapter.setElementSize(this, size);
27643         this.animate = oldAnimate;
27644     },
27645     
27646     /**
27647      * Destroy this splitbar. 
27648      * @param {Boolean} removeEl True to remove the element
27649      */
27650     destroy : function(removeEl){
27651         if(this.shim){
27652             this.shim.remove();
27653         }
27654         this.dd.unreg();
27655         this.proxy.parentNode.removeChild(this.proxy);
27656         if(removeEl){
27657             this.el.remove();
27658         }
27659     }
27660 });
27661
27662 /**
27663  * @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.
27664  */
27665 Roo.SplitBar.createProxy = function(dir){
27666     var proxy = new Roo.Element(document.createElement("div"));
27667     proxy.unselectable();
27668     var cls = 'x-splitbar-proxy';
27669     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27670     document.body.appendChild(proxy.dom);
27671     return proxy.dom;
27672 };
27673
27674 /** 
27675  * @class Roo.SplitBar.BasicLayoutAdapter
27676  * Default Adapter. It assumes the splitter and resizing element are not positioned
27677  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27678  */
27679 Roo.SplitBar.BasicLayoutAdapter = function(){
27680 };
27681
27682 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27683     // do nothing for now
27684     init : function(s){
27685     
27686     },
27687     /**
27688      * Called before drag operations to get the current size of the resizing element. 
27689      * @param {Roo.SplitBar} s The SplitBar using this adapter
27690      */
27691      getElementSize : function(s){
27692         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27693             return s.resizingEl.getWidth();
27694         }else{
27695             return s.resizingEl.getHeight();
27696         }
27697     },
27698     
27699     /**
27700      * Called after drag operations to set the size of the resizing element.
27701      * @param {Roo.SplitBar} s The SplitBar using this adapter
27702      * @param {Number} newSize The new size to set
27703      * @param {Function} onComplete A function to be invoked when resizing is complete
27704      */
27705     setElementSize : function(s, newSize, onComplete){
27706         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27707             if(!s.animate){
27708                 s.resizingEl.setWidth(newSize);
27709                 if(onComplete){
27710                     onComplete(s, newSize);
27711                 }
27712             }else{
27713                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27714             }
27715         }else{
27716             
27717             if(!s.animate){
27718                 s.resizingEl.setHeight(newSize);
27719                 if(onComplete){
27720                     onComplete(s, newSize);
27721                 }
27722             }else{
27723                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27724             }
27725         }
27726     }
27727 };
27728
27729 /** 
27730  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27731  * @extends Roo.SplitBar.BasicLayoutAdapter
27732  * Adapter that  moves the splitter element to align with the resized sizing element. 
27733  * Used with an absolute positioned SplitBar.
27734  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27735  * document.body, make sure you assign an id to the body element.
27736  */
27737 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27738     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27739     this.container = Roo.get(container);
27740 };
27741
27742 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27743     init : function(s){
27744         this.basic.init(s);
27745     },
27746     
27747     getElementSize : function(s){
27748         return this.basic.getElementSize(s);
27749     },
27750     
27751     setElementSize : function(s, newSize, onComplete){
27752         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27753     },
27754     
27755     moveSplitter : function(s){
27756         var yes = Roo.SplitBar;
27757         switch(s.placement){
27758             case yes.LEFT:
27759                 s.el.setX(s.resizingEl.getRight());
27760                 break;
27761             case yes.RIGHT:
27762                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27763                 break;
27764             case yes.TOP:
27765                 s.el.setY(s.resizingEl.getBottom());
27766                 break;
27767             case yes.BOTTOM:
27768                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27769                 break;
27770         }
27771     }
27772 };
27773
27774 /**
27775  * Orientation constant - Create a vertical SplitBar
27776  * @static
27777  * @type Number
27778  */
27779 Roo.SplitBar.VERTICAL = 1;
27780
27781 /**
27782  * Orientation constant - Create a horizontal SplitBar
27783  * @static
27784  * @type Number
27785  */
27786 Roo.SplitBar.HORIZONTAL = 2;
27787
27788 /**
27789  * Placement constant - The resizing element is to the left of the splitter element
27790  * @static
27791  * @type Number
27792  */
27793 Roo.SplitBar.LEFT = 1;
27794
27795 /**
27796  * Placement constant - The resizing element is to the right of the splitter element
27797  * @static
27798  * @type Number
27799  */
27800 Roo.SplitBar.RIGHT = 2;
27801
27802 /**
27803  * Placement constant - The resizing element is positioned above the splitter element
27804  * @static
27805  * @type Number
27806  */
27807 Roo.SplitBar.TOP = 3;
27808
27809 /**
27810  * Placement constant - The resizing element is positioned under splitter element
27811  * @static
27812  * @type Number
27813  */
27814 Roo.SplitBar.BOTTOM = 4;
27815 /*
27816  * Based on:
27817  * Ext JS Library 1.1.1
27818  * Copyright(c) 2006-2007, Ext JS, LLC.
27819  *
27820  * Originally Released Under LGPL - original licence link has changed is not relivant.
27821  *
27822  * Fork - LGPL
27823  * <script type="text/javascript">
27824  */
27825
27826 /**
27827  * @class Roo.View
27828  * @extends Roo.util.Observable
27829  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27830  * This class also supports single and multi selection modes. <br>
27831  * Create a data model bound view:
27832  <pre><code>
27833  var store = new Roo.data.Store(...);
27834
27835  var view = new Roo.View({
27836     el : "my-element",
27837     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27838  
27839     singleSelect: true,
27840     selectedClass: "ydataview-selected",
27841     store: store
27842  });
27843
27844  // listen for node click?
27845  view.on("click", function(vw, index, node, e){
27846  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27847  });
27848
27849  // load XML data
27850  dataModel.load("foobar.xml");
27851  </code></pre>
27852  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27853  * <br><br>
27854  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27855  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27856  * 
27857  * Note: old style constructor is still suported (container, template, config)
27858  * 
27859  * @constructor
27860  * Create a new View
27861  * @param {Object} config The config object
27862  * 
27863  */
27864 Roo.View = function(config, depreciated_tpl, depreciated_config){
27865     
27866     this.parent = false;
27867     
27868     if (typeof(depreciated_tpl) == 'undefined') {
27869         // new way.. - universal constructor.
27870         Roo.apply(this, config);
27871         this.el  = Roo.get(this.el);
27872     } else {
27873         // old format..
27874         this.el  = Roo.get(config);
27875         this.tpl = depreciated_tpl;
27876         Roo.apply(this, depreciated_config);
27877     }
27878     this.wrapEl  = this.el.wrap().wrap();
27879     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27880     
27881     
27882     if(typeof(this.tpl) == "string"){
27883         this.tpl = new Roo.Template(this.tpl);
27884     } else {
27885         // support xtype ctors..
27886         this.tpl = new Roo.factory(this.tpl, Roo);
27887     }
27888     
27889     
27890     this.tpl.compile();
27891     
27892     /** @private */
27893     this.addEvents({
27894         /**
27895          * @event beforeclick
27896          * Fires before a click is processed. Returns false to cancel the default action.
27897          * @param {Roo.View} this
27898          * @param {Number} index The index of the target node
27899          * @param {HTMLElement} node The target node
27900          * @param {Roo.EventObject} e The raw event object
27901          */
27902             "beforeclick" : true,
27903         /**
27904          * @event click
27905          * Fires when a template node is clicked.
27906          * @param {Roo.View} this
27907          * @param {Number} index The index of the target node
27908          * @param {HTMLElement} node The target node
27909          * @param {Roo.EventObject} e The raw event object
27910          */
27911             "click" : true,
27912         /**
27913          * @event dblclick
27914          * Fires when a template node is double clicked.
27915          * @param {Roo.View} this
27916          * @param {Number} index The index of the target node
27917          * @param {HTMLElement} node The target node
27918          * @param {Roo.EventObject} e The raw event object
27919          */
27920             "dblclick" : true,
27921         /**
27922          * @event contextmenu
27923          * Fires when a template node is right clicked.
27924          * @param {Roo.View} this
27925          * @param {Number} index The index of the target node
27926          * @param {HTMLElement} node The target node
27927          * @param {Roo.EventObject} e The raw event object
27928          */
27929             "contextmenu" : true,
27930         /**
27931          * @event selectionchange
27932          * Fires when the selected nodes change.
27933          * @param {Roo.View} this
27934          * @param {Array} selections Array of the selected nodes
27935          */
27936             "selectionchange" : true,
27937     
27938         /**
27939          * @event beforeselect
27940          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27941          * @param {Roo.View} this
27942          * @param {HTMLElement} node The node to be selected
27943          * @param {Array} selections Array of currently selected nodes
27944          */
27945             "beforeselect" : true,
27946         /**
27947          * @event preparedata
27948          * Fires on every row to render, to allow you to change the data.
27949          * @param {Roo.View} this
27950          * @param {Object} data to be rendered (change this)
27951          */
27952           "preparedata" : true
27953           
27954           
27955         });
27956
27957
27958
27959     this.el.on({
27960         "click": this.onClick,
27961         "dblclick": this.onDblClick,
27962         "contextmenu": this.onContextMenu,
27963         scope:this
27964     });
27965
27966     this.selections = [];
27967     this.nodes = [];
27968     this.cmp = new Roo.CompositeElementLite([]);
27969     if(this.store){
27970         this.store = Roo.factory(this.store, Roo.data);
27971         this.setStore(this.store, true);
27972     }
27973     
27974     if ( this.footer && this.footer.xtype) {
27975            
27976          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27977         
27978         this.footer.dataSource = this.store;
27979         this.footer.container = fctr;
27980         this.footer = Roo.factory(this.footer, Roo);
27981         fctr.insertFirst(this.el);
27982         
27983         // this is a bit insane - as the paging toolbar seems to detach the el..
27984 //        dom.parentNode.parentNode.parentNode
27985          // they get detached?
27986     }
27987     
27988     
27989     Roo.View.superclass.constructor.call(this);
27990     
27991     
27992 };
27993
27994 Roo.extend(Roo.View, Roo.util.Observable, {
27995     
27996      /**
27997      * @cfg {Roo.data.Store} store Data store to load data from.
27998      */
27999     store : false,
28000     
28001     /**
28002      * @cfg {String|Roo.Element} el The container element.
28003      */
28004     el : '',
28005     
28006     /**
28007      * @cfg {String|Roo.Template} tpl The template used by this View 
28008      */
28009     tpl : false,
28010     /**
28011      * @cfg {String} dataName the named area of the template to use as the data area
28012      *                          Works with domtemplates roo-name="name"
28013      */
28014     dataName: false,
28015     /**
28016      * @cfg {String} selectedClass The css class to add to selected nodes
28017      */
28018     selectedClass : "x-view-selected",
28019      /**
28020      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28021      */
28022     emptyText : "",
28023     
28024     /**
28025      * @cfg {String} text to display on mask (default Loading)
28026      */
28027     mask : false,
28028     /**
28029      * @cfg {Boolean} multiSelect Allow multiple selection
28030      */
28031     multiSelect : false,
28032     /**
28033      * @cfg {Boolean} singleSelect Allow single selection
28034      */
28035     singleSelect:  false,
28036     
28037     /**
28038      * @cfg {Boolean} toggleSelect - selecting 
28039      */
28040     toggleSelect : false,
28041     
28042     /**
28043      * @cfg {Boolean} tickable - selecting 
28044      */
28045     tickable : false,
28046     
28047     /**
28048      * Returns the element this view is bound to.
28049      * @return {Roo.Element}
28050      */
28051     getEl : function(){
28052         return this.wrapEl;
28053     },
28054     
28055     
28056
28057     /**
28058      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28059      */
28060     refresh : function(){
28061         //Roo.log('refresh');
28062         var t = this.tpl;
28063         
28064         // if we are using something like 'domtemplate', then
28065         // the what gets used is:
28066         // t.applySubtemplate(NAME, data, wrapping data..)
28067         // the outer template then get' applied with
28068         //     the store 'extra data'
28069         // and the body get's added to the
28070         //      roo-name="data" node?
28071         //      <span class='roo-tpl-{name}'></span> ?????
28072         
28073         
28074         
28075         this.clearSelections();
28076         this.el.update("");
28077         var html = [];
28078         var records = this.store.getRange();
28079         if(records.length < 1) {
28080             
28081             // is this valid??  = should it render a template??
28082             
28083             this.el.update(this.emptyText);
28084             return;
28085         }
28086         var el = this.el;
28087         if (this.dataName) {
28088             this.el.update(t.apply(this.store.meta)); //????
28089             el = this.el.child('.roo-tpl-' + this.dataName);
28090         }
28091         
28092         for(var i = 0, len = records.length; i < len; i++){
28093             var data = this.prepareData(records[i].data, i, records[i]);
28094             this.fireEvent("preparedata", this, data, i, records[i]);
28095             
28096             var d = Roo.apply({}, data);
28097             
28098             if(this.tickable){
28099                 Roo.apply(d, {'roo-id' : Roo.id()});
28100                 
28101                 var _this = this;
28102             
28103                 Roo.each(this.parent.item, function(item){
28104                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28105                         return;
28106                     }
28107                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28108                 });
28109             }
28110             
28111             html[html.length] = Roo.util.Format.trim(
28112                 this.dataName ?
28113                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28114                     t.apply(d)
28115             );
28116         }
28117         
28118         
28119         
28120         el.update(html.join(""));
28121         this.nodes = el.dom.childNodes;
28122         this.updateIndexes(0);
28123     },
28124     
28125
28126     /**
28127      * Function to override to reformat the data that is sent to
28128      * the template for each node.
28129      * DEPRICATED - use the preparedata event handler.
28130      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28131      * a JSON object for an UpdateManager bound view).
28132      */
28133     prepareData : function(data, index, record)
28134     {
28135         this.fireEvent("preparedata", this, data, index, record);
28136         return data;
28137     },
28138
28139     onUpdate : function(ds, record){
28140         // Roo.log('on update');   
28141         this.clearSelections();
28142         var index = this.store.indexOf(record);
28143         var n = this.nodes[index];
28144         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28145         n.parentNode.removeChild(n);
28146         this.updateIndexes(index, index);
28147     },
28148
28149     
28150     
28151 // --------- FIXME     
28152     onAdd : function(ds, records, index)
28153     {
28154         //Roo.log(['on Add', ds, records, index] );        
28155         this.clearSelections();
28156         if(this.nodes.length == 0){
28157             this.refresh();
28158             return;
28159         }
28160         var n = this.nodes[index];
28161         for(var i = 0, len = records.length; i < len; i++){
28162             var d = this.prepareData(records[i].data, i, records[i]);
28163             if(n){
28164                 this.tpl.insertBefore(n, d);
28165             }else{
28166                 
28167                 this.tpl.append(this.el, d);
28168             }
28169         }
28170         this.updateIndexes(index);
28171     },
28172
28173     onRemove : function(ds, record, index){
28174        // Roo.log('onRemove');
28175         this.clearSelections();
28176         var el = this.dataName  ?
28177             this.el.child('.roo-tpl-' + this.dataName) :
28178             this.el; 
28179         
28180         el.dom.removeChild(this.nodes[index]);
28181         this.updateIndexes(index);
28182     },
28183
28184     /**
28185      * Refresh an individual node.
28186      * @param {Number} index
28187      */
28188     refreshNode : function(index){
28189         this.onUpdate(this.store, this.store.getAt(index));
28190     },
28191
28192     updateIndexes : function(startIndex, endIndex){
28193         var ns = this.nodes;
28194         startIndex = startIndex || 0;
28195         endIndex = endIndex || ns.length - 1;
28196         for(var i = startIndex; i <= endIndex; i++){
28197             ns[i].nodeIndex = i;
28198         }
28199     },
28200
28201     /**
28202      * Changes the data store this view uses and refresh the view.
28203      * @param {Store} store
28204      */
28205     setStore : function(store, initial){
28206         if(!initial && this.store){
28207             this.store.un("datachanged", this.refresh);
28208             this.store.un("add", this.onAdd);
28209             this.store.un("remove", this.onRemove);
28210             this.store.un("update", this.onUpdate);
28211             this.store.un("clear", this.refresh);
28212             this.store.un("beforeload", this.onBeforeLoad);
28213             this.store.un("load", this.onLoad);
28214             this.store.un("loadexception", this.onLoad);
28215         }
28216         if(store){
28217           
28218             store.on("datachanged", this.refresh, this);
28219             store.on("add", this.onAdd, this);
28220             store.on("remove", this.onRemove, this);
28221             store.on("update", this.onUpdate, this);
28222             store.on("clear", this.refresh, this);
28223             store.on("beforeload", this.onBeforeLoad, this);
28224             store.on("load", this.onLoad, this);
28225             store.on("loadexception", this.onLoad, this);
28226         }
28227         
28228         if(store){
28229             this.refresh();
28230         }
28231     },
28232     /**
28233      * onbeforeLoad - masks the loading area.
28234      *
28235      */
28236     onBeforeLoad : function(store,opts)
28237     {
28238          //Roo.log('onBeforeLoad');   
28239         if (!opts.add) {
28240             this.el.update("");
28241         }
28242         this.el.mask(this.mask ? this.mask : "Loading" ); 
28243     },
28244     onLoad : function ()
28245     {
28246         this.el.unmask();
28247     },
28248     
28249
28250     /**
28251      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28252      * @param {HTMLElement} node
28253      * @return {HTMLElement} The template node
28254      */
28255     findItemFromChild : function(node){
28256         var el = this.dataName  ?
28257             this.el.child('.roo-tpl-' + this.dataName,true) :
28258             this.el.dom; 
28259         
28260         if(!node || node.parentNode == el){
28261                     return node;
28262             }
28263             var p = node.parentNode;
28264             while(p && p != el){
28265             if(p.parentNode == el){
28266                 return p;
28267             }
28268             p = p.parentNode;
28269         }
28270             return null;
28271     },
28272
28273     /** @ignore */
28274     onClick : function(e){
28275         var item = this.findItemFromChild(e.getTarget());
28276         if(item){
28277             var index = this.indexOf(item);
28278             if(this.onItemClick(item, index, e) !== false){
28279                 this.fireEvent("click", this, index, item, e);
28280             }
28281         }else{
28282             this.clearSelections();
28283         }
28284     },
28285
28286     /** @ignore */
28287     onContextMenu : function(e){
28288         var item = this.findItemFromChild(e.getTarget());
28289         if(item){
28290             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28291         }
28292     },
28293
28294     /** @ignore */
28295     onDblClick : function(e){
28296         var item = this.findItemFromChild(e.getTarget());
28297         if(item){
28298             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28299         }
28300     },
28301
28302     onItemClick : function(item, index, e)
28303     {
28304         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28305             return false;
28306         }
28307         if (this.toggleSelect) {
28308             var m = this.isSelected(item) ? 'unselect' : 'select';
28309             //Roo.log(m);
28310             var _t = this;
28311             _t[m](item, true, false);
28312             return true;
28313         }
28314         if(this.multiSelect || this.singleSelect){
28315             if(this.multiSelect && e.shiftKey && this.lastSelection){
28316                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28317             }else{
28318                 this.select(item, this.multiSelect && e.ctrlKey);
28319                 this.lastSelection = item;
28320             }
28321             
28322             if(!this.tickable){
28323                 e.preventDefault();
28324             }
28325             
28326         }
28327         return true;
28328     },
28329
28330     /**
28331      * Get the number of selected nodes.
28332      * @return {Number}
28333      */
28334     getSelectionCount : function(){
28335         return this.selections.length;
28336     },
28337
28338     /**
28339      * Get the currently selected nodes.
28340      * @return {Array} An array of HTMLElements
28341      */
28342     getSelectedNodes : function(){
28343         return this.selections;
28344     },
28345
28346     /**
28347      * Get the indexes of the selected nodes.
28348      * @return {Array}
28349      */
28350     getSelectedIndexes : function(){
28351         var indexes = [], s = this.selections;
28352         for(var i = 0, len = s.length; i < len; i++){
28353             indexes.push(s[i].nodeIndex);
28354         }
28355         return indexes;
28356     },
28357
28358     /**
28359      * Clear all selections
28360      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28361      */
28362     clearSelections : function(suppressEvent){
28363         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28364             this.cmp.elements = this.selections;
28365             this.cmp.removeClass(this.selectedClass);
28366             this.selections = [];
28367             if(!suppressEvent){
28368                 this.fireEvent("selectionchange", this, this.selections);
28369             }
28370         }
28371     },
28372
28373     /**
28374      * Returns true if the passed node is selected
28375      * @param {HTMLElement/Number} node The node or node index
28376      * @return {Boolean}
28377      */
28378     isSelected : function(node){
28379         var s = this.selections;
28380         if(s.length < 1){
28381             return false;
28382         }
28383         node = this.getNode(node);
28384         return s.indexOf(node) !== -1;
28385     },
28386
28387     /**
28388      * Selects nodes.
28389      * @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
28390      * @param {Boolean} keepExisting (optional) true to keep existing selections
28391      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28392      */
28393     select : function(nodeInfo, keepExisting, suppressEvent){
28394         if(nodeInfo instanceof Array){
28395             if(!keepExisting){
28396                 this.clearSelections(true);
28397             }
28398             for(var i = 0, len = nodeInfo.length; i < len; i++){
28399                 this.select(nodeInfo[i], true, true);
28400             }
28401             return;
28402         } 
28403         var node = this.getNode(nodeInfo);
28404         if(!node || this.isSelected(node)){
28405             return; // already selected.
28406         }
28407         if(!keepExisting){
28408             this.clearSelections(true);
28409         }
28410         
28411         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28412             Roo.fly(node).addClass(this.selectedClass);
28413             this.selections.push(node);
28414             if(!suppressEvent){
28415                 this.fireEvent("selectionchange", this, this.selections);
28416             }
28417         }
28418         
28419         
28420     },
28421       /**
28422      * Unselects nodes.
28423      * @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
28424      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28425      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28426      */
28427     unselect : function(nodeInfo, keepExisting, suppressEvent)
28428     {
28429         if(nodeInfo instanceof Array){
28430             Roo.each(this.selections, function(s) {
28431                 this.unselect(s, nodeInfo);
28432             }, this);
28433             return;
28434         }
28435         var node = this.getNode(nodeInfo);
28436         if(!node || !this.isSelected(node)){
28437             //Roo.log("not selected");
28438             return; // not selected.
28439         }
28440         // fireevent???
28441         var ns = [];
28442         Roo.each(this.selections, function(s) {
28443             if (s == node ) {
28444                 Roo.fly(node).removeClass(this.selectedClass);
28445
28446                 return;
28447             }
28448             ns.push(s);
28449         },this);
28450         
28451         this.selections= ns;
28452         this.fireEvent("selectionchange", this, this.selections);
28453     },
28454
28455     /**
28456      * Gets a template node.
28457      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28458      * @return {HTMLElement} The node or null if it wasn't found
28459      */
28460     getNode : function(nodeInfo){
28461         if(typeof nodeInfo == "string"){
28462             return document.getElementById(nodeInfo);
28463         }else if(typeof nodeInfo == "number"){
28464             return this.nodes[nodeInfo];
28465         }
28466         return nodeInfo;
28467     },
28468
28469     /**
28470      * Gets a range template nodes.
28471      * @param {Number} startIndex
28472      * @param {Number} endIndex
28473      * @return {Array} An array of nodes
28474      */
28475     getNodes : function(start, end){
28476         var ns = this.nodes;
28477         start = start || 0;
28478         end = typeof end == "undefined" ? ns.length - 1 : end;
28479         var nodes = [];
28480         if(start <= end){
28481             for(var i = start; i <= end; i++){
28482                 nodes.push(ns[i]);
28483             }
28484         } else{
28485             for(var i = start; i >= end; i--){
28486                 nodes.push(ns[i]);
28487             }
28488         }
28489         return nodes;
28490     },
28491
28492     /**
28493      * Finds the index of the passed node
28494      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28495      * @return {Number} The index of the node or -1
28496      */
28497     indexOf : function(node){
28498         node = this.getNode(node);
28499         if(typeof node.nodeIndex == "number"){
28500             return node.nodeIndex;
28501         }
28502         var ns = this.nodes;
28503         for(var i = 0, len = ns.length; i < len; i++){
28504             if(ns[i] == node){
28505                 return i;
28506             }
28507         }
28508         return -1;
28509     }
28510 });
28511 /*
28512  * Based on:
28513  * Ext JS Library 1.1.1
28514  * Copyright(c) 2006-2007, Ext JS, LLC.
28515  *
28516  * Originally Released Under LGPL - original licence link has changed is not relivant.
28517  *
28518  * Fork - LGPL
28519  * <script type="text/javascript">
28520  */
28521
28522 /**
28523  * @class Roo.JsonView
28524  * @extends Roo.View
28525  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28526 <pre><code>
28527 var view = new Roo.JsonView({
28528     container: "my-element",
28529     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28530     multiSelect: true, 
28531     jsonRoot: "data" 
28532 });
28533
28534 // listen for node click?
28535 view.on("click", function(vw, index, node, e){
28536     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28537 });
28538
28539 // direct load of JSON data
28540 view.load("foobar.php");
28541
28542 // Example from my blog list
28543 var tpl = new Roo.Template(
28544     '&lt;div class="entry"&gt;' +
28545     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28546     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28547     "&lt;/div&gt;&lt;hr /&gt;"
28548 );
28549
28550 var moreView = new Roo.JsonView({
28551     container :  "entry-list", 
28552     template : tpl,
28553     jsonRoot: "posts"
28554 });
28555 moreView.on("beforerender", this.sortEntries, this);
28556 moreView.load({
28557     url: "/blog/get-posts.php",
28558     params: "allposts=true",
28559     text: "Loading Blog Entries..."
28560 });
28561 </code></pre>
28562
28563 * Note: old code is supported with arguments : (container, template, config)
28564
28565
28566  * @constructor
28567  * Create a new JsonView
28568  * 
28569  * @param {Object} config The config object
28570  * 
28571  */
28572 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28573     
28574     
28575     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28576
28577     var um = this.el.getUpdateManager();
28578     um.setRenderer(this);
28579     um.on("update", this.onLoad, this);
28580     um.on("failure", this.onLoadException, this);
28581
28582     /**
28583      * @event beforerender
28584      * Fires before rendering of the downloaded JSON data.
28585      * @param {Roo.JsonView} this
28586      * @param {Object} data The JSON data loaded
28587      */
28588     /**
28589      * @event load
28590      * Fires when data is loaded.
28591      * @param {Roo.JsonView} this
28592      * @param {Object} data The JSON data loaded
28593      * @param {Object} response The raw Connect response object
28594      */
28595     /**
28596      * @event loadexception
28597      * Fires when loading fails.
28598      * @param {Roo.JsonView} this
28599      * @param {Object} response The raw Connect response object
28600      */
28601     this.addEvents({
28602         'beforerender' : true,
28603         'load' : true,
28604         'loadexception' : true
28605     });
28606 };
28607 Roo.extend(Roo.JsonView, Roo.View, {
28608     /**
28609      * @type {String} The root property in the loaded JSON object that contains the data
28610      */
28611     jsonRoot : "",
28612
28613     /**
28614      * Refreshes the view.
28615      */
28616     refresh : function(){
28617         this.clearSelections();
28618         this.el.update("");
28619         var html = [];
28620         var o = this.jsonData;
28621         if(o && o.length > 0){
28622             for(var i = 0, len = o.length; i < len; i++){
28623                 var data = this.prepareData(o[i], i, o);
28624                 html[html.length] = this.tpl.apply(data);
28625             }
28626         }else{
28627             html.push(this.emptyText);
28628         }
28629         this.el.update(html.join(""));
28630         this.nodes = this.el.dom.childNodes;
28631         this.updateIndexes(0);
28632     },
28633
28634     /**
28635      * 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.
28636      * @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:
28637      <pre><code>
28638      view.load({
28639          url: "your-url.php",
28640          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28641          callback: yourFunction,
28642          scope: yourObject, //(optional scope)
28643          discardUrl: false,
28644          nocache: false,
28645          text: "Loading...",
28646          timeout: 30,
28647          scripts: false
28648      });
28649      </code></pre>
28650      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28651      * 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.
28652      * @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}
28653      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28654      * @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.
28655      */
28656     load : function(){
28657         var um = this.el.getUpdateManager();
28658         um.update.apply(um, arguments);
28659     },
28660
28661     // note - render is a standard framework call...
28662     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28663     render : function(el, response){
28664         
28665         this.clearSelections();
28666         this.el.update("");
28667         var o;
28668         try{
28669             if (response != '') {
28670                 o = Roo.util.JSON.decode(response.responseText);
28671                 if(this.jsonRoot){
28672                     
28673                     o = o[this.jsonRoot];
28674                 }
28675             }
28676         } catch(e){
28677         }
28678         /**
28679          * The current JSON data or null
28680          */
28681         this.jsonData = o;
28682         this.beforeRender();
28683         this.refresh();
28684     },
28685
28686 /**
28687  * Get the number of records in the current JSON dataset
28688  * @return {Number}
28689  */
28690     getCount : function(){
28691         return this.jsonData ? this.jsonData.length : 0;
28692     },
28693
28694 /**
28695  * Returns the JSON object for the specified node(s)
28696  * @param {HTMLElement/Array} node The node or an array of nodes
28697  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28698  * you get the JSON object for the node
28699  */
28700     getNodeData : function(node){
28701         if(node instanceof Array){
28702             var data = [];
28703             for(var i = 0, len = node.length; i < len; i++){
28704                 data.push(this.getNodeData(node[i]));
28705             }
28706             return data;
28707         }
28708         return this.jsonData[this.indexOf(node)] || null;
28709     },
28710
28711     beforeRender : function(){
28712         this.snapshot = this.jsonData;
28713         if(this.sortInfo){
28714             this.sort.apply(this, this.sortInfo);
28715         }
28716         this.fireEvent("beforerender", this, this.jsonData);
28717     },
28718
28719     onLoad : function(el, o){
28720         this.fireEvent("load", this, this.jsonData, o);
28721     },
28722
28723     onLoadException : function(el, o){
28724         this.fireEvent("loadexception", this, o);
28725     },
28726
28727 /**
28728  * Filter the data by a specific property.
28729  * @param {String} property A property on your JSON objects
28730  * @param {String/RegExp} value Either string that the property values
28731  * should start with, or a RegExp to test against the property
28732  */
28733     filter : function(property, value){
28734         if(this.jsonData){
28735             var data = [];
28736             var ss = this.snapshot;
28737             if(typeof value == "string"){
28738                 var vlen = value.length;
28739                 if(vlen == 0){
28740                     this.clearFilter();
28741                     return;
28742                 }
28743                 value = value.toLowerCase();
28744                 for(var i = 0, len = ss.length; i < len; i++){
28745                     var o = ss[i];
28746                     if(o[property].substr(0, vlen).toLowerCase() == value){
28747                         data.push(o);
28748                     }
28749                 }
28750             } else if(value.exec){ // regex?
28751                 for(var i = 0, len = ss.length; i < len; i++){
28752                     var o = ss[i];
28753                     if(value.test(o[property])){
28754                         data.push(o);
28755                     }
28756                 }
28757             } else{
28758                 return;
28759             }
28760             this.jsonData = data;
28761             this.refresh();
28762         }
28763     },
28764
28765 /**
28766  * Filter by a function. The passed function will be called with each
28767  * object in the current dataset. If the function returns true the value is kept,
28768  * otherwise it is filtered.
28769  * @param {Function} fn
28770  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28771  */
28772     filterBy : function(fn, scope){
28773         if(this.jsonData){
28774             var data = [];
28775             var ss = this.snapshot;
28776             for(var i = 0, len = ss.length; i < len; i++){
28777                 var o = ss[i];
28778                 if(fn.call(scope || this, o)){
28779                     data.push(o);
28780                 }
28781             }
28782             this.jsonData = data;
28783             this.refresh();
28784         }
28785     },
28786
28787 /**
28788  * Clears the current filter.
28789  */
28790     clearFilter : function(){
28791         if(this.snapshot && this.jsonData != this.snapshot){
28792             this.jsonData = this.snapshot;
28793             this.refresh();
28794         }
28795     },
28796
28797
28798 /**
28799  * Sorts the data for this view and refreshes it.
28800  * @param {String} property A property on your JSON objects to sort on
28801  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28802  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28803  */
28804     sort : function(property, dir, sortType){
28805         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28806         if(this.jsonData){
28807             var p = property;
28808             var dsc = dir && dir.toLowerCase() == "desc";
28809             var f = function(o1, o2){
28810                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28811                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28812                 ;
28813                 if(v1 < v2){
28814                     return dsc ? +1 : -1;
28815                 } else if(v1 > v2){
28816                     return dsc ? -1 : +1;
28817                 } else{
28818                     return 0;
28819                 }
28820             };
28821             this.jsonData.sort(f);
28822             this.refresh();
28823             if(this.jsonData != this.snapshot){
28824                 this.snapshot.sort(f);
28825             }
28826         }
28827     }
28828 });/*
28829  * Based on:
28830  * Ext JS Library 1.1.1
28831  * Copyright(c) 2006-2007, Ext JS, LLC.
28832  *
28833  * Originally Released Under LGPL - original licence link has changed is not relivant.
28834  *
28835  * Fork - LGPL
28836  * <script type="text/javascript">
28837  */
28838  
28839
28840 /**
28841  * @class Roo.ColorPalette
28842  * @extends Roo.Component
28843  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28844  * Here's an example of typical usage:
28845  * <pre><code>
28846 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28847 cp.render('my-div');
28848
28849 cp.on('select', function(palette, selColor){
28850     // do something with selColor
28851 });
28852 </code></pre>
28853  * @constructor
28854  * Create a new ColorPalette
28855  * @param {Object} config The config object
28856  */
28857 Roo.ColorPalette = function(config){
28858     Roo.ColorPalette.superclass.constructor.call(this, config);
28859     this.addEvents({
28860         /**
28861              * @event select
28862              * Fires when a color is selected
28863              * @param {ColorPalette} this
28864              * @param {String} color The 6-digit color hex code (without the # symbol)
28865              */
28866         select: true
28867     });
28868
28869     if(this.handler){
28870         this.on("select", this.handler, this.scope, true);
28871     }
28872 };
28873 Roo.extend(Roo.ColorPalette, Roo.Component, {
28874     /**
28875      * @cfg {String} itemCls
28876      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28877      */
28878     itemCls : "x-color-palette",
28879     /**
28880      * @cfg {String} value
28881      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28882      * the hex codes are case-sensitive.
28883      */
28884     value : null,
28885     clickEvent:'click',
28886     // private
28887     ctype: "Roo.ColorPalette",
28888
28889     /**
28890      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28891      */
28892     allowReselect : false,
28893
28894     /**
28895      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28896      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28897      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28898      * of colors with the width setting until the box is symmetrical.</p>
28899      * <p>You can override individual colors if needed:</p>
28900      * <pre><code>
28901 var cp = new Roo.ColorPalette();
28902 cp.colors[0] = "FF0000";  // change the first box to red
28903 </code></pre>
28904
28905 Or you can provide a custom array of your own for complete control:
28906 <pre><code>
28907 var cp = new Roo.ColorPalette();
28908 cp.colors = ["000000", "993300", "333300"];
28909 </code></pre>
28910      * @type Array
28911      */
28912     colors : [
28913         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28914         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28915         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28916         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28917         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28918     ],
28919
28920     // private
28921     onRender : function(container, position){
28922         var t = new Roo.MasterTemplate(
28923             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28924         );
28925         var c = this.colors;
28926         for(var i = 0, len = c.length; i < len; i++){
28927             t.add([c[i]]);
28928         }
28929         var el = document.createElement("div");
28930         el.className = this.itemCls;
28931         t.overwrite(el);
28932         container.dom.insertBefore(el, position);
28933         this.el = Roo.get(el);
28934         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28935         if(this.clickEvent != 'click'){
28936             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28937         }
28938     },
28939
28940     // private
28941     afterRender : function(){
28942         Roo.ColorPalette.superclass.afterRender.call(this);
28943         if(this.value){
28944             var s = this.value;
28945             this.value = null;
28946             this.select(s);
28947         }
28948     },
28949
28950     // private
28951     handleClick : function(e, t){
28952         e.preventDefault();
28953         if(!this.disabled){
28954             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28955             this.select(c.toUpperCase());
28956         }
28957     },
28958
28959     /**
28960      * Selects the specified color in the palette (fires the select event)
28961      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28962      */
28963     select : function(color){
28964         color = color.replace("#", "");
28965         if(color != this.value || this.allowReselect){
28966             var el = this.el;
28967             if(this.value){
28968                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28969             }
28970             el.child("a.color-"+color).addClass("x-color-palette-sel");
28971             this.value = color;
28972             this.fireEvent("select", this, color);
28973         }
28974     }
28975 });/*
28976  * Based on:
28977  * Ext JS Library 1.1.1
28978  * Copyright(c) 2006-2007, Ext JS, LLC.
28979  *
28980  * Originally Released Under LGPL - original licence link has changed is not relivant.
28981  *
28982  * Fork - LGPL
28983  * <script type="text/javascript">
28984  */
28985  
28986 /**
28987  * @class Roo.DatePicker
28988  * @extends Roo.Component
28989  * Simple date picker class.
28990  * @constructor
28991  * Create a new DatePicker
28992  * @param {Object} config The config object
28993  */
28994 Roo.DatePicker = function(config){
28995     Roo.DatePicker.superclass.constructor.call(this, config);
28996
28997     this.value = config && config.value ?
28998                  config.value.clearTime() : new Date().clearTime();
28999
29000     this.addEvents({
29001         /**
29002              * @event select
29003              * Fires when a date is selected
29004              * @param {DatePicker} this
29005              * @param {Date} date The selected date
29006              */
29007         'select': true,
29008         /**
29009              * @event monthchange
29010              * Fires when the displayed month changes 
29011              * @param {DatePicker} this
29012              * @param {Date} date The selected month
29013              */
29014         'monthchange': true
29015     });
29016
29017     if(this.handler){
29018         this.on("select", this.handler,  this.scope || this);
29019     }
29020     // build the disabledDatesRE
29021     if(!this.disabledDatesRE && this.disabledDates){
29022         var dd = this.disabledDates;
29023         var re = "(?:";
29024         for(var i = 0; i < dd.length; i++){
29025             re += dd[i];
29026             if(i != dd.length-1) {
29027                 re += "|";
29028             }
29029         }
29030         this.disabledDatesRE = new RegExp(re + ")");
29031     }
29032 };
29033
29034 Roo.extend(Roo.DatePicker, Roo.Component, {
29035     /**
29036      * @cfg {String} todayText
29037      * The text to display on the button that selects the current date (defaults to "Today")
29038      */
29039     todayText : "Today",
29040     /**
29041      * @cfg {String} okText
29042      * The text to display on the ok button
29043      */
29044     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29045     /**
29046      * @cfg {String} cancelText
29047      * The text to display on the cancel button
29048      */
29049     cancelText : "Cancel",
29050     /**
29051      * @cfg {String} todayTip
29052      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29053      */
29054     todayTip : "{0} (Spacebar)",
29055     /**
29056      * @cfg {Date} minDate
29057      * Minimum allowable date (JavaScript date object, defaults to null)
29058      */
29059     minDate : null,
29060     /**
29061      * @cfg {Date} maxDate
29062      * Maximum allowable date (JavaScript date object, defaults to null)
29063      */
29064     maxDate : null,
29065     /**
29066      * @cfg {String} minText
29067      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29068      */
29069     minText : "This date is before the minimum date",
29070     /**
29071      * @cfg {String} maxText
29072      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29073      */
29074     maxText : "This date is after the maximum date",
29075     /**
29076      * @cfg {String} format
29077      * The default date format string which can be overriden for localization support.  The format must be
29078      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29079      */
29080     format : "m/d/y",
29081     /**
29082      * @cfg {Array} disabledDays
29083      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29084      */
29085     disabledDays : null,
29086     /**
29087      * @cfg {String} disabledDaysText
29088      * The tooltip to display when the date falls on a disabled day (defaults to "")
29089      */
29090     disabledDaysText : "",
29091     /**
29092      * @cfg {RegExp} disabledDatesRE
29093      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29094      */
29095     disabledDatesRE : null,
29096     /**
29097      * @cfg {String} disabledDatesText
29098      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29099      */
29100     disabledDatesText : "",
29101     /**
29102      * @cfg {Boolean} constrainToViewport
29103      * True to constrain the date picker to the viewport (defaults to true)
29104      */
29105     constrainToViewport : true,
29106     /**
29107      * @cfg {Array} monthNames
29108      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29109      */
29110     monthNames : Date.monthNames,
29111     /**
29112      * @cfg {Array} dayNames
29113      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29114      */
29115     dayNames : Date.dayNames,
29116     /**
29117      * @cfg {String} nextText
29118      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29119      */
29120     nextText: 'Next Month (Control+Right)',
29121     /**
29122      * @cfg {String} prevText
29123      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29124      */
29125     prevText: 'Previous Month (Control+Left)',
29126     /**
29127      * @cfg {String} monthYearText
29128      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29129      */
29130     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29131     /**
29132      * @cfg {Number} startDay
29133      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29134      */
29135     startDay : 0,
29136     /**
29137      * @cfg {Bool} showClear
29138      * Show a clear button (usefull for date form elements that can be blank.)
29139      */
29140     
29141     showClear: false,
29142     
29143     /**
29144      * Sets the value of the date field
29145      * @param {Date} value The date to set
29146      */
29147     setValue : function(value){
29148         var old = this.value;
29149         
29150         if (typeof(value) == 'string') {
29151          
29152             value = Date.parseDate(value, this.format);
29153         }
29154         if (!value) {
29155             value = new Date();
29156         }
29157         
29158         this.value = value.clearTime(true);
29159         if(this.el){
29160             this.update(this.value);
29161         }
29162     },
29163
29164     /**
29165      * Gets the current selected value of the date field
29166      * @return {Date} The selected date
29167      */
29168     getValue : function(){
29169         return this.value;
29170     },
29171
29172     // private
29173     focus : function(){
29174         if(this.el){
29175             this.update(this.activeDate);
29176         }
29177     },
29178
29179     // privateval
29180     onRender : function(container, position){
29181         
29182         var m = [
29183              '<table cellspacing="0">',
29184                 '<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>',
29185                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29186         var dn = this.dayNames;
29187         for(var i = 0; i < 7; i++){
29188             var d = this.startDay+i;
29189             if(d > 6){
29190                 d = d-7;
29191             }
29192             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29193         }
29194         m[m.length] = "</tr></thead><tbody><tr>";
29195         for(var i = 0; i < 42; i++) {
29196             if(i % 7 == 0 && i != 0){
29197                 m[m.length] = "</tr><tr>";
29198             }
29199             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29200         }
29201         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29202             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29203
29204         var el = document.createElement("div");
29205         el.className = "x-date-picker";
29206         el.innerHTML = m.join("");
29207
29208         container.dom.insertBefore(el, position);
29209
29210         this.el = Roo.get(el);
29211         this.eventEl = Roo.get(el.firstChild);
29212
29213         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29214             handler: this.showPrevMonth,
29215             scope: this,
29216             preventDefault:true,
29217             stopDefault:true
29218         });
29219
29220         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29221             handler: this.showNextMonth,
29222             scope: this,
29223             preventDefault:true,
29224             stopDefault:true
29225         });
29226
29227         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29228
29229         this.monthPicker = this.el.down('div.x-date-mp');
29230         this.monthPicker.enableDisplayMode('block');
29231         
29232         var kn = new Roo.KeyNav(this.eventEl, {
29233             "left" : function(e){
29234                 e.ctrlKey ?
29235                     this.showPrevMonth() :
29236                     this.update(this.activeDate.add("d", -1));
29237             },
29238
29239             "right" : function(e){
29240                 e.ctrlKey ?
29241                     this.showNextMonth() :
29242                     this.update(this.activeDate.add("d", 1));
29243             },
29244
29245             "up" : function(e){
29246                 e.ctrlKey ?
29247                     this.showNextYear() :
29248                     this.update(this.activeDate.add("d", -7));
29249             },
29250
29251             "down" : function(e){
29252                 e.ctrlKey ?
29253                     this.showPrevYear() :
29254                     this.update(this.activeDate.add("d", 7));
29255             },
29256
29257             "pageUp" : function(e){
29258                 this.showNextMonth();
29259             },
29260
29261             "pageDown" : function(e){
29262                 this.showPrevMonth();
29263             },
29264
29265             "enter" : function(e){
29266                 e.stopPropagation();
29267                 return true;
29268             },
29269
29270             scope : this
29271         });
29272
29273         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29274
29275         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29276
29277         this.el.unselectable();
29278         
29279         this.cells = this.el.select("table.x-date-inner tbody td");
29280         this.textNodes = this.el.query("table.x-date-inner tbody span");
29281
29282         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29283             text: "&#160;",
29284             tooltip: this.monthYearText
29285         });
29286
29287         this.mbtn.on('click', this.showMonthPicker, this);
29288         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29289
29290
29291         var today = (new Date()).dateFormat(this.format);
29292         
29293         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29294         if (this.showClear) {
29295             baseTb.add( new Roo.Toolbar.Fill());
29296         }
29297         baseTb.add({
29298             text: String.format(this.todayText, today),
29299             tooltip: String.format(this.todayTip, today),
29300             handler: this.selectToday,
29301             scope: this
29302         });
29303         
29304         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29305             
29306         //});
29307         if (this.showClear) {
29308             
29309             baseTb.add( new Roo.Toolbar.Fill());
29310             baseTb.add({
29311                 text: '&#160;',
29312                 cls: 'x-btn-icon x-btn-clear',
29313                 handler: function() {
29314                     //this.value = '';
29315                     this.fireEvent("select", this, '');
29316                 },
29317                 scope: this
29318             });
29319         }
29320         
29321         
29322         if(Roo.isIE){
29323             this.el.repaint();
29324         }
29325         this.update(this.value);
29326     },
29327
29328     createMonthPicker : function(){
29329         if(!this.monthPicker.dom.firstChild){
29330             var buf = ['<table border="0" cellspacing="0">'];
29331             for(var i = 0; i < 6; i++){
29332                 buf.push(
29333                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29334                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29335                     i == 0 ?
29336                     '<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>' :
29337                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29338                 );
29339             }
29340             buf.push(
29341                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29342                     this.okText,
29343                     '</button><button type="button" class="x-date-mp-cancel">',
29344                     this.cancelText,
29345                     '</button></td></tr>',
29346                 '</table>'
29347             );
29348             this.monthPicker.update(buf.join(''));
29349             this.monthPicker.on('click', this.onMonthClick, this);
29350             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29351
29352             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29353             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29354
29355             this.mpMonths.each(function(m, a, i){
29356                 i += 1;
29357                 if((i%2) == 0){
29358                     m.dom.xmonth = 5 + Math.round(i * .5);
29359                 }else{
29360                     m.dom.xmonth = Math.round((i-1) * .5);
29361                 }
29362             });
29363         }
29364     },
29365
29366     showMonthPicker : function(){
29367         this.createMonthPicker();
29368         var size = this.el.getSize();
29369         this.monthPicker.setSize(size);
29370         this.monthPicker.child('table').setSize(size);
29371
29372         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29373         this.updateMPMonth(this.mpSelMonth);
29374         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29375         this.updateMPYear(this.mpSelYear);
29376
29377         this.monthPicker.slideIn('t', {duration:.2});
29378     },
29379
29380     updateMPYear : function(y){
29381         this.mpyear = y;
29382         var ys = this.mpYears.elements;
29383         for(var i = 1; i <= 10; i++){
29384             var td = ys[i-1], y2;
29385             if((i%2) == 0){
29386                 y2 = y + Math.round(i * .5);
29387                 td.firstChild.innerHTML = y2;
29388                 td.xyear = y2;
29389             }else{
29390                 y2 = y - (5-Math.round(i * .5));
29391                 td.firstChild.innerHTML = y2;
29392                 td.xyear = y2;
29393             }
29394             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29395         }
29396     },
29397
29398     updateMPMonth : function(sm){
29399         this.mpMonths.each(function(m, a, i){
29400             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29401         });
29402     },
29403
29404     selectMPMonth: function(m){
29405         
29406     },
29407
29408     onMonthClick : function(e, t){
29409         e.stopEvent();
29410         var el = new Roo.Element(t), pn;
29411         if(el.is('button.x-date-mp-cancel')){
29412             this.hideMonthPicker();
29413         }
29414         else if(el.is('button.x-date-mp-ok')){
29415             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29416             this.hideMonthPicker();
29417         }
29418         else if(pn = el.up('td.x-date-mp-month', 2)){
29419             this.mpMonths.removeClass('x-date-mp-sel');
29420             pn.addClass('x-date-mp-sel');
29421             this.mpSelMonth = pn.dom.xmonth;
29422         }
29423         else if(pn = el.up('td.x-date-mp-year', 2)){
29424             this.mpYears.removeClass('x-date-mp-sel');
29425             pn.addClass('x-date-mp-sel');
29426             this.mpSelYear = pn.dom.xyear;
29427         }
29428         else if(el.is('a.x-date-mp-prev')){
29429             this.updateMPYear(this.mpyear-10);
29430         }
29431         else if(el.is('a.x-date-mp-next')){
29432             this.updateMPYear(this.mpyear+10);
29433         }
29434     },
29435
29436     onMonthDblClick : function(e, t){
29437         e.stopEvent();
29438         var el = new Roo.Element(t), pn;
29439         if(pn = el.up('td.x-date-mp-month', 2)){
29440             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29441             this.hideMonthPicker();
29442         }
29443         else if(pn = el.up('td.x-date-mp-year', 2)){
29444             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29445             this.hideMonthPicker();
29446         }
29447     },
29448
29449     hideMonthPicker : function(disableAnim){
29450         if(this.monthPicker){
29451             if(disableAnim === true){
29452                 this.monthPicker.hide();
29453             }else{
29454                 this.monthPicker.slideOut('t', {duration:.2});
29455             }
29456         }
29457     },
29458
29459     // private
29460     showPrevMonth : function(e){
29461         this.update(this.activeDate.add("mo", -1));
29462     },
29463
29464     // private
29465     showNextMonth : function(e){
29466         this.update(this.activeDate.add("mo", 1));
29467     },
29468
29469     // private
29470     showPrevYear : function(){
29471         this.update(this.activeDate.add("y", -1));
29472     },
29473
29474     // private
29475     showNextYear : function(){
29476         this.update(this.activeDate.add("y", 1));
29477     },
29478
29479     // private
29480     handleMouseWheel : function(e){
29481         var delta = e.getWheelDelta();
29482         if(delta > 0){
29483             this.showPrevMonth();
29484             e.stopEvent();
29485         } else if(delta < 0){
29486             this.showNextMonth();
29487             e.stopEvent();
29488         }
29489     },
29490
29491     // private
29492     handleDateClick : function(e, t){
29493         e.stopEvent();
29494         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29495             this.setValue(new Date(t.dateValue));
29496             this.fireEvent("select", this, this.value);
29497         }
29498     },
29499
29500     // private
29501     selectToday : function(){
29502         this.setValue(new Date().clearTime());
29503         this.fireEvent("select", this, this.value);
29504     },
29505
29506     // private
29507     update : function(date)
29508     {
29509         var vd = this.activeDate;
29510         this.activeDate = date;
29511         if(vd && this.el){
29512             var t = date.getTime();
29513             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29514                 this.cells.removeClass("x-date-selected");
29515                 this.cells.each(function(c){
29516                    if(c.dom.firstChild.dateValue == t){
29517                        c.addClass("x-date-selected");
29518                        setTimeout(function(){
29519                             try{c.dom.firstChild.focus();}catch(e){}
29520                        }, 50);
29521                        return false;
29522                    }
29523                 });
29524                 return;
29525             }
29526         }
29527         
29528         var days = date.getDaysInMonth();
29529         var firstOfMonth = date.getFirstDateOfMonth();
29530         var startingPos = firstOfMonth.getDay()-this.startDay;
29531
29532         if(startingPos <= this.startDay){
29533             startingPos += 7;
29534         }
29535
29536         var pm = date.add("mo", -1);
29537         var prevStart = pm.getDaysInMonth()-startingPos;
29538
29539         var cells = this.cells.elements;
29540         var textEls = this.textNodes;
29541         days += startingPos;
29542
29543         // convert everything to numbers so it's fast
29544         var day = 86400000;
29545         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29546         var today = new Date().clearTime().getTime();
29547         var sel = date.clearTime().getTime();
29548         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29549         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29550         var ddMatch = this.disabledDatesRE;
29551         var ddText = this.disabledDatesText;
29552         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29553         var ddaysText = this.disabledDaysText;
29554         var format = this.format;
29555
29556         var setCellClass = function(cal, cell){
29557             cell.title = "";
29558             var t = d.getTime();
29559             cell.firstChild.dateValue = t;
29560             if(t == today){
29561                 cell.className += " x-date-today";
29562                 cell.title = cal.todayText;
29563             }
29564             if(t == sel){
29565                 cell.className += " x-date-selected";
29566                 setTimeout(function(){
29567                     try{cell.firstChild.focus();}catch(e){}
29568                 }, 50);
29569             }
29570             // disabling
29571             if(t < min) {
29572                 cell.className = " x-date-disabled";
29573                 cell.title = cal.minText;
29574                 return;
29575             }
29576             if(t > max) {
29577                 cell.className = " x-date-disabled";
29578                 cell.title = cal.maxText;
29579                 return;
29580             }
29581             if(ddays){
29582                 if(ddays.indexOf(d.getDay()) != -1){
29583                     cell.title = ddaysText;
29584                     cell.className = " x-date-disabled";
29585                 }
29586             }
29587             if(ddMatch && format){
29588                 var fvalue = d.dateFormat(format);
29589                 if(ddMatch.test(fvalue)){
29590                     cell.title = ddText.replace("%0", fvalue);
29591                     cell.className = " x-date-disabled";
29592                 }
29593             }
29594         };
29595
29596         var i = 0;
29597         for(; i < startingPos; i++) {
29598             textEls[i].innerHTML = (++prevStart);
29599             d.setDate(d.getDate()+1);
29600             cells[i].className = "x-date-prevday";
29601             setCellClass(this, cells[i]);
29602         }
29603         for(; i < days; i++){
29604             intDay = i - startingPos + 1;
29605             textEls[i].innerHTML = (intDay);
29606             d.setDate(d.getDate()+1);
29607             cells[i].className = "x-date-active";
29608             setCellClass(this, cells[i]);
29609         }
29610         var extraDays = 0;
29611         for(; i < 42; i++) {
29612              textEls[i].innerHTML = (++extraDays);
29613              d.setDate(d.getDate()+1);
29614              cells[i].className = "x-date-nextday";
29615              setCellClass(this, cells[i]);
29616         }
29617
29618         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29619         this.fireEvent('monthchange', this, date);
29620         
29621         if(!this.internalRender){
29622             var main = this.el.dom.firstChild;
29623             var w = main.offsetWidth;
29624             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29625             Roo.fly(main).setWidth(w);
29626             this.internalRender = true;
29627             // opera does not respect the auto grow header center column
29628             // then, after it gets a width opera refuses to recalculate
29629             // without a second pass
29630             if(Roo.isOpera && !this.secondPass){
29631                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29632                 this.secondPass = true;
29633                 this.update.defer(10, this, [date]);
29634             }
29635         }
29636         
29637         
29638     }
29639 });        /*
29640  * Based on:
29641  * Ext JS Library 1.1.1
29642  * Copyright(c) 2006-2007, Ext JS, LLC.
29643  *
29644  * Originally Released Under LGPL - original licence link has changed is not relivant.
29645  *
29646  * Fork - LGPL
29647  * <script type="text/javascript">
29648  */
29649 /**
29650  * @class Roo.TabPanel
29651  * @extends Roo.util.Observable
29652  * A lightweight tab container.
29653  * <br><br>
29654  * Usage:
29655  * <pre><code>
29656 // basic tabs 1, built from existing content
29657 var tabs = new Roo.TabPanel("tabs1");
29658 tabs.addTab("script", "View Script");
29659 tabs.addTab("markup", "View Markup");
29660 tabs.activate("script");
29661
29662 // more advanced tabs, built from javascript
29663 var jtabs = new Roo.TabPanel("jtabs");
29664 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29665
29666 // set up the UpdateManager
29667 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29668 var updater = tab2.getUpdateManager();
29669 updater.setDefaultUrl("ajax1.htm");
29670 tab2.on('activate', updater.refresh, updater, true);
29671
29672 // Use setUrl for Ajax loading
29673 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29674 tab3.setUrl("ajax2.htm", null, true);
29675
29676 // Disabled tab
29677 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29678 tab4.disable();
29679
29680 jtabs.activate("jtabs-1");
29681  * </code></pre>
29682  * @constructor
29683  * Create a new TabPanel.
29684  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29685  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29686  */
29687 Roo.TabPanel = function(container, config){
29688     /**
29689     * The container element for this TabPanel.
29690     * @type Roo.Element
29691     */
29692     this.el = Roo.get(container, true);
29693     if(config){
29694         if(typeof config == "boolean"){
29695             this.tabPosition = config ? "bottom" : "top";
29696         }else{
29697             Roo.apply(this, config);
29698         }
29699     }
29700     if(this.tabPosition == "bottom"){
29701         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29702         this.el.addClass("x-tabs-bottom");
29703     }
29704     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29705     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29706     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29707     if(Roo.isIE){
29708         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29709     }
29710     if(this.tabPosition != "bottom"){
29711         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29712          * @type Roo.Element
29713          */
29714         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29715         this.el.addClass("x-tabs-top");
29716     }
29717     this.items = [];
29718
29719     this.bodyEl.setStyle("position", "relative");
29720
29721     this.active = null;
29722     this.activateDelegate = this.activate.createDelegate(this);
29723
29724     this.addEvents({
29725         /**
29726          * @event tabchange
29727          * Fires when the active tab changes
29728          * @param {Roo.TabPanel} this
29729          * @param {Roo.TabPanelItem} activePanel The new active tab
29730          */
29731         "tabchange": true,
29732         /**
29733          * @event beforetabchange
29734          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29735          * @param {Roo.TabPanel} this
29736          * @param {Object} e Set cancel to true on this object to cancel the tab change
29737          * @param {Roo.TabPanelItem} tab The tab being changed to
29738          */
29739         "beforetabchange" : true
29740     });
29741
29742     Roo.EventManager.onWindowResize(this.onResize, this);
29743     this.cpad = this.el.getPadding("lr");
29744     this.hiddenCount = 0;
29745
29746
29747     // toolbar on the tabbar support...
29748     if (this.toolbar) {
29749         var tcfg = this.toolbar;
29750         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29751         this.toolbar = new Roo.Toolbar(tcfg);
29752         if (Roo.isSafari) {
29753             var tbl = tcfg.container.child('table', true);
29754             tbl.setAttribute('width', '100%');
29755         }
29756         
29757     }
29758    
29759
29760
29761     Roo.TabPanel.superclass.constructor.call(this);
29762 };
29763
29764 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29765     /*
29766      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29767      */
29768     tabPosition : "top",
29769     /*
29770      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29771      */
29772     currentTabWidth : 0,
29773     /*
29774      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29775      */
29776     minTabWidth : 40,
29777     /*
29778      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29779      */
29780     maxTabWidth : 250,
29781     /*
29782      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29783      */
29784     preferredTabWidth : 175,
29785     /*
29786      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29787      */
29788     resizeTabs : false,
29789     /*
29790      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29791      */
29792     monitorResize : true,
29793     /*
29794      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29795      */
29796     toolbar : false,
29797
29798     /**
29799      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29800      * @param {String} id The id of the div to use <b>or create</b>
29801      * @param {String} text The text for the tab
29802      * @param {String} content (optional) Content to put in the TabPanelItem body
29803      * @param {Boolean} closable (optional) True to create a close icon on the tab
29804      * @return {Roo.TabPanelItem} The created TabPanelItem
29805      */
29806     addTab : function(id, text, content, closable){
29807         var item = new Roo.TabPanelItem(this, id, text, closable);
29808         this.addTabItem(item);
29809         if(content){
29810             item.setContent(content);
29811         }
29812         return item;
29813     },
29814
29815     /**
29816      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29817      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29818      * @return {Roo.TabPanelItem}
29819      */
29820     getTab : function(id){
29821         return this.items[id];
29822     },
29823
29824     /**
29825      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29826      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29827      */
29828     hideTab : function(id){
29829         var t = this.items[id];
29830         if(!t.isHidden()){
29831            t.setHidden(true);
29832            this.hiddenCount++;
29833            this.autoSizeTabs();
29834         }
29835     },
29836
29837     /**
29838      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29839      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29840      */
29841     unhideTab : function(id){
29842         var t = this.items[id];
29843         if(t.isHidden()){
29844            t.setHidden(false);
29845            this.hiddenCount--;
29846            this.autoSizeTabs();
29847         }
29848     },
29849
29850     /**
29851      * Adds an existing {@link Roo.TabPanelItem}.
29852      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29853      */
29854     addTabItem : function(item){
29855         this.items[item.id] = item;
29856         this.items.push(item);
29857         if(this.resizeTabs){
29858            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29859            this.autoSizeTabs();
29860         }else{
29861             item.autoSize();
29862         }
29863     },
29864
29865     /**
29866      * Removes a {@link Roo.TabPanelItem}.
29867      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29868      */
29869     removeTab : function(id){
29870         var items = this.items;
29871         var tab = items[id];
29872         if(!tab) { return; }
29873         var index = items.indexOf(tab);
29874         if(this.active == tab && items.length > 1){
29875             var newTab = this.getNextAvailable(index);
29876             if(newTab) {
29877                 newTab.activate();
29878             }
29879         }
29880         this.stripEl.dom.removeChild(tab.pnode.dom);
29881         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29882             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29883         }
29884         items.splice(index, 1);
29885         delete this.items[tab.id];
29886         tab.fireEvent("close", tab);
29887         tab.purgeListeners();
29888         this.autoSizeTabs();
29889     },
29890
29891     getNextAvailable : function(start){
29892         var items = this.items;
29893         var index = start;
29894         // look for a next tab that will slide over to
29895         // replace the one being removed
29896         while(index < items.length){
29897             var item = items[++index];
29898             if(item && !item.isHidden()){
29899                 return item;
29900             }
29901         }
29902         // if one isn't found select the previous tab (on the left)
29903         index = start;
29904         while(index >= 0){
29905             var item = items[--index];
29906             if(item && !item.isHidden()){
29907                 return item;
29908             }
29909         }
29910         return null;
29911     },
29912
29913     /**
29914      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29915      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29916      */
29917     disableTab : function(id){
29918         var tab = this.items[id];
29919         if(tab && this.active != tab){
29920             tab.disable();
29921         }
29922     },
29923
29924     /**
29925      * Enables a {@link Roo.TabPanelItem} that is disabled.
29926      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29927      */
29928     enableTab : function(id){
29929         var tab = this.items[id];
29930         tab.enable();
29931     },
29932
29933     /**
29934      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29935      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29936      * @return {Roo.TabPanelItem} The TabPanelItem.
29937      */
29938     activate : function(id){
29939         var tab = this.items[id];
29940         if(!tab){
29941             return null;
29942         }
29943         if(tab == this.active || tab.disabled){
29944             return tab;
29945         }
29946         var e = {};
29947         this.fireEvent("beforetabchange", this, e, tab);
29948         if(e.cancel !== true && !tab.disabled){
29949             if(this.active){
29950                 this.active.hide();
29951             }
29952             this.active = this.items[id];
29953             this.active.show();
29954             this.fireEvent("tabchange", this, this.active);
29955         }
29956         return tab;
29957     },
29958
29959     /**
29960      * Gets the active {@link Roo.TabPanelItem}.
29961      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29962      */
29963     getActiveTab : function(){
29964         return this.active;
29965     },
29966
29967     /**
29968      * Updates the tab body element to fit the height of the container element
29969      * for overflow scrolling
29970      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29971      */
29972     syncHeight : function(targetHeight){
29973         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29974         var bm = this.bodyEl.getMargins();
29975         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29976         this.bodyEl.setHeight(newHeight);
29977         return newHeight;
29978     },
29979
29980     onResize : function(){
29981         if(this.monitorResize){
29982             this.autoSizeTabs();
29983         }
29984     },
29985
29986     /**
29987      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29988      */
29989     beginUpdate : function(){
29990         this.updating = true;
29991     },
29992
29993     /**
29994      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29995      */
29996     endUpdate : function(){
29997         this.updating = false;
29998         this.autoSizeTabs();
29999     },
30000
30001     /**
30002      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30003      */
30004     autoSizeTabs : function(){
30005         var count = this.items.length;
30006         var vcount = count - this.hiddenCount;
30007         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30008             return;
30009         }
30010         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30011         var availWidth = Math.floor(w / vcount);
30012         var b = this.stripBody;
30013         if(b.getWidth() > w){
30014             var tabs = this.items;
30015             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30016             if(availWidth < this.minTabWidth){
30017                 /*if(!this.sleft){    // incomplete scrolling code
30018                     this.createScrollButtons();
30019                 }
30020                 this.showScroll();
30021                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30022             }
30023         }else{
30024             if(this.currentTabWidth < this.preferredTabWidth){
30025                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30026             }
30027         }
30028     },
30029
30030     /**
30031      * Returns the number of tabs in this TabPanel.
30032      * @return {Number}
30033      */
30034      getCount : function(){
30035          return this.items.length;
30036      },
30037
30038     /**
30039      * Resizes all the tabs to the passed width
30040      * @param {Number} The new width
30041      */
30042     setTabWidth : function(width){
30043         this.currentTabWidth = width;
30044         for(var i = 0, len = this.items.length; i < len; i++) {
30045                 if(!this.items[i].isHidden()) {
30046                 this.items[i].setWidth(width);
30047             }
30048         }
30049     },
30050
30051     /**
30052      * Destroys this TabPanel
30053      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30054      */
30055     destroy : function(removeEl){
30056         Roo.EventManager.removeResizeListener(this.onResize, this);
30057         for(var i = 0, len = this.items.length; i < len; i++){
30058             this.items[i].purgeListeners();
30059         }
30060         if(removeEl === true){
30061             this.el.update("");
30062             this.el.remove();
30063         }
30064     }
30065 });
30066
30067 /**
30068  * @class Roo.TabPanelItem
30069  * @extends Roo.util.Observable
30070  * Represents an individual item (tab plus body) in a TabPanel.
30071  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30072  * @param {String} id The id of this TabPanelItem
30073  * @param {String} text The text for the tab of this TabPanelItem
30074  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30075  */
30076 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30077     /**
30078      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30079      * @type Roo.TabPanel
30080      */
30081     this.tabPanel = tabPanel;
30082     /**
30083      * The id for this TabPanelItem
30084      * @type String
30085      */
30086     this.id = id;
30087     /** @private */
30088     this.disabled = false;
30089     /** @private */
30090     this.text = text;
30091     /** @private */
30092     this.loaded = false;
30093     this.closable = closable;
30094
30095     /**
30096      * The body element for this TabPanelItem.
30097      * @type Roo.Element
30098      */
30099     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30100     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30101     this.bodyEl.setStyle("display", "block");
30102     this.bodyEl.setStyle("zoom", "1");
30103     this.hideAction();
30104
30105     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30106     /** @private */
30107     this.el = Roo.get(els.el, true);
30108     this.inner = Roo.get(els.inner, true);
30109     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30110     this.pnode = Roo.get(els.el.parentNode, true);
30111     this.el.on("mousedown", this.onTabMouseDown, this);
30112     this.el.on("click", this.onTabClick, this);
30113     /** @private */
30114     if(closable){
30115         var c = Roo.get(els.close, true);
30116         c.dom.title = this.closeText;
30117         c.addClassOnOver("close-over");
30118         c.on("click", this.closeClick, this);
30119      }
30120
30121     this.addEvents({
30122          /**
30123          * @event activate
30124          * Fires when this tab becomes the active tab.
30125          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30126          * @param {Roo.TabPanelItem} this
30127          */
30128         "activate": true,
30129         /**
30130          * @event beforeclose
30131          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30132          * @param {Roo.TabPanelItem} this
30133          * @param {Object} e Set cancel to true on this object to cancel the close.
30134          */
30135         "beforeclose": true,
30136         /**
30137          * @event close
30138          * Fires when this tab is closed.
30139          * @param {Roo.TabPanelItem} this
30140          */
30141          "close": true,
30142         /**
30143          * @event deactivate
30144          * Fires when this tab is no longer the active tab.
30145          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30146          * @param {Roo.TabPanelItem} this
30147          */
30148          "deactivate" : true
30149     });
30150     this.hidden = false;
30151
30152     Roo.TabPanelItem.superclass.constructor.call(this);
30153 };
30154
30155 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30156     purgeListeners : function(){
30157        Roo.util.Observable.prototype.purgeListeners.call(this);
30158        this.el.removeAllListeners();
30159     },
30160     /**
30161      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30162      */
30163     show : function(){
30164         this.pnode.addClass("on");
30165         this.showAction();
30166         if(Roo.isOpera){
30167             this.tabPanel.stripWrap.repaint();
30168         }
30169         this.fireEvent("activate", this.tabPanel, this);
30170     },
30171
30172     /**
30173      * Returns true if this tab is the active tab.
30174      * @return {Boolean}
30175      */
30176     isActive : function(){
30177         return this.tabPanel.getActiveTab() == this;
30178     },
30179
30180     /**
30181      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30182      */
30183     hide : function(){
30184         this.pnode.removeClass("on");
30185         this.hideAction();
30186         this.fireEvent("deactivate", this.tabPanel, this);
30187     },
30188
30189     hideAction : function(){
30190         this.bodyEl.hide();
30191         this.bodyEl.setStyle("position", "absolute");
30192         this.bodyEl.setLeft("-20000px");
30193         this.bodyEl.setTop("-20000px");
30194     },
30195
30196     showAction : function(){
30197         this.bodyEl.setStyle("position", "relative");
30198         this.bodyEl.setTop("");
30199         this.bodyEl.setLeft("");
30200         this.bodyEl.show();
30201     },
30202
30203     /**
30204      * Set the tooltip for the tab.
30205      * @param {String} tooltip The tab's tooltip
30206      */
30207     setTooltip : function(text){
30208         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30209             this.textEl.dom.qtip = text;
30210             this.textEl.dom.removeAttribute('title');
30211         }else{
30212             this.textEl.dom.title = text;
30213         }
30214     },
30215
30216     onTabClick : function(e){
30217         e.preventDefault();
30218         this.tabPanel.activate(this.id);
30219     },
30220
30221     onTabMouseDown : function(e){
30222         e.preventDefault();
30223         this.tabPanel.activate(this.id);
30224     },
30225
30226     getWidth : function(){
30227         return this.inner.getWidth();
30228     },
30229
30230     setWidth : function(width){
30231         var iwidth = width - this.pnode.getPadding("lr");
30232         this.inner.setWidth(iwidth);
30233         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30234         this.pnode.setWidth(width);
30235     },
30236
30237     /**
30238      * Show or hide the tab
30239      * @param {Boolean} hidden True to hide or false to show.
30240      */
30241     setHidden : function(hidden){
30242         this.hidden = hidden;
30243         this.pnode.setStyle("display", hidden ? "none" : "");
30244     },
30245
30246     /**
30247      * Returns true if this tab is "hidden"
30248      * @return {Boolean}
30249      */
30250     isHidden : function(){
30251         return this.hidden;
30252     },
30253
30254     /**
30255      * Returns the text for this tab
30256      * @return {String}
30257      */
30258     getText : function(){
30259         return this.text;
30260     },
30261
30262     autoSize : function(){
30263         //this.el.beginMeasure();
30264         this.textEl.setWidth(1);
30265         /*
30266          *  #2804 [new] Tabs in Roojs
30267          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30268          */
30269         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30270         //this.el.endMeasure();
30271     },
30272
30273     /**
30274      * Sets the text for the tab (Note: this also sets the tooltip text)
30275      * @param {String} text The tab's text and tooltip
30276      */
30277     setText : function(text){
30278         this.text = text;
30279         this.textEl.update(text);
30280         this.setTooltip(text);
30281         if(!this.tabPanel.resizeTabs){
30282             this.autoSize();
30283         }
30284     },
30285     /**
30286      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30287      */
30288     activate : function(){
30289         this.tabPanel.activate(this.id);
30290     },
30291
30292     /**
30293      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30294      */
30295     disable : function(){
30296         if(this.tabPanel.active != this){
30297             this.disabled = true;
30298             this.pnode.addClass("disabled");
30299         }
30300     },
30301
30302     /**
30303      * Enables this TabPanelItem if it was previously disabled.
30304      */
30305     enable : function(){
30306         this.disabled = false;
30307         this.pnode.removeClass("disabled");
30308     },
30309
30310     /**
30311      * Sets the content for this TabPanelItem.
30312      * @param {String} content The content
30313      * @param {Boolean} loadScripts true to look for and load scripts
30314      */
30315     setContent : function(content, loadScripts){
30316         this.bodyEl.update(content, loadScripts);
30317     },
30318
30319     /**
30320      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30321      * @return {Roo.UpdateManager} The UpdateManager
30322      */
30323     getUpdateManager : function(){
30324         return this.bodyEl.getUpdateManager();
30325     },
30326
30327     /**
30328      * Set a URL to be used to load the content for this TabPanelItem.
30329      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30330      * @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)
30331      * @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)
30332      * @return {Roo.UpdateManager} The UpdateManager
30333      */
30334     setUrl : function(url, params, loadOnce){
30335         if(this.refreshDelegate){
30336             this.un('activate', this.refreshDelegate);
30337         }
30338         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30339         this.on("activate", this.refreshDelegate);
30340         return this.bodyEl.getUpdateManager();
30341     },
30342
30343     /** @private */
30344     _handleRefresh : function(url, params, loadOnce){
30345         if(!loadOnce || !this.loaded){
30346             var updater = this.bodyEl.getUpdateManager();
30347             updater.update(url, params, this._setLoaded.createDelegate(this));
30348         }
30349     },
30350
30351     /**
30352      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30353      *   Will fail silently if the setUrl method has not been called.
30354      *   This does not activate the panel, just updates its content.
30355      */
30356     refresh : function(){
30357         if(this.refreshDelegate){
30358            this.loaded = false;
30359            this.refreshDelegate();
30360         }
30361     },
30362
30363     /** @private */
30364     _setLoaded : function(){
30365         this.loaded = true;
30366     },
30367
30368     /** @private */
30369     closeClick : function(e){
30370         var o = {};
30371         e.stopEvent();
30372         this.fireEvent("beforeclose", this, o);
30373         if(o.cancel !== true){
30374             this.tabPanel.removeTab(this.id);
30375         }
30376     },
30377     /**
30378      * The text displayed in the tooltip for the close icon.
30379      * @type String
30380      */
30381     closeText : "Close this tab"
30382 });
30383
30384 /** @private */
30385 Roo.TabPanel.prototype.createStrip = function(container){
30386     var strip = document.createElement("div");
30387     strip.className = "x-tabs-wrap";
30388     container.appendChild(strip);
30389     return strip;
30390 };
30391 /** @private */
30392 Roo.TabPanel.prototype.createStripList = function(strip){
30393     // div wrapper for retard IE
30394     // returns the "tr" element.
30395     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30396         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30397         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30398     return strip.firstChild.firstChild.firstChild.firstChild;
30399 };
30400 /** @private */
30401 Roo.TabPanel.prototype.createBody = function(container){
30402     var body = document.createElement("div");
30403     Roo.id(body, "tab-body");
30404     Roo.fly(body).addClass("x-tabs-body");
30405     container.appendChild(body);
30406     return body;
30407 };
30408 /** @private */
30409 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30410     var body = Roo.getDom(id);
30411     if(!body){
30412         body = document.createElement("div");
30413         body.id = id;
30414     }
30415     Roo.fly(body).addClass("x-tabs-item-body");
30416     bodyEl.insertBefore(body, bodyEl.firstChild);
30417     return body;
30418 };
30419 /** @private */
30420 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30421     var td = document.createElement("td");
30422     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30423     //stripEl.appendChild(td);
30424     if(closable){
30425         td.className = "x-tabs-closable";
30426         if(!this.closeTpl){
30427             this.closeTpl = new Roo.Template(
30428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30430                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30431             );
30432         }
30433         var el = this.closeTpl.overwrite(td, {"text": text});
30434         var close = el.getElementsByTagName("div")[0];
30435         var inner = el.getElementsByTagName("em")[0];
30436         return {"el": el, "close": close, "inner": inner};
30437     } else {
30438         if(!this.tabTpl){
30439             this.tabTpl = new Roo.Template(
30440                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30441                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30442             );
30443         }
30444         var el = this.tabTpl.overwrite(td, {"text": text});
30445         var inner = el.getElementsByTagName("em")[0];
30446         return {"el": el, "inner": inner};
30447     }
30448 };/*
30449  * Based on:
30450  * Ext JS Library 1.1.1
30451  * Copyright(c) 2006-2007, Ext JS, LLC.
30452  *
30453  * Originally Released Under LGPL - original licence link has changed is not relivant.
30454  *
30455  * Fork - LGPL
30456  * <script type="text/javascript">
30457  */
30458
30459 /**
30460  * @class Roo.Button
30461  * @extends Roo.util.Observable
30462  * Simple Button class
30463  * @cfg {String} text The button text
30464  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30465  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30466  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30467  * @cfg {Object} scope The scope of the handler
30468  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30469  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30470  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30471  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30472  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30473  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30474    applies if enableToggle = true)
30475  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30476  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30477   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30478  * @constructor
30479  * Create a new button
30480  * @param {Object} config The config object
30481  */
30482 Roo.Button = function(renderTo, config)
30483 {
30484     if (!config) {
30485         config = renderTo;
30486         renderTo = config.renderTo || false;
30487     }
30488     
30489     Roo.apply(this, config);
30490     this.addEvents({
30491         /**
30492              * @event click
30493              * Fires when this button is clicked
30494              * @param {Button} this
30495              * @param {EventObject} e The click event
30496              */
30497             "click" : true,
30498         /**
30499              * @event toggle
30500              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30501              * @param {Button} this
30502              * @param {Boolean} pressed
30503              */
30504             "toggle" : true,
30505         /**
30506              * @event mouseover
30507              * Fires when the mouse hovers over the button
30508              * @param {Button} this
30509              * @param {Event} e The event object
30510              */
30511         'mouseover' : true,
30512         /**
30513              * @event mouseout
30514              * Fires when the mouse exits the button
30515              * @param {Button} this
30516              * @param {Event} e The event object
30517              */
30518         'mouseout': true,
30519          /**
30520              * @event render
30521              * Fires when the button is rendered
30522              * @param {Button} this
30523              */
30524         'render': true
30525     });
30526     if(this.menu){
30527         this.menu = Roo.menu.MenuMgr.get(this.menu);
30528     }
30529     // register listeners first!!  - so render can be captured..
30530     Roo.util.Observable.call(this);
30531     if(renderTo){
30532         this.render(renderTo);
30533     }
30534     
30535   
30536 };
30537
30538 Roo.extend(Roo.Button, Roo.util.Observable, {
30539     /**
30540      * 
30541      */
30542     
30543     /**
30544      * Read-only. True if this button is hidden
30545      * @type Boolean
30546      */
30547     hidden : false,
30548     /**
30549      * Read-only. True if this button is disabled
30550      * @type Boolean
30551      */
30552     disabled : false,
30553     /**
30554      * Read-only. True if this button is pressed (only if enableToggle = true)
30555      * @type Boolean
30556      */
30557     pressed : false,
30558
30559     /**
30560      * @cfg {Number} tabIndex 
30561      * The DOM tabIndex for this button (defaults to undefined)
30562      */
30563     tabIndex : undefined,
30564
30565     /**
30566      * @cfg {Boolean} enableToggle
30567      * True to enable pressed/not pressed toggling (defaults to false)
30568      */
30569     enableToggle: false,
30570     /**
30571      * @cfg {Roo.menu.Menu} menu
30572      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30573      */
30574     menu : undefined,
30575     /**
30576      * @cfg {String} menuAlign
30577      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30578      */
30579     menuAlign : "tl-bl?",
30580
30581     /**
30582      * @cfg {String} iconCls
30583      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30584      */
30585     iconCls : undefined,
30586     /**
30587      * @cfg {String} type
30588      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30589      */
30590     type : 'button',
30591
30592     // private
30593     menuClassTarget: 'tr',
30594
30595     /**
30596      * @cfg {String} clickEvent
30597      * The type of event to map to the button's event handler (defaults to 'click')
30598      */
30599     clickEvent : 'click',
30600
30601     /**
30602      * @cfg {Boolean} handleMouseEvents
30603      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30604      */
30605     handleMouseEvents : true,
30606
30607     /**
30608      * @cfg {String} tooltipType
30609      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30610      */
30611     tooltipType : 'qtip',
30612
30613     /**
30614      * @cfg {String} cls
30615      * A CSS class to apply to the button's main element.
30616      */
30617     
30618     /**
30619      * @cfg {Roo.Template} template (Optional)
30620      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30621      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30622      * require code modifications if required elements (e.g. a button) aren't present.
30623      */
30624
30625     // private
30626     render : function(renderTo){
30627         var btn;
30628         if(this.hideParent){
30629             this.parentEl = Roo.get(renderTo);
30630         }
30631         if(!this.dhconfig){
30632             if(!this.template){
30633                 if(!Roo.Button.buttonTemplate){
30634                     // hideous table template
30635                     Roo.Button.buttonTemplate = new Roo.Template(
30636                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30637                         '<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>',
30638                         "</tr></tbody></table>");
30639                 }
30640                 this.template = Roo.Button.buttonTemplate;
30641             }
30642             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30643             var btnEl = btn.child("button:first");
30644             btnEl.on('focus', this.onFocus, this);
30645             btnEl.on('blur', this.onBlur, this);
30646             if(this.cls){
30647                 btn.addClass(this.cls);
30648             }
30649             if(this.icon){
30650                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30651             }
30652             if(this.iconCls){
30653                 btnEl.addClass(this.iconCls);
30654                 if(!this.cls){
30655                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30656                 }
30657             }
30658             if(this.tabIndex !== undefined){
30659                 btnEl.dom.tabIndex = this.tabIndex;
30660             }
30661             if(this.tooltip){
30662                 if(typeof this.tooltip == 'object'){
30663                     Roo.QuickTips.tips(Roo.apply({
30664                           target: btnEl.id
30665                     }, this.tooltip));
30666                 } else {
30667                     btnEl.dom[this.tooltipType] = this.tooltip;
30668                 }
30669             }
30670         }else{
30671             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30672         }
30673         this.el = btn;
30674         if(this.id){
30675             this.el.dom.id = this.el.id = this.id;
30676         }
30677         if(this.menu){
30678             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30679             this.menu.on("show", this.onMenuShow, this);
30680             this.menu.on("hide", this.onMenuHide, this);
30681         }
30682         btn.addClass("x-btn");
30683         if(Roo.isIE && !Roo.isIE7){
30684             this.autoWidth.defer(1, this);
30685         }else{
30686             this.autoWidth();
30687         }
30688         if(this.handleMouseEvents){
30689             btn.on("mouseover", this.onMouseOver, this);
30690             btn.on("mouseout", this.onMouseOut, this);
30691             btn.on("mousedown", this.onMouseDown, this);
30692         }
30693         btn.on(this.clickEvent, this.onClick, this);
30694         //btn.on("mouseup", this.onMouseUp, this);
30695         if(this.hidden){
30696             this.hide();
30697         }
30698         if(this.disabled){
30699             this.disable();
30700         }
30701         Roo.ButtonToggleMgr.register(this);
30702         if(this.pressed){
30703             this.el.addClass("x-btn-pressed");
30704         }
30705         if(this.repeat){
30706             var repeater = new Roo.util.ClickRepeater(btn,
30707                 typeof this.repeat == "object" ? this.repeat : {}
30708             );
30709             repeater.on("click", this.onClick,  this);
30710         }
30711         
30712         this.fireEvent('render', this);
30713         
30714     },
30715     /**
30716      * Returns the button's underlying element
30717      * @return {Roo.Element} The element
30718      */
30719     getEl : function(){
30720         return this.el;  
30721     },
30722     
30723     /**
30724      * Destroys this Button and removes any listeners.
30725      */
30726     destroy : function(){
30727         Roo.ButtonToggleMgr.unregister(this);
30728         this.el.removeAllListeners();
30729         this.purgeListeners();
30730         this.el.remove();
30731     },
30732
30733     // private
30734     autoWidth : function(){
30735         if(this.el){
30736             this.el.setWidth("auto");
30737             if(Roo.isIE7 && Roo.isStrict){
30738                 var ib = this.el.child('button');
30739                 if(ib && ib.getWidth() > 20){
30740                     ib.clip();
30741                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30742                 }
30743             }
30744             if(this.minWidth){
30745                 if(this.hidden){
30746                     this.el.beginMeasure();
30747                 }
30748                 if(this.el.getWidth() < this.minWidth){
30749                     this.el.setWidth(this.minWidth);
30750                 }
30751                 if(this.hidden){
30752                     this.el.endMeasure();
30753                 }
30754             }
30755         }
30756     },
30757
30758     /**
30759      * Assigns this button's click handler
30760      * @param {Function} handler The function to call when the button is clicked
30761      * @param {Object} scope (optional) Scope for the function passed in
30762      */
30763     setHandler : function(handler, scope){
30764         this.handler = handler;
30765         this.scope = scope;  
30766     },
30767     
30768     /**
30769      * Sets this button's text
30770      * @param {String} text The button text
30771      */
30772     setText : function(text){
30773         this.text = text;
30774         if(this.el){
30775             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30776         }
30777         this.autoWidth();
30778     },
30779     
30780     /**
30781      * Gets the text for this button
30782      * @return {String} The button text
30783      */
30784     getText : function(){
30785         return this.text;  
30786     },
30787     
30788     /**
30789      * Show this button
30790      */
30791     show: function(){
30792         this.hidden = false;
30793         if(this.el){
30794             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30795         }
30796     },
30797     
30798     /**
30799      * Hide this button
30800      */
30801     hide: function(){
30802         this.hidden = true;
30803         if(this.el){
30804             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30805         }
30806     },
30807     
30808     /**
30809      * Convenience function for boolean show/hide
30810      * @param {Boolean} visible True to show, false to hide
30811      */
30812     setVisible: function(visible){
30813         if(visible) {
30814             this.show();
30815         }else{
30816             this.hide();
30817         }
30818     },
30819     
30820     /**
30821      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30822      * @param {Boolean} state (optional) Force a particular state
30823      */
30824     toggle : function(state){
30825         state = state === undefined ? !this.pressed : state;
30826         if(state != this.pressed){
30827             if(state){
30828                 this.el.addClass("x-btn-pressed");
30829                 this.pressed = true;
30830                 this.fireEvent("toggle", this, true);
30831             }else{
30832                 this.el.removeClass("x-btn-pressed");
30833                 this.pressed = false;
30834                 this.fireEvent("toggle", this, false);
30835             }
30836             if(this.toggleHandler){
30837                 this.toggleHandler.call(this.scope || this, this, state);
30838             }
30839         }
30840     },
30841     
30842     /**
30843      * Focus the button
30844      */
30845     focus : function(){
30846         this.el.child('button:first').focus();
30847     },
30848     
30849     /**
30850      * Disable this button
30851      */
30852     disable : function(){
30853         if(this.el){
30854             this.el.addClass("x-btn-disabled");
30855         }
30856         this.disabled = true;
30857     },
30858     
30859     /**
30860      * Enable this button
30861      */
30862     enable : function(){
30863         if(this.el){
30864             this.el.removeClass("x-btn-disabled");
30865         }
30866         this.disabled = false;
30867     },
30868
30869     /**
30870      * Convenience function for boolean enable/disable
30871      * @param {Boolean} enabled True to enable, false to disable
30872      */
30873     setDisabled : function(v){
30874         this[v !== true ? "enable" : "disable"]();
30875     },
30876
30877     // private
30878     onClick : function(e)
30879     {
30880         if(e){
30881             e.preventDefault();
30882         }
30883         if(e.button != 0){
30884             return;
30885         }
30886         if(!this.disabled){
30887             if(this.enableToggle){
30888                 this.toggle();
30889             }
30890             if(this.menu && !this.menu.isVisible()){
30891                 this.menu.show(this.el, this.menuAlign);
30892             }
30893             this.fireEvent("click", this, e);
30894             if(this.handler){
30895                 this.el.removeClass("x-btn-over");
30896                 this.handler.call(this.scope || this, this, e);
30897             }
30898         }
30899     },
30900     // private
30901     onMouseOver : function(e){
30902         if(!this.disabled){
30903             this.el.addClass("x-btn-over");
30904             this.fireEvent('mouseover', this, e);
30905         }
30906     },
30907     // private
30908     onMouseOut : function(e){
30909         if(!e.within(this.el,  true)){
30910             this.el.removeClass("x-btn-over");
30911             this.fireEvent('mouseout', this, e);
30912         }
30913     },
30914     // private
30915     onFocus : function(e){
30916         if(!this.disabled){
30917             this.el.addClass("x-btn-focus");
30918         }
30919     },
30920     // private
30921     onBlur : function(e){
30922         this.el.removeClass("x-btn-focus");
30923     },
30924     // private
30925     onMouseDown : function(e){
30926         if(!this.disabled && e.button == 0){
30927             this.el.addClass("x-btn-click");
30928             Roo.get(document).on('mouseup', this.onMouseUp, this);
30929         }
30930     },
30931     // private
30932     onMouseUp : function(e){
30933         if(e.button == 0){
30934             this.el.removeClass("x-btn-click");
30935             Roo.get(document).un('mouseup', this.onMouseUp, this);
30936         }
30937     },
30938     // private
30939     onMenuShow : function(e){
30940         this.el.addClass("x-btn-menu-active");
30941     },
30942     // private
30943     onMenuHide : function(e){
30944         this.el.removeClass("x-btn-menu-active");
30945     }   
30946 });
30947
30948 // Private utility class used by Button
30949 Roo.ButtonToggleMgr = function(){
30950    var groups = {};
30951    
30952    function toggleGroup(btn, state){
30953        if(state){
30954            var g = groups[btn.toggleGroup];
30955            for(var i = 0, l = g.length; i < l; i++){
30956                if(g[i] != btn){
30957                    g[i].toggle(false);
30958                }
30959            }
30960        }
30961    }
30962    
30963    return {
30964        register : function(btn){
30965            if(!btn.toggleGroup){
30966                return;
30967            }
30968            var g = groups[btn.toggleGroup];
30969            if(!g){
30970                g = groups[btn.toggleGroup] = [];
30971            }
30972            g.push(btn);
30973            btn.on("toggle", toggleGroup);
30974        },
30975        
30976        unregister : function(btn){
30977            if(!btn.toggleGroup){
30978                return;
30979            }
30980            var g = groups[btn.toggleGroup];
30981            if(g){
30982                g.remove(btn);
30983                btn.un("toggle", toggleGroup);
30984            }
30985        }
30986    };
30987 }();/*
30988  * Based on:
30989  * Ext JS Library 1.1.1
30990  * Copyright(c) 2006-2007, Ext JS, LLC.
30991  *
30992  * Originally Released Under LGPL - original licence link has changed is not relivant.
30993  *
30994  * Fork - LGPL
30995  * <script type="text/javascript">
30996  */
30997  
30998 /**
30999  * @class Roo.SplitButton
31000  * @extends Roo.Button
31001  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31002  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31003  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31004  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31005  * @cfg {String} arrowTooltip The title attribute of the arrow
31006  * @constructor
31007  * Create a new menu button
31008  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31009  * @param {Object} config The config object
31010  */
31011 Roo.SplitButton = function(renderTo, config){
31012     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31013     /**
31014      * @event arrowclick
31015      * Fires when this button's arrow is clicked
31016      * @param {SplitButton} this
31017      * @param {EventObject} e The click event
31018      */
31019     this.addEvents({"arrowclick":true});
31020 };
31021
31022 Roo.extend(Roo.SplitButton, Roo.Button, {
31023     render : function(renderTo){
31024         // this is one sweet looking template!
31025         var tpl = new Roo.Template(
31026             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31027             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31028             '<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>',
31029             "</tbody></table></td><td>",
31030             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31031             '<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>',
31032             "</tbody></table></td></tr></table>"
31033         );
31034         var btn = tpl.append(renderTo, [this.text, this.type], true);
31035         var btnEl = btn.child("button");
31036         if(this.cls){
31037             btn.addClass(this.cls);
31038         }
31039         if(this.icon){
31040             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31041         }
31042         if(this.iconCls){
31043             btnEl.addClass(this.iconCls);
31044             if(!this.cls){
31045                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31046             }
31047         }
31048         this.el = btn;
31049         if(this.handleMouseEvents){
31050             btn.on("mouseover", this.onMouseOver, this);
31051             btn.on("mouseout", this.onMouseOut, this);
31052             btn.on("mousedown", this.onMouseDown, this);
31053             btn.on("mouseup", this.onMouseUp, this);
31054         }
31055         btn.on(this.clickEvent, this.onClick, this);
31056         if(this.tooltip){
31057             if(typeof this.tooltip == 'object'){
31058                 Roo.QuickTips.tips(Roo.apply({
31059                       target: btnEl.id
31060                 }, this.tooltip));
31061             } else {
31062                 btnEl.dom[this.tooltipType] = this.tooltip;
31063             }
31064         }
31065         if(this.arrowTooltip){
31066             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31067         }
31068         if(this.hidden){
31069             this.hide();
31070         }
31071         if(this.disabled){
31072             this.disable();
31073         }
31074         if(this.pressed){
31075             this.el.addClass("x-btn-pressed");
31076         }
31077         if(Roo.isIE && !Roo.isIE7){
31078             this.autoWidth.defer(1, this);
31079         }else{
31080             this.autoWidth();
31081         }
31082         if(this.menu){
31083             this.menu.on("show", this.onMenuShow, this);
31084             this.menu.on("hide", this.onMenuHide, this);
31085         }
31086         this.fireEvent('render', this);
31087     },
31088
31089     // private
31090     autoWidth : function(){
31091         if(this.el){
31092             var tbl = this.el.child("table:first");
31093             var tbl2 = this.el.child("table:last");
31094             this.el.setWidth("auto");
31095             tbl.setWidth("auto");
31096             if(Roo.isIE7 && Roo.isStrict){
31097                 var ib = this.el.child('button:first');
31098                 if(ib && ib.getWidth() > 20){
31099                     ib.clip();
31100                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31101                 }
31102             }
31103             if(this.minWidth){
31104                 if(this.hidden){
31105                     this.el.beginMeasure();
31106                 }
31107                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31108                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31109                 }
31110                 if(this.hidden){
31111                     this.el.endMeasure();
31112                 }
31113             }
31114             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31115         } 
31116     },
31117     /**
31118      * Sets this button's click handler
31119      * @param {Function} handler The function to call when the button is clicked
31120      * @param {Object} scope (optional) Scope for the function passed above
31121      */
31122     setHandler : function(handler, scope){
31123         this.handler = handler;
31124         this.scope = scope;  
31125     },
31126     
31127     /**
31128      * Sets this button's arrow click handler
31129      * @param {Function} handler The function to call when the arrow is clicked
31130      * @param {Object} scope (optional) Scope for the function passed above
31131      */
31132     setArrowHandler : function(handler, scope){
31133         this.arrowHandler = handler;
31134         this.scope = scope;  
31135     },
31136     
31137     /**
31138      * Focus the button
31139      */
31140     focus : function(){
31141         if(this.el){
31142             this.el.child("button:first").focus();
31143         }
31144     },
31145
31146     // private
31147     onClick : function(e){
31148         e.preventDefault();
31149         if(!this.disabled){
31150             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31151                 if(this.menu && !this.menu.isVisible()){
31152                     this.menu.show(this.el, this.menuAlign);
31153                 }
31154                 this.fireEvent("arrowclick", this, e);
31155                 if(this.arrowHandler){
31156                     this.arrowHandler.call(this.scope || this, this, e);
31157                 }
31158             }else{
31159                 this.fireEvent("click", this, e);
31160                 if(this.handler){
31161                     this.handler.call(this.scope || this, this, e);
31162                 }
31163             }
31164         }
31165     },
31166     // private
31167     onMouseDown : function(e){
31168         if(!this.disabled){
31169             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31170         }
31171     },
31172     // private
31173     onMouseUp : function(e){
31174         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31175     }   
31176 });
31177
31178
31179 // backwards compat
31180 Roo.MenuButton = Roo.SplitButton;/*
31181  * Based on:
31182  * Ext JS Library 1.1.1
31183  * Copyright(c) 2006-2007, Ext JS, LLC.
31184  *
31185  * Originally Released Under LGPL - original licence link has changed is not relivant.
31186  *
31187  * Fork - LGPL
31188  * <script type="text/javascript">
31189  */
31190
31191 /**
31192  * @class Roo.Toolbar
31193  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31194  * Basic Toolbar class.
31195  * @constructor
31196  * Creates a new Toolbar
31197  * @param {Object} container The config object
31198  */ 
31199 Roo.Toolbar = function(container, buttons, config)
31200 {
31201     /// old consturctor format still supported..
31202     if(container instanceof Array){ // omit the container for later rendering
31203         buttons = container;
31204         config = buttons;
31205         container = null;
31206     }
31207     if (typeof(container) == 'object' && container.xtype) {
31208         config = container;
31209         container = config.container;
31210         buttons = config.buttons || []; // not really - use items!!
31211     }
31212     var xitems = [];
31213     if (config && config.items) {
31214         xitems = config.items;
31215         delete config.items;
31216     }
31217     Roo.apply(this, config);
31218     this.buttons = buttons;
31219     
31220     if(container){
31221         this.render(container);
31222     }
31223     this.xitems = xitems;
31224     Roo.each(xitems, function(b) {
31225         this.add(b);
31226     }, this);
31227     
31228 };
31229
31230 Roo.Toolbar.prototype = {
31231     /**
31232      * @cfg {Array} items
31233      * array of button configs or elements to add (will be converted to a MixedCollection)
31234      */
31235     items: false,
31236     /**
31237      * @cfg {String/HTMLElement/Element} container
31238      * The id or element that will contain the toolbar
31239      */
31240     // private
31241     render : function(ct){
31242         this.el = Roo.get(ct);
31243         if(this.cls){
31244             this.el.addClass(this.cls);
31245         }
31246         // using a table allows for vertical alignment
31247         // 100% width is needed by Safari...
31248         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31249         this.tr = this.el.child("tr", true);
31250         var autoId = 0;
31251         this.items = new Roo.util.MixedCollection(false, function(o){
31252             return o.id || ("item" + (++autoId));
31253         });
31254         if(this.buttons){
31255             this.add.apply(this, this.buttons);
31256             delete this.buttons;
31257         }
31258     },
31259
31260     /**
31261      * Adds element(s) to the toolbar -- this function takes a variable number of 
31262      * arguments of mixed type and adds them to the toolbar.
31263      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31264      * <ul>
31265      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31266      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31267      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31268      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31269      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31270      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31271      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31272      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31273      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31274      * </ul>
31275      * @param {Mixed} arg2
31276      * @param {Mixed} etc.
31277      */
31278     add : function(){
31279         var a = arguments, l = a.length;
31280         for(var i = 0; i < l; i++){
31281             this._add(a[i]);
31282         }
31283     },
31284     // private..
31285     _add : function(el) {
31286         
31287         if (el.xtype) {
31288             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31289         }
31290         
31291         if (el.applyTo){ // some kind of form field
31292             return this.addField(el);
31293         } 
31294         if (el.render){ // some kind of Toolbar.Item
31295             return this.addItem(el);
31296         }
31297         if (typeof el == "string"){ // string
31298             if(el == "separator" || el == "-"){
31299                 return this.addSeparator();
31300             }
31301             if (el == " "){
31302                 return this.addSpacer();
31303             }
31304             if(el == "->"){
31305                 return this.addFill();
31306             }
31307             return this.addText(el);
31308             
31309         }
31310         if(el.tagName){ // element
31311             return this.addElement(el);
31312         }
31313         if(typeof el == "object"){ // must be button config?
31314             return this.addButton(el);
31315         }
31316         // and now what?!?!
31317         return false;
31318         
31319     },
31320     
31321     /**
31322      * Add an Xtype element
31323      * @param {Object} xtype Xtype Object
31324      * @return {Object} created Object
31325      */
31326     addxtype : function(e){
31327         return this.add(e);  
31328     },
31329     
31330     /**
31331      * Returns the Element for this toolbar.
31332      * @return {Roo.Element}
31333      */
31334     getEl : function(){
31335         return this.el;  
31336     },
31337     
31338     /**
31339      * Adds a separator
31340      * @return {Roo.Toolbar.Item} The separator item
31341      */
31342     addSeparator : function(){
31343         return this.addItem(new Roo.Toolbar.Separator());
31344     },
31345
31346     /**
31347      * Adds a spacer element
31348      * @return {Roo.Toolbar.Spacer} The spacer item
31349      */
31350     addSpacer : function(){
31351         return this.addItem(new Roo.Toolbar.Spacer());
31352     },
31353
31354     /**
31355      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31356      * @return {Roo.Toolbar.Fill} The fill item
31357      */
31358     addFill : function(){
31359         return this.addItem(new Roo.Toolbar.Fill());
31360     },
31361
31362     /**
31363      * Adds any standard HTML element to the toolbar
31364      * @param {String/HTMLElement/Element} el The element or id of the element to add
31365      * @return {Roo.Toolbar.Item} The element's item
31366      */
31367     addElement : function(el){
31368         return this.addItem(new Roo.Toolbar.Item(el));
31369     },
31370     /**
31371      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31372      * @type Roo.util.MixedCollection  
31373      */
31374     items : false,
31375      
31376     /**
31377      * Adds any Toolbar.Item or subclass
31378      * @param {Roo.Toolbar.Item} item
31379      * @return {Roo.Toolbar.Item} The item
31380      */
31381     addItem : function(item){
31382         var td = this.nextBlock();
31383         item.render(td);
31384         this.items.add(item);
31385         return item;
31386     },
31387     
31388     /**
31389      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31390      * @param {Object/Array} config A button config or array of configs
31391      * @return {Roo.Toolbar.Button/Array}
31392      */
31393     addButton : function(config){
31394         if(config instanceof Array){
31395             var buttons = [];
31396             for(var i = 0, len = config.length; i < len; i++) {
31397                 buttons.push(this.addButton(config[i]));
31398             }
31399             return buttons;
31400         }
31401         var b = config;
31402         if(!(config instanceof Roo.Toolbar.Button)){
31403             b = config.split ?
31404                 new Roo.Toolbar.SplitButton(config) :
31405                 new Roo.Toolbar.Button(config);
31406         }
31407         var td = this.nextBlock();
31408         b.render(td);
31409         this.items.add(b);
31410         return b;
31411     },
31412     
31413     /**
31414      * Adds text to the toolbar
31415      * @param {String} text The text to add
31416      * @return {Roo.Toolbar.Item} The element's item
31417      */
31418     addText : function(text){
31419         return this.addItem(new Roo.Toolbar.TextItem(text));
31420     },
31421     
31422     /**
31423      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31424      * @param {Number} index The index where the item is to be inserted
31425      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31426      * @return {Roo.Toolbar.Button/Item}
31427      */
31428     insertButton : function(index, item){
31429         if(item instanceof Array){
31430             var buttons = [];
31431             for(var i = 0, len = item.length; i < len; i++) {
31432                buttons.push(this.insertButton(index + i, item[i]));
31433             }
31434             return buttons;
31435         }
31436         if (!(item instanceof Roo.Toolbar.Button)){
31437            item = new Roo.Toolbar.Button(item);
31438         }
31439         var td = document.createElement("td");
31440         this.tr.insertBefore(td, this.tr.childNodes[index]);
31441         item.render(td);
31442         this.items.insert(index, item);
31443         return item;
31444     },
31445     
31446     /**
31447      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31448      * @param {Object} config
31449      * @return {Roo.Toolbar.Item} The element's item
31450      */
31451     addDom : function(config, returnEl){
31452         var td = this.nextBlock();
31453         Roo.DomHelper.overwrite(td, config);
31454         var ti = new Roo.Toolbar.Item(td.firstChild);
31455         ti.render(td);
31456         this.items.add(ti);
31457         return ti;
31458     },
31459
31460     /**
31461      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31462      * @type Roo.util.MixedCollection  
31463      */
31464     fields : false,
31465     
31466     /**
31467      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31468      * Note: the field should not have been rendered yet. For a field that has already been
31469      * rendered, use {@link #addElement}.
31470      * @param {Roo.form.Field} field
31471      * @return {Roo.ToolbarItem}
31472      */
31473      
31474       
31475     addField : function(field) {
31476         if (!this.fields) {
31477             var autoId = 0;
31478             this.fields = new Roo.util.MixedCollection(false, function(o){
31479                 return o.id || ("item" + (++autoId));
31480             });
31481
31482         }
31483         
31484         var td = this.nextBlock();
31485         field.render(td);
31486         var ti = new Roo.Toolbar.Item(td.firstChild);
31487         ti.render(td);
31488         this.items.add(ti);
31489         this.fields.add(field);
31490         return ti;
31491     },
31492     /**
31493      * Hide the toolbar
31494      * @method hide
31495      */
31496      
31497       
31498     hide : function()
31499     {
31500         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31501         this.el.child('div').hide();
31502     },
31503     /**
31504      * Show the toolbar
31505      * @method show
31506      */
31507     show : function()
31508     {
31509         this.el.child('div').show();
31510     },
31511       
31512     // private
31513     nextBlock : function(){
31514         var td = document.createElement("td");
31515         this.tr.appendChild(td);
31516         return td;
31517     },
31518
31519     // private
31520     destroy : function(){
31521         if(this.items){ // rendered?
31522             Roo.destroy.apply(Roo, this.items.items);
31523         }
31524         if(this.fields){ // rendered?
31525             Roo.destroy.apply(Roo, this.fields.items);
31526         }
31527         Roo.Element.uncache(this.el, this.tr);
31528     }
31529 };
31530
31531 /**
31532  * @class Roo.Toolbar.Item
31533  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31534  * @constructor
31535  * Creates a new Item
31536  * @param {HTMLElement} el 
31537  */
31538 Roo.Toolbar.Item = function(el){
31539     var cfg = {};
31540     if (typeof (el.xtype) != 'undefined') {
31541         cfg = el;
31542         el = cfg.el;
31543     }
31544     
31545     this.el = Roo.getDom(el);
31546     this.id = Roo.id(this.el);
31547     this.hidden = false;
31548     
31549     this.addEvents({
31550          /**
31551              * @event render
31552              * Fires when the button is rendered
31553              * @param {Button} this
31554              */
31555         'render': true
31556     });
31557     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31558 };
31559 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31560 //Roo.Toolbar.Item.prototype = {
31561     
31562     /**
31563      * Get this item's HTML Element
31564      * @return {HTMLElement}
31565      */
31566     getEl : function(){
31567        return this.el;  
31568     },
31569
31570     // private
31571     render : function(td){
31572         
31573          this.td = td;
31574         td.appendChild(this.el);
31575         
31576         this.fireEvent('render', this);
31577     },
31578     
31579     /**
31580      * Removes and destroys this item.
31581      */
31582     destroy : function(){
31583         this.td.parentNode.removeChild(this.td);
31584     },
31585     
31586     /**
31587      * Shows this item.
31588      */
31589     show: function(){
31590         this.hidden = false;
31591         this.td.style.display = "";
31592     },
31593     
31594     /**
31595      * Hides this item.
31596      */
31597     hide: function(){
31598         this.hidden = true;
31599         this.td.style.display = "none";
31600     },
31601     
31602     /**
31603      * Convenience function for boolean show/hide.
31604      * @param {Boolean} visible true to show/false to hide
31605      */
31606     setVisible: function(visible){
31607         if(visible) {
31608             this.show();
31609         }else{
31610             this.hide();
31611         }
31612     },
31613     
31614     /**
31615      * Try to focus this item.
31616      */
31617     focus : function(){
31618         Roo.fly(this.el).focus();
31619     },
31620     
31621     /**
31622      * Disables this item.
31623      */
31624     disable : function(){
31625         Roo.fly(this.td).addClass("x-item-disabled");
31626         this.disabled = true;
31627         this.el.disabled = true;
31628     },
31629     
31630     /**
31631      * Enables this item.
31632      */
31633     enable : function(){
31634         Roo.fly(this.td).removeClass("x-item-disabled");
31635         this.disabled = false;
31636         this.el.disabled = false;
31637     }
31638 });
31639
31640
31641 /**
31642  * @class Roo.Toolbar.Separator
31643  * @extends Roo.Toolbar.Item
31644  * A simple toolbar separator class
31645  * @constructor
31646  * Creates a new Separator
31647  */
31648 Roo.Toolbar.Separator = function(cfg){
31649     
31650     var s = document.createElement("span");
31651     s.className = "ytb-sep";
31652     if (cfg) {
31653         cfg.el = s;
31654     }
31655     
31656     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31657 };
31658 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31659     enable:Roo.emptyFn,
31660     disable:Roo.emptyFn,
31661     focus:Roo.emptyFn
31662 });
31663
31664 /**
31665  * @class Roo.Toolbar.Spacer
31666  * @extends Roo.Toolbar.Item
31667  * A simple element that adds extra horizontal space to a toolbar.
31668  * @constructor
31669  * Creates a new Spacer
31670  */
31671 Roo.Toolbar.Spacer = function(cfg){
31672     var s = document.createElement("div");
31673     s.className = "ytb-spacer";
31674     if (cfg) {
31675         cfg.el = s;
31676     }
31677     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31678 };
31679 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31680     enable:Roo.emptyFn,
31681     disable:Roo.emptyFn,
31682     focus:Roo.emptyFn
31683 });
31684
31685 /**
31686  * @class Roo.Toolbar.Fill
31687  * @extends Roo.Toolbar.Spacer
31688  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31689  * @constructor
31690  * Creates a new Spacer
31691  */
31692 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31693     // private
31694     render : function(td){
31695         td.style.width = '100%';
31696         Roo.Toolbar.Fill.superclass.render.call(this, td);
31697     }
31698 });
31699
31700 /**
31701  * @class Roo.Toolbar.TextItem
31702  * @extends Roo.Toolbar.Item
31703  * A simple class that renders text directly into a toolbar.
31704  * @constructor
31705  * Creates a new TextItem
31706  * @cfg {string} text 
31707  */
31708 Roo.Toolbar.TextItem = function(cfg){
31709     var  text = cfg || "";
31710     if (typeof(cfg) == 'object') {
31711         text = cfg.text || "";
31712     }  else {
31713         cfg = null;
31714     }
31715     var s = document.createElement("span");
31716     s.className = "ytb-text";
31717     s.innerHTML = text;
31718     if (cfg) {
31719         cfg.el  = s;
31720     }
31721     
31722     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31723 };
31724 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31725     
31726      
31727     enable:Roo.emptyFn,
31728     disable:Roo.emptyFn,
31729     focus:Roo.emptyFn,
31730      /**
31731      * Shows this button
31732      */
31733     show: function(){
31734         this.hidden = false;
31735         this.el.style.display = "";
31736     },
31737     
31738     /**
31739      * Hides this button
31740      */
31741     hide: function(){
31742         this.hidden = true;
31743         this.el.style.display = "none";
31744     }
31745     
31746 });
31747
31748 /**
31749  * @class Roo.Toolbar.Button
31750  * @extends Roo.Button
31751  * A button that renders into a toolbar.
31752  * @constructor
31753  * Creates a new Button
31754  * @param {Object} config A standard {@link Roo.Button} config object
31755  */
31756 Roo.Toolbar.Button = function(config){
31757     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31758 };
31759 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31760 {
31761     
31762     
31763     render : function(td){
31764         this.td = td;
31765         Roo.Toolbar.Button.superclass.render.call(this, td);
31766     },
31767     
31768     /**
31769      * Removes and destroys this button
31770      */
31771     destroy : function(){
31772         Roo.Toolbar.Button.superclass.destroy.call(this);
31773         this.td.parentNode.removeChild(this.td);
31774     },
31775     
31776     /**
31777      * Shows this button
31778      */
31779     show: function(){
31780         this.hidden = false;
31781         this.td.style.display = "";
31782     },
31783     
31784     /**
31785      * Hides this button
31786      */
31787     hide: function(){
31788         this.hidden = true;
31789         this.td.style.display = "none";
31790     },
31791
31792     /**
31793      * Disables this item
31794      */
31795     disable : function(){
31796         Roo.fly(this.td).addClass("x-item-disabled");
31797         this.disabled = true;
31798     },
31799
31800     /**
31801      * Enables this item
31802      */
31803     enable : function(){
31804         Roo.fly(this.td).removeClass("x-item-disabled");
31805         this.disabled = false;
31806     }
31807 });
31808 // backwards compat
31809 Roo.ToolbarButton = Roo.Toolbar.Button;
31810
31811 /**
31812  * @class Roo.Toolbar.SplitButton
31813  * @extends Roo.SplitButton
31814  * A menu button that renders into a toolbar.
31815  * @constructor
31816  * Creates a new SplitButton
31817  * @param {Object} config A standard {@link Roo.SplitButton} config object
31818  */
31819 Roo.Toolbar.SplitButton = function(config){
31820     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31821 };
31822 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31823     render : function(td){
31824         this.td = td;
31825         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31826     },
31827     
31828     /**
31829      * Removes and destroys this button
31830      */
31831     destroy : function(){
31832         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31833         this.td.parentNode.removeChild(this.td);
31834     },
31835     
31836     /**
31837      * Shows this button
31838      */
31839     show: function(){
31840         this.hidden = false;
31841         this.td.style.display = "";
31842     },
31843     
31844     /**
31845      * Hides this button
31846      */
31847     hide: function(){
31848         this.hidden = true;
31849         this.td.style.display = "none";
31850     }
31851 });
31852
31853 // backwards compat
31854 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31855  * Based on:
31856  * Ext JS Library 1.1.1
31857  * Copyright(c) 2006-2007, Ext JS, LLC.
31858  *
31859  * Originally Released Under LGPL - original licence link has changed is not relivant.
31860  *
31861  * Fork - LGPL
31862  * <script type="text/javascript">
31863  */
31864  
31865 /**
31866  * @class Roo.PagingToolbar
31867  * @extends Roo.Toolbar
31868  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31869  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31870  * @constructor
31871  * Create a new PagingToolbar
31872  * @param {Object} config The config object
31873  */
31874 Roo.PagingToolbar = function(el, ds, config)
31875 {
31876     // old args format still supported... - xtype is prefered..
31877     if (typeof(el) == 'object' && el.xtype) {
31878         // created from xtype...
31879         config = el;
31880         ds = el.dataSource;
31881         el = config.container;
31882     }
31883     var items = [];
31884     if (config.items) {
31885         items = config.items;
31886         config.items = [];
31887     }
31888     
31889     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31890     this.ds = ds;
31891     this.cursor = 0;
31892     this.renderButtons(this.el);
31893     this.bind(ds);
31894     
31895     // supprot items array.
31896    
31897     Roo.each(items, function(e) {
31898         this.add(Roo.factory(e));
31899     },this);
31900     
31901 };
31902
31903 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31904    
31905     /**
31906      * @cfg {String/HTMLElement/Element} container
31907      * container The id or element that will contain the toolbar
31908      */
31909     /**
31910      * @cfg {Boolean} displayInfo
31911      * True to display the displayMsg (defaults to false)
31912      */
31913     
31914     
31915     /**
31916      * @cfg {Number} pageSize
31917      * The number of records to display per page (defaults to 20)
31918      */
31919     pageSize: 20,
31920     /**
31921      * @cfg {String} displayMsg
31922      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31923      */
31924     displayMsg : 'Displaying {0} - {1} of {2}',
31925     /**
31926      * @cfg {String} emptyMsg
31927      * The message to display when no records are found (defaults to "No data to display")
31928      */
31929     emptyMsg : 'No data to display',
31930     /**
31931      * Customizable piece of the default paging text (defaults to "Page")
31932      * @type String
31933      */
31934     beforePageText : "Page",
31935     /**
31936      * Customizable piece of the default paging text (defaults to "of %0")
31937      * @type String
31938      */
31939     afterPageText : "of {0}",
31940     /**
31941      * Customizable piece of the default paging text (defaults to "First Page")
31942      * @type String
31943      */
31944     firstText : "First Page",
31945     /**
31946      * Customizable piece of the default paging text (defaults to "Previous Page")
31947      * @type String
31948      */
31949     prevText : "Previous Page",
31950     /**
31951      * Customizable piece of the default paging text (defaults to "Next Page")
31952      * @type String
31953      */
31954     nextText : "Next Page",
31955     /**
31956      * Customizable piece of the default paging text (defaults to "Last Page")
31957      * @type String
31958      */
31959     lastText : "Last Page",
31960     /**
31961      * Customizable piece of the default paging text (defaults to "Refresh")
31962      * @type String
31963      */
31964     refreshText : "Refresh",
31965
31966     // private
31967     renderButtons : function(el){
31968         Roo.PagingToolbar.superclass.render.call(this, el);
31969         this.first = this.addButton({
31970             tooltip: this.firstText,
31971             cls: "x-btn-icon x-grid-page-first",
31972             disabled: true,
31973             handler: this.onClick.createDelegate(this, ["first"])
31974         });
31975         this.prev = this.addButton({
31976             tooltip: this.prevText,
31977             cls: "x-btn-icon x-grid-page-prev",
31978             disabled: true,
31979             handler: this.onClick.createDelegate(this, ["prev"])
31980         });
31981         //this.addSeparator();
31982         this.add(this.beforePageText);
31983         this.field = Roo.get(this.addDom({
31984            tag: "input",
31985            type: "text",
31986            size: "3",
31987            value: "1",
31988            cls: "x-grid-page-number"
31989         }).el);
31990         this.field.on("keydown", this.onPagingKeydown, this);
31991         this.field.on("focus", function(){this.dom.select();});
31992         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31993         this.field.setHeight(18);
31994         //this.addSeparator();
31995         this.next = this.addButton({
31996             tooltip: this.nextText,
31997             cls: "x-btn-icon x-grid-page-next",
31998             disabled: true,
31999             handler: this.onClick.createDelegate(this, ["next"])
32000         });
32001         this.last = this.addButton({
32002             tooltip: this.lastText,
32003             cls: "x-btn-icon x-grid-page-last",
32004             disabled: true,
32005             handler: this.onClick.createDelegate(this, ["last"])
32006         });
32007         //this.addSeparator();
32008         this.loading = this.addButton({
32009             tooltip: this.refreshText,
32010             cls: "x-btn-icon x-grid-loading",
32011             handler: this.onClick.createDelegate(this, ["refresh"])
32012         });
32013
32014         if(this.displayInfo){
32015             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32016         }
32017     },
32018
32019     // private
32020     updateInfo : function(){
32021         if(this.displayEl){
32022             var count = this.ds.getCount();
32023             var msg = count == 0 ?
32024                 this.emptyMsg :
32025                 String.format(
32026                     this.displayMsg,
32027                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32028                 );
32029             this.displayEl.update(msg);
32030         }
32031     },
32032
32033     // private
32034     onLoad : function(ds, r, o){
32035        this.cursor = o.params ? o.params.start : 0;
32036        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32037
32038        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32039        this.field.dom.value = ap;
32040        this.first.setDisabled(ap == 1);
32041        this.prev.setDisabled(ap == 1);
32042        this.next.setDisabled(ap == ps);
32043        this.last.setDisabled(ap == ps);
32044        this.loading.enable();
32045        this.updateInfo();
32046     },
32047
32048     // private
32049     getPageData : function(){
32050         var total = this.ds.getTotalCount();
32051         return {
32052             total : total,
32053             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32054             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32055         };
32056     },
32057
32058     // private
32059     onLoadError : function(){
32060         this.loading.enable();
32061     },
32062
32063     // private
32064     onPagingKeydown : function(e){
32065         var k = e.getKey();
32066         var d = this.getPageData();
32067         if(k == e.RETURN){
32068             var v = this.field.dom.value, pageNum;
32069             if(!v || isNaN(pageNum = parseInt(v, 10))){
32070                 this.field.dom.value = d.activePage;
32071                 return;
32072             }
32073             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32074             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32075             e.stopEvent();
32076         }
32077         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))
32078         {
32079           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32080           this.field.dom.value = pageNum;
32081           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32082           e.stopEvent();
32083         }
32084         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32085         {
32086           var v = this.field.dom.value, pageNum; 
32087           var increment = (e.shiftKey) ? 10 : 1;
32088           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32089             increment *= -1;
32090           }
32091           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32092             this.field.dom.value = d.activePage;
32093             return;
32094           }
32095           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32096           {
32097             this.field.dom.value = parseInt(v, 10) + increment;
32098             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32099             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32100           }
32101           e.stopEvent();
32102         }
32103     },
32104
32105     // private
32106     beforeLoad : function(){
32107         if(this.loading){
32108             this.loading.disable();
32109         }
32110     },
32111
32112     // private
32113     onClick : function(which){
32114         var ds = this.ds;
32115         switch(which){
32116             case "first":
32117                 ds.load({params:{start: 0, limit: this.pageSize}});
32118             break;
32119             case "prev":
32120                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32121             break;
32122             case "next":
32123                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32124             break;
32125             case "last":
32126                 var total = ds.getTotalCount();
32127                 var extra = total % this.pageSize;
32128                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32129                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32130             break;
32131             case "refresh":
32132                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32133             break;
32134         }
32135     },
32136
32137     /**
32138      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32139      * @param {Roo.data.Store} store The data store to unbind
32140      */
32141     unbind : function(ds){
32142         ds.un("beforeload", this.beforeLoad, this);
32143         ds.un("load", this.onLoad, this);
32144         ds.un("loadexception", this.onLoadError, this);
32145         ds.un("remove", this.updateInfo, this);
32146         ds.un("add", this.updateInfo, this);
32147         this.ds = undefined;
32148     },
32149
32150     /**
32151      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32152      * @param {Roo.data.Store} store The data store to bind
32153      */
32154     bind : function(ds){
32155         ds.on("beforeload", this.beforeLoad, this);
32156         ds.on("load", this.onLoad, this);
32157         ds.on("loadexception", this.onLoadError, this);
32158         ds.on("remove", this.updateInfo, this);
32159         ds.on("add", this.updateInfo, this);
32160         this.ds = ds;
32161     }
32162 });/*
32163  * Based on:
32164  * Ext JS Library 1.1.1
32165  * Copyright(c) 2006-2007, Ext JS, LLC.
32166  *
32167  * Originally Released Under LGPL - original licence link has changed is not relivant.
32168  *
32169  * Fork - LGPL
32170  * <script type="text/javascript">
32171  */
32172
32173 /**
32174  * @class Roo.Resizable
32175  * @extends Roo.util.Observable
32176  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32177  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32178  * 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
32179  * the element will be wrapped for you automatically.</p>
32180  * <p>Here is the list of valid resize handles:</p>
32181  * <pre>
32182 Value   Description
32183 ------  -------------------
32184  'n'     north
32185  's'     south
32186  'e'     east
32187  'w'     west
32188  'nw'    northwest
32189  'sw'    southwest
32190  'se'    southeast
32191  'ne'    northeast
32192  'hd'    horizontal drag
32193  'all'   all
32194 </pre>
32195  * <p>Here's an example showing the creation of a typical Resizable:</p>
32196  * <pre><code>
32197 var resizer = new Roo.Resizable("element-id", {
32198     handles: 'all',
32199     minWidth: 200,
32200     minHeight: 100,
32201     maxWidth: 500,
32202     maxHeight: 400,
32203     pinned: true
32204 });
32205 resizer.on("resize", myHandler);
32206 </code></pre>
32207  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32208  * resizer.east.setDisplayed(false);</p>
32209  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32210  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32211  * resize operation's new size (defaults to [0, 0])
32212  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32213  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32214  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32215  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32216  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32217  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32218  * @cfg {Number} width The width of the element in pixels (defaults to null)
32219  * @cfg {Number} height The height of the element in pixels (defaults to null)
32220  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32221  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32222  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32223  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32224  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32225  * in favor of the handles config option (defaults to false)
32226  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32227  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32228  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32229  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32230  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32231  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32232  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32233  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32234  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32235  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32236  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32237  * @constructor
32238  * Create a new resizable component
32239  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32240  * @param {Object} config configuration options
32241   */
32242 Roo.Resizable = function(el, config)
32243 {
32244     this.el = Roo.get(el);
32245
32246     if(config && config.wrap){
32247         config.resizeChild = this.el;
32248         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32249         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32250         this.el.setStyle("overflow", "hidden");
32251         this.el.setPositioning(config.resizeChild.getPositioning());
32252         config.resizeChild.clearPositioning();
32253         if(!config.width || !config.height){
32254             var csize = config.resizeChild.getSize();
32255             this.el.setSize(csize.width, csize.height);
32256         }
32257         if(config.pinned && !config.adjustments){
32258             config.adjustments = "auto";
32259         }
32260     }
32261
32262     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32263     this.proxy.unselectable();
32264     this.proxy.enableDisplayMode('block');
32265
32266     Roo.apply(this, config);
32267
32268     if(this.pinned){
32269         this.disableTrackOver = true;
32270         this.el.addClass("x-resizable-pinned");
32271     }
32272     // if the element isn't positioned, make it relative
32273     var position = this.el.getStyle("position");
32274     if(position != "absolute" && position != "fixed"){
32275         this.el.setStyle("position", "relative");
32276     }
32277     if(!this.handles){ // no handles passed, must be legacy style
32278         this.handles = 's,e,se';
32279         if(this.multiDirectional){
32280             this.handles += ',n,w';
32281         }
32282     }
32283     if(this.handles == "all"){
32284         this.handles = "n s e w ne nw se sw";
32285     }
32286     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32287     var ps = Roo.Resizable.positions;
32288     for(var i = 0, len = hs.length; i < len; i++){
32289         if(hs[i] && ps[hs[i]]){
32290             var pos = ps[hs[i]];
32291             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32292         }
32293     }
32294     // legacy
32295     this.corner = this.southeast;
32296     
32297     // updateBox = the box can move..
32298     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32299         this.updateBox = true;
32300     }
32301
32302     this.activeHandle = null;
32303
32304     if(this.resizeChild){
32305         if(typeof this.resizeChild == "boolean"){
32306             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32307         }else{
32308             this.resizeChild = Roo.get(this.resizeChild, true);
32309         }
32310     }
32311     
32312     if(this.adjustments == "auto"){
32313         var rc = this.resizeChild;
32314         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32315         if(rc && (hw || hn)){
32316             rc.position("relative");
32317             rc.setLeft(hw ? hw.el.getWidth() : 0);
32318             rc.setTop(hn ? hn.el.getHeight() : 0);
32319         }
32320         this.adjustments = [
32321             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32322             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32323         ];
32324     }
32325
32326     if(this.draggable){
32327         this.dd = this.dynamic ?
32328             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32329         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32330     }
32331
32332     // public events
32333     this.addEvents({
32334         /**
32335          * @event beforeresize
32336          * Fired before resize is allowed. Set enabled to false to cancel resize.
32337          * @param {Roo.Resizable} this
32338          * @param {Roo.EventObject} e The mousedown event
32339          */
32340         "beforeresize" : true,
32341         /**
32342          * @event resizing
32343          * Fired a resizing.
32344          * @param {Roo.Resizable} this
32345          * @param {Number} x The new x position
32346          * @param {Number} y The new y position
32347          * @param {Number} w The new w width
32348          * @param {Number} h The new h hight
32349          * @param {Roo.EventObject} e The mouseup event
32350          */
32351         "resizing" : true,
32352         /**
32353          * @event resize
32354          * Fired after a resize.
32355          * @param {Roo.Resizable} this
32356          * @param {Number} width The new width
32357          * @param {Number} height The new height
32358          * @param {Roo.EventObject} e The mouseup event
32359          */
32360         "resize" : true
32361     });
32362
32363     if(this.width !== null && this.height !== null){
32364         this.resizeTo(this.width, this.height);
32365     }else{
32366         this.updateChildSize();
32367     }
32368     if(Roo.isIE){
32369         this.el.dom.style.zoom = 1;
32370     }
32371     Roo.Resizable.superclass.constructor.call(this);
32372 };
32373
32374 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32375         resizeChild : false,
32376         adjustments : [0, 0],
32377         minWidth : 5,
32378         minHeight : 5,
32379         maxWidth : 10000,
32380         maxHeight : 10000,
32381         enabled : true,
32382         animate : false,
32383         duration : .35,
32384         dynamic : false,
32385         handles : false,
32386         multiDirectional : false,
32387         disableTrackOver : false,
32388         easing : 'easeOutStrong',
32389         widthIncrement : 0,
32390         heightIncrement : 0,
32391         pinned : false,
32392         width : null,
32393         height : null,
32394         preserveRatio : false,
32395         transparent: false,
32396         minX: 0,
32397         minY: 0,
32398         draggable: false,
32399
32400         /**
32401          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32402          */
32403         constrainTo: undefined,
32404         /**
32405          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32406          */
32407         resizeRegion: undefined,
32408
32409
32410     /**
32411      * Perform a manual resize
32412      * @param {Number} width
32413      * @param {Number} height
32414      */
32415     resizeTo : function(width, height){
32416         this.el.setSize(width, height);
32417         this.updateChildSize();
32418         this.fireEvent("resize", this, width, height, null);
32419     },
32420
32421     // private
32422     startSizing : function(e, handle){
32423         this.fireEvent("beforeresize", this, e);
32424         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32425
32426             if(!this.overlay){
32427                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32428                 this.overlay.unselectable();
32429                 this.overlay.enableDisplayMode("block");
32430                 this.overlay.on("mousemove", this.onMouseMove, this);
32431                 this.overlay.on("mouseup", this.onMouseUp, this);
32432             }
32433             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32434
32435             this.resizing = true;
32436             this.startBox = this.el.getBox();
32437             this.startPoint = e.getXY();
32438             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32439                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32440
32441             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32442             this.overlay.show();
32443
32444             if(this.constrainTo) {
32445                 var ct = Roo.get(this.constrainTo);
32446                 this.resizeRegion = ct.getRegion().adjust(
32447                     ct.getFrameWidth('t'),
32448                     ct.getFrameWidth('l'),
32449                     -ct.getFrameWidth('b'),
32450                     -ct.getFrameWidth('r')
32451                 );
32452             }
32453
32454             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32455             this.proxy.show();
32456             this.proxy.setBox(this.startBox);
32457             if(!this.dynamic){
32458                 this.proxy.setStyle('visibility', 'visible');
32459             }
32460         }
32461     },
32462
32463     // private
32464     onMouseDown : function(handle, e){
32465         if(this.enabled){
32466             e.stopEvent();
32467             this.activeHandle = handle;
32468             this.startSizing(e, handle);
32469         }
32470     },
32471
32472     // private
32473     onMouseUp : function(e){
32474         var size = this.resizeElement();
32475         this.resizing = false;
32476         this.handleOut();
32477         this.overlay.hide();
32478         this.proxy.hide();
32479         this.fireEvent("resize", this, size.width, size.height, e);
32480     },
32481
32482     // private
32483     updateChildSize : function(){
32484         
32485         if(this.resizeChild){
32486             var el = this.el;
32487             var child = this.resizeChild;
32488             var adj = this.adjustments;
32489             if(el.dom.offsetWidth){
32490                 var b = el.getSize(true);
32491                 child.setSize(b.width+adj[0], b.height+adj[1]);
32492             }
32493             // Second call here for IE
32494             // The first call enables instant resizing and
32495             // the second call corrects scroll bars if they
32496             // exist
32497             if(Roo.isIE){
32498                 setTimeout(function(){
32499                     if(el.dom.offsetWidth){
32500                         var b = el.getSize(true);
32501                         child.setSize(b.width+adj[0], b.height+adj[1]);
32502                     }
32503                 }, 10);
32504             }
32505         }
32506     },
32507
32508     // private
32509     snap : function(value, inc, min){
32510         if(!inc || !value) {
32511             return value;
32512         }
32513         var newValue = value;
32514         var m = value % inc;
32515         if(m > 0){
32516             if(m > (inc/2)){
32517                 newValue = value + (inc-m);
32518             }else{
32519                 newValue = value - m;
32520             }
32521         }
32522         return Math.max(min, newValue);
32523     },
32524
32525     // private
32526     resizeElement : function(){
32527         var box = this.proxy.getBox();
32528         if(this.updateBox){
32529             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32530         }else{
32531             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32532         }
32533         this.updateChildSize();
32534         if(!this.dynamic){
32535             this.proxy.hide();
32536         }
32537         return box;
32538     },
32539
32540     // private
32541     constrain : function(v, diff, m, mx){
32542         if(v - diff < m){
32543             diff = v - m;
32544         }else if(v - diff > mx){
32545             diff = mx - v;
32546         }
32547         return diff;
32548     },
32549
32550     // private
32551     onMouseMove : function(e){
32552         
32553         if(this.enabled){
32554             try{// try catch so if something goes wrong the user doesn't get hung
32555
32556             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32557                 return;
32558             }
32559
32560             //var curXY = this.startPoint;
32561             var curSize = this.curSize || this.startBox;
32562             var x = this.startBox.x, y = this.startBox.y;
32563             var ox = x, oy = y;
32564             var w = curSize.width, h = curSize.height;
32565             var ow = w, oh = h;
32566             var mw = this.minWidth, mh = this.minHeight;
32567             var mxw = this.maxWidth, mxh = this.maxHeight;
32568             var wi = this.widthIncrement;
32569             var hi = this.heightIncrement;
32570
32571             var eventXY = e.getXY();
32572             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32573             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32574
32575             var pos = this.activeHandle.position;
32576
32577             switch(pos){
32578                 case "east":
32579                     w += diffX;
32580                     w = Math.min(Math.max(mw, w), mxw);
32581                     break;
32582              
32583                 case "south":
32584                     h += diffY;
32585                     h = Math.min(Math.max(mh, h), mxh);
32586                     break;
32587                 case "southeast":
32588                     w += diffX;
32589                     h += diffY;
32590                     w = Math.min(Math.max(mw, w), mxw);
32591                     h = Math.min(Math.max(mh, h), mxh);
32592                     break;
32593                 case "north":
32594                     diffY = this.constrain(h, diffY, mh, mxh);
32595                     y += diffY;
32596                     h -= diffY;
32597                     break;
32598                 case "hdrag":
32599                     
32600                     if (wi) {
32601                         var adiffX = Math.abs(diffX);
32602                         var sub = (adiffX % wi); // how much 
32603                         if (sub > (wi/2)) { // far enough to snap
32604                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32605                         } else {
32606                             // remove difference.. 
32607                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32608                         }
32609                     }
32610                     x += diffX;
32611                     x = Math.max(this.minX, x);
32612                     break;
32613                 case "west":
32614                     diffX = this.constrain(w, diffX, mw, mxw);
32615                     x += diffX;
32616                     w -= diffX;
32617                     break;
32618                 case "northeast":
32619                     w += diffX;
32620                     w = Math.min(Math.max(mw, w), mxw);
32621                     diffY = this.constrain(h, diffY, mh, mxh);
32622                     y += diffY;
32623                     h -= diffY;
32624                     break;
32625                 case "northwest":
32626                     diffX = this.constrain(w, diffX, mw, mxw);
32627                     diffY = this.constrain(h, diffY, mh, mxh);
32628                     y += diffY;
32629                     h -= diffY;
32630                     x += diffX;
32631                     w -= diffX;
32632                     break;
32633                case "southwest":
32634                     diffX = this.constrain(w, diffX, mw, mxw);
32635                     h += diffY;
32636                     h = Math.min(Math.max(mh, h), mxh);
32637                     x += diffX;
32638                     w -= diffX;
32639                     break;
32640             }
32641
32642             var sw = this.snap(w, wi, mw);
32643             var sh = this.snap(h, hi, mh);
32644             if(sw != w || sh != h){
32645                 switch(pos){
32646                     case "northeast":
32647                         y -= sh - h;
32648                     break;
32649                     case "north":
32650                         y -= sh - h;
32651                         break;
32652                     case "southwest":
32653                         x -= sw - w;
32654                     break;
32655                     case "west":
32656                         x -= sw - w;
32657                         break;
32658                     case "northwest":
32659                         x -= sw - w;
32660                         y -= sh - h;
32661                     break;
32662                 }
32663                 w = sw;
32664                 h = sh;
32665             }
32666
32667             if(this.preserveRatio){
32668                 switch(pos){
32669                     case "southeast":
32670                     case "east":
32671                         h = oh * (w/ow);
32672                         h = Math.min(Math.max(mh, h), mxh);
32673                         w = ow * (h/oh);
32674                        break;
32675                     case "south":
32676                         w = ow * (h/oh);
32677                         w = Math.min(Math.max(mw, w), mxw);
32678                         h = oh * (w/ow);
32679                         break;
32680                     case "northeast":
32681                         w = ow * (h/oh);
32682                         w = Math.min(Math.max(mw, w), mxw);
32683                         h = oh * (w/ow);
32684                     break;
32685                     case "north":
32686                         var tw = w;
32687                         w = ow * (h/oh);
32688                         w = Math.min(Math.max(mw, w), mxw);
32689                         h = oh * (w/ow);
32690                         x += (tw - w) / 2;
32691                         break;
32692                     case "southwest":
32693                         h = oh * (w/ow);
32694                         h = Math.min(Math.max(mh, h), mxh);
32695                         var tw = w;
32696                         w = ow * (h/oh);
32697                         x += tw - w;
32698                         break;
32699                     case "west":
32700                         var th = h;
32701                         h = oh * (w/ow);
32702                         h = Math.min(Math.max(mh, h), mxh);
32703                         y += (th - h) / 2;
32704                         var tw = w;
32705                         w = ow * (h/oh);
32706                         x += tw - w;
32707                        break;
32708                     case "northwest":
32709                         var tw = w;
32710                         var th = h;
32711                         h = oh * (w/ow);
32712                         h = Math.min(Math.max(mh, h), mxh);
32713                         w = ow * (h/oh);
32714                         y += th - h;
32715                         x += tw - w;
32716                        break;
32717
32718                 }
32719             }
32720             if (pos == 'hdrag') {
32721                 w = ow;
32722             }
32723             this.proxy.setBounds(x, y, w, h);
32724             if(this.dynamic){
32725                 this.resizeElement();
32726             }
32727             }catch(e){}
32728         }
32729         this.fireEvent("resizing", this, x, y, w, h, e);
32730     },
32731
32732     // private
32733     handleOver : function(){
32734         if(this.enabled){
32735             this.el.addClass("x-resizable-over");
32736         }
32737     },
32738
32739     // private
32740     handleOut : function(){
32741         if(!this.resizing){
32742             this.el.removeClass("x-resizable-over");
32743         }
32744     },
32745
32746     /**
32747      * Returns the element this component is bound to.
32748      * @return {Roo.Element}
32749      */
32750     getEl : function(){
32751         return this.el;
32752     },
32753
32754     /**
32755      * Returns the resizeChild element (or null).
32756      * @return {Roo.Element}
32757      */
32758     getResizeChild : function(){
32759         return this.resizeChild;
32760     },
32761     groupHandler : function()
32762     {
32763         
32764     },
32765     /**
32766      * Destroys this resizable. If the element was wrapped and
32767      * removeEl is not true then the element remains.
32768      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32769      */
32770     destroy : function(removeEl){
32771         this.proxy.remove();
32772         if(this.overlay){
32773             this.overlay.removeAllListeners();
32774             this.overlay.remove();
32775         }
32776         var ps = Roo.Resizable.positions;
32777         for(var k in ps){
32778             if(typeof ps[k] != "function" && this[ps[k]]){
32779                 var h = this[ps[k]];
32780                 h.el.removeAllListeners();
32781                 h.el.remove();
32782             }
32783         }
32784         if(removeEl){
32785             this.el.update("");
32786             this.el.remove();
32787         }
32788     }
32789 });
32790
32791 // private
32792 // hash to map config positions to true positions
32793 Roo.Resizable.positions = {
32794     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32795     hd: "hdrag"
32796 };
32797
32798 // private
32799 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32800     if(!this.tpl){
32801         // only initialize the template if resizable is used
32802         var tpl = Roo.DomHelper.createTemplate(
32803             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32804         );
32805         tpl.compile();
32806         Roo.Resizable.Handle.prototype.tpl = tpl;
32807     }
32808     this.position = pos;
32809     this.rz = rz;
32810     // show north drag fro topdra
32811     var handlepos = pos == 'hdrag' ? 'north' : pos;
32812     
32813     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32814     if (pos == 'hdrag') {
32815         this.el.setStyle('cursor', 'pointer');
32816     }
32817     this.el.unselectable();
32818     if(transparent){
32819         this.el.setOpacity(0);
32820     }
32821     this.el.on("mousedown", this.onMouseDown, this);
32822     if(!disableTrackOver){
32823         this.el.on("mouseover", this.onMouseOver, this);
32824         this.el.on("mouseout", this.onMouseOut, this);
32825     }
32826 };
32827
32828 // private
32829 Roo.Resizable.Handle.prototype = {
32830     afterResize : function(rz){
32831         Roo.log('after?');
32832         // do nothing
32833     },
32834     // private
32835     onMouseDown : function(e){
32836         this.rz.onMouseDown(this, e);
32837     },
32838     // private
32839     onMouseOver : function(e){
32840         this.rz.handleOver(this, e);
32841     },
32842     // private
32843     onMouseOut : function(e){
32844         this.rz.handleOut(this, e);
32845     }
32846 };/*
32847  * Based on:
32848  * Ext JS Library 1.1.1
32849  * Copyright(c) 2006-2007, Ext JS, LLC.
32850  *
32851  * Originally Released Under LGPL - original licence link has changed is not relivant.
32852  *
32853  * Fork - LGPL
32854  * <script type="text/javascript">
32855  */
32856
32857 /**
32858  * @class Roo.Editor
32859  * @extends Roo.Component
32860  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32861  * @constructor
32862  * Create a new Editor
32863  * @param {Roo.form.Field} field The Field object (or descendant)
32864  * @param {Object} config The config object
32865  */
32866 Roo.Editor = function(field, config){
32867     Roo.Editor.superclass.constructor.call(this, config);
32868     this.field = field;
32869     this.addEvents({
32870         /**
32871              * @event beforestartedit
32872              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32873              * false from the handler of this event.
32874              * @param {Editor} this
32875              * @param {Roo.Element} boundEl The underlying element bound to this editor
32876              * @param {Mixed} value The field value being set
32877              */
32878         "beforestartedit" : true,
32879         /**
32880              * @event startedit
32881              * Fires when this editor is displayed
32882              * @param {Roo.Element} boundEl The underlying element bound to this editor
32883              * @param {Mixed} value The starting field value
32884              */
32885         "startedit" : true,
32886         /**
32887              * @event beforecomplete
32888              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32889              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32890              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32891              * event will not fire since no edit actually occurred.
32892              * @param {Editor} this
32893              * @param {Mixed} value The current field value
32894              * @param {Mixed} startValue The original field value
32895              */
32896         "beforecomplete" : true,
32897         /**
32898              * @event complete
32899              * Fires after editing is complete and any changed value has been written to the underlying field.
32900              * @param {Editor} this
32901              * @param {Mixed} value The current field value
32902              * @param {Mixed} startValue The original field value
32903              */
32904         "complete" : true,
32905         /**
32906          * @event specialkey
32907          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32908          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32909          * @param {Roo.form.Field} this
32910          * @param {Roo.EventObject} e The event object
32911          */
32912         "specialkey" : true
32913     });
32914 };
32915
32916 Roo.extend(Roo.Editor, Roo.Component, {
32917     /**
32918      * @cfg {Boolean/String} autosize
32919      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32920      * or "height" to adopt the height only (defaults to false)
32921      */
32922     /**
32923      * @cfg {Boolean} revertInvalid
32924      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32925      * validation fails (defaults to true)
32926      */
32927     /**
32928      * @cfg {Boolean} ignoreNoChange
32929      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32930      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32931      * will never be ignored.
32932      */
32933     /**
32934      * @cfg {Boolean} hideEl
32935      * False to keep the bound element visible while the editor is displayed (defaults to true)
32936      */
32937     /**
32938      * @cfg {Mixed} value
32939      * The data value of the underlying field (defaults to "")
32940      */
32941     value : "",
32942     /**
32943      * @cfg {String} alignment
32944      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32945      */
32946     alignment: "c-c?",
32947     /**
32948      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32949      * for bottom-right shadow (defaults to "frame")
32950      */
32951     shadow : "frame",
32952     /**
32953      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32954      */
32955     constrain : false,
32956     /**
32957      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32958      */
32959     completeOnEnter : false,
32960     /**
32961      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32962      */
32963     cancelOnEsc : false,
32964     /**
32965      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32966      */
32967     updateEl : false,
32968
32969     // private
32970     onRender : function(ct, position){
32971         this.el = new Roo.Layer({
32972             shadow: this.shadow,
32973             cls: "x-editor",
32974             parentEl : ct,
32975             shim : this.shim,
32976             shadowOffset:4,
32977             id: this.id,
32978             constrain: this.constrain
32979         });
32980         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32981         if(this.field.msgTarget != 'title'){
32982             this.field.msgTarget = 'qtip';
32983         }
32984         this.field.render(this.el);
32985         if(Roo.isGecko){
32986             this.field.el.dom.setAttribute('autocomplete', 'off');
32987         }
32988         this.field.on("specialkey", this.onSpecialKey, this);
32989         if(this.swallowKeys){
32990             this.field.el.swallowEvent(['keydown','keypress']);
32991         }
32992         this.field.show();
32993         this.field.on("blur", this.onBlur, this);
32994         if(this.field.grow){
32995             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32996         }
32997     },
32998
32999     onSpecialKey : function(field, e)
33000     {
33001         //Roo.log('editor onSpecialKey');
33002         if(this.completeOnEnter && e.getKey() == e.ENTER){
33003             e.stopEvent();
33004             this.completeEdit();
33005             return;
33006         }
33007         // do not fire special key otherwise it might hide close the editor...
33008         if(e.getKey() == e.ENTER){    
33009             return;
33010         }
33011         if(this.cancelOnEsc && e.getKey() == e.ESC){
33012             this.cancelEdit();
33013             return;
33014         } 
33015         this.fireEvent('specialkey', field, e);
33016     
33017     },
33018
33019     /**
33020      * Starts the editing process and shows the editor.
33021      * @param {String/HTMLElement/Element} el The element to edit
33022      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33023       * to the innerHTML of el.
33024      */
33025     startEdit : function(el, value){
33026         if(this.editing){
33027             this.completeEdit();
33028         }
33029         this.boundEl = Roo.get(el);
33030         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33031         if(!this.rendered){
33032             this.render(this.parentEl || document.body);
33033         }
33034         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33035             return;
33036         }
33037         this.startValue = v;
33038         this.field.setValue(v);
33039         if(this.autoSize){
33040             var sz = this.boundEl.getSize();
33041             switch(this.autoSize){
33042                 case "width":
33043                 this.setSize(sz.width,  "");
33044                 break;
33045                 case "height":
33046                 this.setSize("",  sz.height);
33047                 break;
33048                 default:
33049                 this.setSize(sz.width,  sz.height);
33050             }
33051         }
33052         this.el.alignTo(this.boundEl, this.alignment);
33053         this.editing = true;
33054         if(Roo.QuickTips){
33055             Roo.QuickTips.disable();
33056         }
33057         this.show();
33058     },
33059
33060     /**
33061      * Sets the height and width of this editor.
33062      * @param {Number} width The new width
33063      * @param {Number} height The new height
33064      */
33065     setSize : function(w, h){
33066         this.field.setSize(w, h);
33067         if(this.el){
33068             this.el.sync();
33069         }
33070     },
33071
33072     /**
33073      * Realigns the editor to the bound field based on the current alignment config value.
33074      */
33075     realign : function(){
33076         this.el.alignTo(this.boundEl, this.alignment);
33077     },
33078
33079     /**
33080      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33081      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33082      */
33083     completeEdit : function(remainVisible){
33084         if(!this.editing){
33085             return;
33086         }
33087         var v = this.getValue();
33088         if(this.revertInvalid !== false && !this.field.isValid()){
33089             v = this.startValue;
33090             this.cancelEdit(true);
33091         }
33092         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33093             this.editing = false;
33094             this.hide();
33095             return;
33096         }
33097         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33098             this.editing = false;
33099             if(this.updateEl && this.boundEl){
33100                 this.boundEl.update(v);
33101             }
33102             if(remainVisible !== true){
33103                 this.hide();
33104             }
33105             this.fireEvent("complete", this, v, this.startValue);
33106         }
33107     },
33108
33109     // private
33110     onShow : function(){
33111         this.el.show();
33112         if(this.hideEl !== false){
33113             this.boundEl.hide();
33114         }
33115         this.field.show();
33116         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33117             this.fixIEFocus = true;
33118             this.deferredFocus.defer(50, this);
33119         }else{
33120             this.field.focus();
33121         }
33122         this.fireEvent("startedit", this.boundEl, this.startValue);
33123     },
33124
33125     deferredFocus : function(){
33126         if(this.editing){
33127             this.field.focus();
33128         }
33129     },
33130
33131     /**
33132      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33133      * reverted to the original starting value.
33134      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33135      * cancel (defaults to false)
33136      */
33137     cancelEdit : function(remainVisible){
33138         if(this.editing){
33139             this.setValue(this.startValue);
33140             if(remainVisible !== true){
33141                 this.hide();
33142             }
33143         }
33144     },
33145
33146     // private
33147     onBlur : function(){
33148         if(this.allowBlur !== true && this.editing){
33149             this.completeEdit();
33150         }
33151     },
33152
33153     // private
33154     onHide : function(){
33155         if(this.editing){
33156             this.completeEdit();
33157             return;
33158         }
33159         this.field.blur();
33160         if(this.field.collapse){
33161             this.field.collapse();
33162         }
33163         this.el.hide();
33164         if(this.hideEl !== false){
33165             this.boundEl.show();
33166         }
33167         if(Roo.QuickTips){
33168             Roo.QuickTips.enable();
33169         }
33170     },
33171
33172     /**
33173      * Sets the data value of the editor
33174      * @param {Mixed} value Any valid value supported by the underlying field
33175      */
33176     setValue : function(v){
33177         this.field.setValue(v);
33178     },
33179
33180     /**
33181      * Gets the data value of the editor
33182      * @return {Mixed} The data value
33183      */
33184     getValue : function(){
33185         return this.field.getValue();
33186     }
33187 });/*
33188  * Based on:
33189  * Ext JS Library 1.1.1
33190  * Copyright(c) 2006-2007, Ext JS, LLC.
33191  *
33192  * Originally Released Under LGPL - original licence link has changed is not relivant.
33193  *
33194  * Fork - LGPL
33195  * <script type="text/javascript">
33196  */
33197  
33198 /**
33199  * @class Roo.BasicDialog
33200  * @extends Roo.util.Observable
33201  * @parent none builder
33202  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33203  * <pre><code>
33204 var dlg = new Roo.BasicDialog("my-dlg", {
33205     height: 200,
33206     width: 300,
33207     minHeight: 100,
33208     minWidth: 150,
33209     modal: true,
33210     proxyDrag: true,
33211     shadow: true
33212 });
33213 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33214 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33215 dlg.addButton('Cancel', dlg.hide, dlg);
33216 dlg.show();
33217 </code></pre>
33218   <b>A Dialog should always be a direct child of the body element.</b>
33219  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33220  * @cfg {String} title Default text to display in the title bar (defaults to null)
33221  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33222  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33223  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33224  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33225  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33226  * (defaults to null with no animation)
33227  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33228  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33229  * property for valid values (defaults to 'all')
33230  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33231  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33232  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33233  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33234  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33235  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33236  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33237  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33238  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33239  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33240  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33241  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33242  * draggable = true (defaults to false)
33243  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33244  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33245  * shadow (defaults to false)
33246  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33247  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33248  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33249  * @cfg {Array} buttons Array of buttons
33250  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33251  * @constructor
33252  * Create a new BasicDialog.
33253  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33254  * @param {Object} config Configuration options
33255  */
33256 Roo.BasicDialog = function(el, config){
33257     this.el = Roo.get(el);
33258     var dh = Roo.DomHelper;
33259     if(!this.el && config && config.autoCreate){
33260         if(typeof config.autoCreate == "object"){
33261             if(!config.autoCreate.id){
33262                 config.autoCreate.id = el;
33263             }
33264             this.el = dh.append(document.body,
33265                         config.autoCreate, true);
33266         }else{
33267             this.el = dh.append(document.body,
33268                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33269         }
33270     }
33271     el = this.el;
33272     el.setDisplayed(true);
33273     el.hide = this.hideAction;
33274     this.id = el.id;
33275     el.addClass("x-dlg");
33276
33277     Roo.apply(this, config);
33278
33279     this.proxy = el.createProxy("x-dlg-proxy");
33280     this.proxy.hide = this.hideAction;
33281     this.proxy.setOpacity(.5);
33282     this.proxy.hide();
33283
33284     if(config.width){
33285         el.setWidth(config.width);
33286     }
33287     if(config.height){
33288         el.setHeight(config.height);
33289     }
33290     this.size = el.getSize();
33291     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33292         this.xy = [config.x,config.y];
33293     }else{
33294         this.xy = el.getCenterXY(true);
33295     }
33296     /** The header element @type Roo.Element */
33297     this.header = el.child("> .x-dlg-hd");
33298     /** The body element @type Roo.Element */
33299     this.body = el.child("> .x-dlg-bd");
33300     /** The footer element @type Roo.Element */
33301     this.footer = el.child("> .x-dlg-ft");
33302
33303     if(!this.header){
33304         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33305     }
33306     if(!this.body){
33307         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33308     }
33309
33310     this.header.unselectable();
33311     if(this.title){
33312         this.header.update(this.title);
33313     }
33314     // this element allows the dialog to be focused for keyboard event
33315     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33316     this.focusEl.swallowEvent("click", true);
33317
33318     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33319
33320     // wrap the body and footer for special rendering
33321     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33322     if(this.footer){
33323         this.bwrap.dom.appendChild(this.footer.dom);
33324     }
33325
33326     this.bg = this.el.createChild({
33327         tag: "div", cls:"x-dlg-bg",
33328         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33329     });
33330     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33331
33332
33333     if(this.autoScroll !== false && !this.autoTabs){
33334         this.body.setStyle("overflow", "auto");
33335     }
33336
33337     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33338
33339     if(this.closable !== false){
33340         this.el.addClass("x-dlg-closable");
33341         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33342         this.close.on("click", this.closeClick, this);
33343         this.close.addClassOnOver("x-dlg-close-over");
33344     }
33345     if(this.collapsible !== false){
33346         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33347         this.collapseBtn.on("click", this.collapseClick, this);
33348         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33349         this.header.on("dblclick", this.collapseClick, this);
33350     }
33351     if(this.resizable !== false){
33352         this.el.addClass("x-dlg-resizable");
33353         this.resizer = new Roo.Resizable(el, {
33354             minWidth: this.minWidth || 80,
33355             minHeight:this.minHeight || 80,
33356             handles: this.resizeHandles || "all",
33357             pinned: true
33358         });
33359         this.resizer.on("beforeresize", this.beforeResize, this);
33360         this.resizer.on("resize", this.onResize, this);
33361     }
33362     if(this.draggable !== false){
33363         el.addClass("x-dlg-draggable");
33364         if (!this.proxyDrag) {
33365             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33366         }
33367         else {
33368             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33369         }
33370         dd.setHandleElId(this.header.id);
33371         dd.endDrag = this.endMove.createDelegate(this);
33372         dd.startDrag = this.startMove.createDelegate(this);
33373         dd.onDrag = this.onDrag.createDelegate(this);
33374         dd.scroll = false;
33375         this.dd = dd;
33376     }
33377     if(this.modal){
33378         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33379         this.mask.enableDisplayMode("block");
33380         this.mask.hide();
33381         this.el.addClass("x-dlg-modal");
33382     }
33383     if(this.shadow){
33384         this.shadow = new Roo.Shadow({
33385             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33386             offset : this.shadowOffset
33387         });
33388     }else{
33389         this.shadowOffset = 0;
33390     }
33391     if(Roo.useShims && this.shim !== false){
33392         this.shim = this.el.createShim();
33393         this.shim.hide = this.hideAction;
33394         this.shim.hide();
33395     }else{
33396         this.shim = false;
33397     }
33398     if(this.autoTabs){
33399         this.initTabs();
33400     }
33401     if (this.buttons) { 
33402         var bts= this.buttons;
33403         this.buttons = [];
33404         Roo.each(bts, function(b) {
33405             this.addButton(b);
33406         }, this);
33407     }
33408     
33409     
33410     this.addEvents({
33411         /**
33412          * @event keydown
33413          * Fires when a key is pressed
33414          * @param {Roo.BasicDialog} this
33415          * @param {Roo.EventObject} e
33416          */
33417         "keydown" : true,
33418         /**
33419          * @event move
33420          * Fires when this dialog is moved by the user.
33421          * @param {Roo.BasicDialog} this
33422          * @param {Number} x The new page X
33423          * @param {Number} y The new page Y
33424          */
33425         "move" : true,
33426         /**
33427          * @event resize
33428          * Fires when this dialog is resized by the user.
33429          * @param {Roo.BasicDialog} this
33430          * @param {Number} width The new width
33431          * @param {Number} height The new height
33432          */
33433         "resize" : true,
33434         /**
33435          * @event beforehide
33436          * Fires before this dialog is hidden.
33437          * @param {Roo.BasicDialog} this
33438          */
33439         "beforehide" : true,
33440         /**
33441          * @event hide
33442          * Fires when this dialog is hidden.
33443          * @param {Roo.BasicDialog} this
33444          */
33445         "hide" : true,
33446         /**
33447          * @event beforeshow
33448          * Fires before this dialog is shown.
33449          * @param {Roo.BasicDialog} this
33450          */
33451         "beforeshow" : true,
33452         /**
33453          * @event show
33454          * Fires when this dialog is shown.
33455          * @param {Roo.BasicDialog} this
33456          */
33457         "show" : true
33458     });
33459     el.on("keydown", this.onKeyDown, this);
33460     el.on("mousedown", this.toFront, this);
33461     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33462     this.el.hide();
33463     Roo.DialogManager.register(this);
33464     Roo.BasicDialog.superclass.constructor.call(this);
33465 };
33466
33467 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33468     shadowOffset: Roo.isIE ? 6 : 5,
33469     minHeight: 80,
33470     minWidth: 200,
33471     minButtonWidth: 75,
33472     defaultButton: null,
33473     buttonAlign: "right",
33474     tabTag: 'div',
33475     firstShow: true,
33476
33477     /**
33478      * Sets the dialog title text
33479      * @param {String} text The title text to display
33480      * @return {Roo.BasicDialog} this
33481      */
33482     setTitle : function(text){
33483         this.header.update(text);
33484         return this;
33485     },
33486
33487     // private
33488     closeClick : function(){
33489         this.hide();
33490     },
33491
33492     // private
33493     collapseClick : function(){
33494         this[this.collapsed ? "expand" : "collapse"]();
33495     },
33496
33497     /**
33498      * Collapses the dialog to its minimized state (only the title bar is visible).
33499      * Equivalent to the user clicking the collapse dialog button.
33500      */
33501     collapse : function(){
33502         if(!this.collapsed){
33503             this.collapsed = true;
33504             this.el.addClass("x-dlg-collapsed");
33505             this.restoreHeight = this.el.getHeight();
33506             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33507         }
33508     },
33509
33510     /**
33511      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33512      * clicking the expand dialog button.
33513      */
33514     expand : function(){
33515         if(this.collapsed){
33516             this.collapsed = false;
33517             this.el.removeClass("x-dlg-collapsed");
33518             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33519         }
33520     },
33521
33522     /**
33523      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33524      * @return {Roo.TabPanel} The tabs component
33525      */
33526     initTabs : function(){
33527         var tabs = this.getTabs();
33528         while(tabs.getTab(0)){
33529             tabs.removeTab(0);
33530         }
33531         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33532             var dom = el.dom;
33533             tabs.addTab(Roo.id(dom), dom.title);
33534             dom.title = "";
33535         });
33536         tabs.activate(0);
33537         return tabs;
33538     },
33539
33540     // private
33541     beforeResize : function(){
33542         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33543     },
33544
33545     // private
33546     onResize : function(){
33547         this.refreshSize();
33548         this.syncBodyHeight();
33549         this.adjustAssets();
33550         this.focus();
33551         this.fireEvent("resize", this, this.size.width, this.size.height);
33552     },
33553
33554     // private
33555     onKeyDown : function(e){
33556         if(this.isVisible()){
33557             this.fireEvent("keydown", this, e);
33558         }
33559     },
33560
33561     /**
33562      * Resizes the dialog.
33563      * @param {Number} width
33564      * @param {Number} height
33565      * @return {Roo.BasicDialog} this
33566      */
33567     resizeTo : function(width, height){
33568         this.el.setSize(width, height);
33569         this.size = {width: width, height: height};
33570         this.syncBodyHeight();
33571         if(this.fixedcenter){
33572             this.center();
33573         }
33574         if(this.isVisible()){
33575             this.constrainXY();
33576             this.adjustAssets();
33577         }
33578         this.fireEvent("resize", this, width, height);
33579         return this;
33580     },
33581
33582
33583     /**
33584      * Resizes the dialog to fit the specified content size.
33585      * @param {Number} width
33586      * @param {Number} height
33587      * @return {Roo.BasicDialog} this
33588      */
33589     setContentSize : function(w, h){
33590         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33591         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33592         //if(!this.el.isBorderBox()){
33593             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33594             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33595         //}
33596         if(this.tabs){
33597             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33598             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33599         }
33600         this.resizeTo(w, h);
33601         return this;
33602     },
33603
33604     /**
33605      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33606      * executed in response to a particular key being pressed while the dialog is active.
33607      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33608      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33609      * @param {Function} fn The function to call
33610      * @param {Object} scope (optional) The scope of the function
33611      * @return {Roo.BasicDialog} this
33612      */
33613     addKeyListener : function(key, fn, scope){
33614         var keyCode, shift, ctrl, alt;
33615         if(typeof key == "object" && !(key instanceof Array)){
33616             keyCode = key["key"];
33617             shift = key["shift"];
33618             ctrl = key["ctrl"];
33619             alt = key["alt"];
33620         }else{
33621             keyCode = key;
33622         }
33623         var handler = function(dlg, e){
33624             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33625                 var k = e.getKey();
33626                 if(keyCode instanceof Array){
33627                     for(var i = 0, len = keyCode.length; i < len; i++){
33628                         if(keyCode[i] == k){
33629                           fn.call(scope || window, dlg, k, e);
33630                           return;
33631                         }
33632                     }
33633                 }else{
33634                     if(k == keyCode){
33635                         fn.call(scope || window, dlg, k, e);
33636                     }
33637                 }
33638             }
33639         };
33640         this.on("keydown", handler);
33641         return this;
33642     },
33643
33644     /**
33645      * Returns the TabPanel component (creates it if it doesn't exist).
33646      * Note: If you wish to simply check for the existence of tabs without creating them,
33647      * check for a null 'tabs' property.
33648      * @return {Roo.TabPanel} The tabs component
33649      */
33650     getTabs : function(){
33651         if(!this.tabs){
33652             this.el.addClass("x-dlg-auto-tabs");
33653             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33654             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33655         }
33656         return this.tabs;
33657     },
33658
33659     /**
33660      * Adds a button to the footer section of the dialog.
33661      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33662      * object or a valid Roo.DomHelper element config
33663      * @param {Function} handler The function called when the button is clicked
33664      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33665      * @return {Roo.Button} The new button
33666      */
33667     addButton : function(config, handler, scope){
33668         var dh = Roo.DomHelper;
33669         if(!this.footer){
33670             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33671         }
33672         if(!this.btnContainer){
33673             var tb = this.footer.createChild({
33674
33675                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33676                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33677             }, null, true);
33678             this.btnContainer = tb.firstChild.firstChild.firstChild;
33679         }
33680         var bconfig = {
33681             handler: handler,
33682             scope: scope,
33683             minWidth: this.minButtonWidth,
33684             hideParent:true
33685         };
33686         if(typeof config == "string"){
33687             bconfig.text = config;
33688         }else{
33689             if(config.tag){
33690                 bconfig.dhconfig = config;
33691             }else{
33692                 Roo.apply(bconfig, config);
33693             }
33694         }
33695         var fc = false;
33696         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33697             bconfig.position = Math.max(0, bconfig.position);
33698             fc = this.btnContainer.childNodes[bconfig.position];
33699         }
33700          
33701         var btn = new Roo.Button(
33702             fc ? 
33703                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33704                 : this.btnContainer.appendChild(document.createElement("td")),
33705             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33706             bconfig
33707         );
33708         this.syncBodyHeight();
33709         if(!this.buttons){
33710             /**
33711              * Array of all the buttons that have been added to this dialog via addButton
33712              * @type Array
33713              */
33714             this.buttons = [];
33715         }
33716         this.buttons.push(btn);
33717         return btn;
33718     },
33719
33720     /**
33721      * Sets the default button to be focused when the dialog is displayed.
33722      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33723      * @return {Roo.BasicDialog} this
33724      */
33725     setDefaultButton : function(btn){
33726         this.defaultButton = btn;
33727         return this;
33728     },
33729
33730     // private
33731     getHeaderFooterHeight : function(safe){
33732         var height = 0;
33733         if(this.header){
33734            height += this.header.getHeight();
33735         }
33736         if(this.footer){
33737            var fm = this.footer.getMargins();
33738             height += (this.footer.getHeight()+fm.top+fm.bottom);
33739         }
33740         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33741         height += this.centerBg.getPadding("tb");
33742         return height;
33743     },
33744
33745     // private
33746     syncBodyHeight : function()
33747     {
33748         var bd = this.body, // the text
33749             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33750             bw = this.bwrap;
33751         var height = this.size.height - this.getHeaderFooterHeight(false);
33752         bd.setHeight(height-bd.getMargins("tb"));
33753         var hh = this.header.getHeight();
33754         var h = this.size.height-hh;
33755         cb.setHeight(h);
33756         
33757         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33758         bw.setHeight(h-cb.getPadding("tb"));
33759         
33760         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33761         bd.setWidth(bw.getWidth(true));
33762         if(this.tabs){
33763             this.tabs.syncHeight();
33764             if(Roo.isIE){
33765                 this.tabs.el.repaint();
33766             }
33767         }
33768     },
33769
33770     /**
33771      * Restores the previous state of the dialog if Roo.state is configured.
33772      * @return {Roo.BasicDialog} this
33773      */
33774     restoreState : function(){
33775         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33776         if(box && box.width){
33777             this.xy = [box.x, box.y];
33778             this.resizeTo(box.width, box.height);
33779         }
33780         return this;
33781     },
33782
33783     // private
33784     beforeShow : function(){
33785         this.expand();
33786         if(this.fixedcenter){
33787             this.xy = this.el.getCenterXY(true);
33788         }
33789         if(this.modal){
33790             Roo.get(document.body).addClass("x-body-masked");
33791             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33792             this.mask.show();
33793         }
33794         this.constrainXY();
33795     },
33796
33797     // private
33798     animShow : function(){
33799         var b = Roo.get(this.animateTarget).getBox();
33800         this.proxy.setSize(b.width, b.height);
33801         this.proxy.setLocation(b.x, b.y);
33802         this.proxy.show();
33803         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33804                     true, .35, this.showEl.createDelegate(this));
33805     },
33806
33807     /**
33808      * Shows the dialog.
33809      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33810      * @return {Roo.BasicDialog} this
33811      */
33812     show : function(animateTarget){
33813         if (this.fireEvent("beforeshow", this) === false){
33814             return;
33815         }
33816         if(this.syncHeightBeforeShow){
33817             this.syncBodyHeight();
33818         }else if(this.firstShow){
33819             this.firstShow = false;
33820             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33821         }
33822         this.animateTarget = animateTarget || this.animateTarget;
33823         if(!this.el.isVisible()){
33824             this.beforeShow();
33825             if(this.animateTarget && Roo.get(this.animateTarget)){
33826                 this.animShow();
33827             }else{
33828                 this.showEl();
33829             }
33830         }
33831         return this;
33832     },
33833
33834     // private
33835     showEl : function(){
33836         this.proxy.hide();
33837         this.el.setXY(this.xy);
33838         this.el.show();
33839         this.adjustAssets(true);
33840         this.toFront();
33841         this.focus();
33842         // IE peekaboo bug - fix found by Dave Fenwick
33843         if(Roo.isIE){
33844             this.el.repaint();
33845         }
33846         this.fireEvent("show", this);
33847     },
33848
33849     /**
33850      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33851      * dialog itself will receive focus.
33852      */
33853     focus : function(){
33854         if(this.defaultButton){
33855             this.defaultButton.focus();
33856         }else{
33857             this.focusEl.focus();
33858         }
33859     },
33860
33861     // private
33862     constrainXY : function(){
33863         if(this.constraintoviewport !== false){
33864             if(!this.viewSize){
33865                 if(this.container){
33866                     var s = this.container.getSize();
33867                     this.viewSize = [s.width, s.height];
33868                 }else{
33869                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33870                 }
33871             }
33872             var s = Roo.get(this.container||document).getScroll();
33873
33874             var x = this.xy[0], y = this.xy[1];
33875             var w = this.size.width, h = this.size.height;
33876             var vw = this.viewSize[0], vh = this.viewSize[1];
33877             // only move it if it needs it
33878             var moved = false;
33879             // first validate right/bottom
33880             if(x + w > vw+s.left){
33881                 x = vw - w;
33882                 moved = true;
33883             }
33884             if(y + h > vh+s.top){
33885                 y = vh - h;
33886                 moved = true;
33887             }
33888             // then make sure top/left isn't negative
33889             if(x < s.left){
33890                 x = s.left;
33891                 moved = true;
33892             }
33893             if(y < s.top){
33894                 y = s.top;
33895                 moved = true;
33896             }
33897             if(moved){
33898                 // cache xy
33899                 this.xy = [x, y];
33900                 if(this.isVisible()){
33901                     this.el.setLocation(x, y);
33902                     this.adjustAssets();
33903                 }
33904             }
33905         }
33906     },
33907
33908     // private
33909     onDrag : function(){
33910         if(!this.proxyDrag){
33911             this.xy = this.el.getXY();
33912             this.adjustAssets();
33913         }
33914     },
33915
33916     // private
33917     adjustAssets : function(doShow){
33918         var x = this.xy[0], y = this.xy[1];
33919         var w = this.size.width, h = this.size.height;
33920         if(doShow === true){
33921             if(this.shadow){
33922                 this.shadow.show(this.el);
33923             }
33924             if(this.shim){
33925                 this.shim.show();
33926             }
33927         }
33928         if(this.shadow && this.shadow.isVisible()){
33929             this.shadow.show(this.el);
33930         }
33931         if(this.shim && this.shim.isVisible()){
33932             this.shim.setBounds(x, y, w, h);
33933         }
33934     },
33935
33936     // private
33937     adjustViewport : function(w, h){
33938         if(!w || !h){
33939             w = Roo.lib.Dom.getViewWidth();
33940             h = Roo.lib.Dom.getViewHeight();
33941         }
33942         // cache the size
33943         this.viewSize = [w, h];
33944         if(this.modal && this.mask.isVisible()){
33945             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33946             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33947         }
33948         if(this.isVisible()){
33949             this.constrainXY();
33950         }
33951     },
33952
33953     /**
33954      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33955      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33956      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33957      */
33958     destroy : function(removeEl){
33959         if(this.isVisible()){
33960             this.animateTarget = null;
33961             this.hide();
33962         }
33963         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33964         if(this.tabs){
33965             this.tabs.destroy(removeEl);
33966         }
33967         Roo.destroy(
33968              this.shim,
33969              this.proxy,
33970              this.resizer,
33971              this.close,
33972              this.mask
33973         );
33974         if(this.dd){
33975             this.dd.unreg();
33976         }
33977         if(this.buttons){
33978            for(var i = 0, len = this.buttons.length; i < len; i++){
33979                this.buttons[i].destroy();
33980            }
33981         }
33982         this.el.removeAllListeners();
33983         if(removeEl === true){
33984             this.el.update("");
33985             this.el.remove();
33986         }
33987         Roo.DialogManager.unregister(this);
33988     },
33989
33990     // private
33991     startMove : function(){
33992         if(this.proxyDrag){
33993             this.proxy.show();
33994         }
33995         if(this.constraintoviewport !== false){
33996             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33997         }
33998     },
33999
34000     // private
34001     endMove : function(){
34002         if(!this.proxyDrag){
34003             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34004         }else{
34005             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34006             this.proxy.hide();
34007         }
34008         this.refreshSize();
34009         this.adjustAssets();
34010         this.focus();
34011         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34012     },
34013
34014     /**
34015      * Brings this dialog to the front of any other visible dialogs
34016      * @return {Roo.BasicDialog} this
34017      */
34018     toFront : function(){
34019         Roo.DialogManager.bringToFront(this);
34020         return this;
34021     },
34022
34023     /**
34024      * Sends this dialog to the back (under) of any other visible dialogs
34025      * @return {Roo.BasicDialog} this
34026      */
34027     toBack : function(){
34028         Roo.DialogManager.sendToBack(this);
34029         return this;
34030     },
34031
34032     /**
34033      * Centers this dialog in the viewport
34034      * @return {Roo.BasicDialog} this
34035      */
34036     center : function(){
34037         var xy = this.el.getCenterXY(true);
34038         this.moveTo(xy[0], xy[1]);
34039         return this;
34040     },
34041
34042     /**
34043      * Moves the dialog's top-left corner to the specified point
34044      * @param {Number} x
34045      * @param {Number} y
34046      * @return {Roo.BasicDialog} this
34047      */
34048     moveTo : function(x, y){
34049         this.xy = [x,y];
34050         if(this.isVisible()){
34051             this.el.setXY(this.xy);
34052             this.adjustAssets();
34053         }
34054         return this;
34055     },
34056
34057     /**
34058      * Aligns the dialog to the specified element
34059      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34060      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34061      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34062      * @return {Roo.BasicDialog} this
34063      */
34064     alignTo : function(element, position, offsets){
34065         this.xy = this.el.getAlignToXY(element, position, offsets);
34066         if(this.isVisible()){
34067             this.el.setXY(this.xy);
34068             this.adjustAssets();
34069         }
34070         return this;
34071     },
34072
34073     /**
34074      * Anchors an element to another element and realigns it when the window is resized.
34075      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34076      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34077      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34078      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34079      * is a number, it is used as the buffer delay (defaults to 50ms).
34080      * @return {Roo.BasicDialog} this
34081      */
34082     anchorTo : function(el, alignment, offsets, monitorScroll){
34083         var action = function(){
34084             this.alignTo(el, alignment, offsets);
34085         };
34086         Roo.EventManager.onWindowResize(action, this);
34087         var tm = typeof monitorScroll;
34088         if(tm != 'undefined'){
34089             Roo.EventManager.on(window, 'scroll', action, this,
34090                 {buffer: tm == 'number' ? monitorScroll : 50});
34091         }
34092         action.call(this);
34093         return this;
34094     },
34095
34096     /**
34097      * Returns true if the dialog is visible
34098      * @return {Boolean}
34099      */
34100     isVisible : function(){
34101         return this.el.isVisible();
34102     },
34103
34104     // private
34105     animHide : function(callback){
34106         var b = Roo.get(this.animateTarget).getBox();
34107         this.proxy.show();
34108         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34109         this.el.hide();
34110         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34111                     this.hideEl.createDelegate(this, [callback]));
34112     },
34113
34114     /**
34115      * Hides the dialog.
34116      * @param {Function} callback (optional) Function to call when the dialog is hidden
34117      * @return {Roo.BasicDialog} this
34118      */
34119     hide : function(callback){
34120         if (this.fireEvent("beforehide", this) === false){
34121             return;
34122         }
34123         if(this.shadow){
34124             this.shadow.hide();
34125         }
34126         if(this.shim) {
34127           this.shim.hide();
34128         }
34129         // sometimes animateTarget seems to get set.. causing problems...
34130         // this just double checks..
34131         if(this.animateTarget && Roo.get(this.animateTarget)) {
34132            this.animHide(callback);
34133         }else{
34134             this.el.hide();
34135             this.hideEl(callback);
34136         }
34137         return this;
34138     },
34139
34140     // private
34141     hideEl : function(callback){
34142         this.proxy.hide();
34143         if(this.modal){
34144             this.mask.hide();
34145             Roo.get(document.body).removeClass("x-body-masked");
34146         }
34147         this.fireEvent("hide", this);
34148         if(typeof callback == "function"){
34149             callback();
34150         }
34151     },
34152
34153     // private
34154     hideAction : function(){
34155         this.setLeft("-10000px");
34156         this.setTop("-10000px");
34157         this.setStyle("visibility", "hidden");
34158     },
34159
34160     // private
34161     refreshSize : function(){
34162         this.size = this.el.getSize();
34163         this.xy = this.el.getXY();
34164         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34165     },
34166
34167     // private
34168     // z-index is managed by the DialogManager and may be overwritten at any time
34169     setZIndex : function(index){
34170         if(this.modal){
34171             this.mask.setStyle("z-index", index);
34172         }
34173         if(this.shim){
34174             this.shim.setStyle("z-index", ++index);
34175         }
34176         if(this.shadow){
34177             this.shadow.setZIndex(++index);
34178         }
34179         this.el.setStyle("z-index", ++index);
34180         if(this.proxy){
34181             this.proxy.setStyle("z-index", ++index);
34182         }
34183         if(this.resizer){
34184             this.resizer.proxy.setStyle("z-index", ++index);
34185         }
34186
34187         this.lastZIndex = index;
34188     },
34189
34190     /**
34191      * Returns the element for this dialog
34192      * @return {Roo.Element} The underlying dialog Element
34193      */
34194     getEl : function(){
34195         return this.el;
34196     }
34197 });
34198
34199 /**
34200  * @class Roo.DialogManager
34201  * Provides global access to BasicDialogs that have been created and
34202  * support for z-indexing (layering) multiple open dialogs.
34203  */
34204 Roo.DialogManager = function(){
34205     var list = {};
34206     var accessList = [];
34207     var front = null;
34208
34209     // private
34210     var sortDialogs = function(d1, d2){
34211         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34212     };
34213
34214     // private
34215     var orderDialogs = function(){
34216         accessList.sort(sortDialogs);
34217         var seed = Roo.DialogManager.zseed;
34218         for(var i = 0, len = accessList.length; i < len; i++){
34219             var dlg = accessList[i];
34220             if(dlg){
34221                 dlg.setZIndex(seed + (i*10));
34222             }
34223         }
34224     };
34225
34226     return {
34227         /**
34228          * The starting z-index for BasicDialogs (defaults to 9000)
34229          * @type Number The z-index value
34230          */
34231         zseed : 9000,
34232
34233         // private
34234         register : function(dlg){
34235             list[dlg.id] = dlg;
34236             accessList.push(dlg);
34237         },
34238
34239         // private
34240         unregister : function(dlg){
34241             delete list[dlg.id];
34242             var i=0;
34243             var len=0;
34244             if(!accessList.indexOf){
34245                 for(  i = 0, len = accessList.length; i < len; i++){
34246                     if(accessList[i] == dlg){
34247                         accessList.splice(i, 1);
34248                         return;
34249                     }
34250                 }
34251             }else{
34252                  i = accessList.indexOf(dlg);
34253                 if(i != -1){
34254                     accessList.splice(i, 1);
34255                 }
34256             }
34257         },
34258
34259         /**
34260          * Gets a registered dialog by id
34261          * @param {String/Object} id The id of the dialog or a dialog
34262          * @return {Roo.BasicDialog} this
34263          */
34264         get : function(id){
34265             return typeof id == "object" ? id : list[id];
34266         },
34267
34268         /**
34269          * Brings the specified dialog to the front
34270          * @param {String/Object} dlg The id of the dialog or a dialog
34271          * @return {Roo.BasicDialog} this
34272          */
34273         bringToFront : function(dlg){
34274             dlg = this.get(dlg);
34275             if(dlg != front){
34276                 front = dlg;
34277                 dlg._lastAccess = new Date().getTime();
34278                 orderDialogs();
34279             }
34280             return dlg;
34281         },
34282
34283         /**
34284          * Sends the specified dialog to the back
34285          * @param {String/Object} dlg The id of the dialog or a dialog
34286          * @return {Roo.BasicDialog} this
34287          */
34288         sendToBack : function(dlg){
34289             dlg = this.get(dlg);
34290             dlg._lastAccess = -(new Date().getTime());
34291             orderDialogs();
34292             return dlg;
34293         },
34294
34295         /**
34296          * Hides all dialogs
34297          */
34298         hideAll : function(){
34299             for(var id in list){
34300                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34301                     list[id].hide();
34302                 }
34303             }
34304         }
34305     };
34306 }();
34307
34308 /**
34309  * @class Roo.LayoutDialog
34310  * @extends Roo.BasicDialog
34311  * @children Roo.ContentPanel
34312  * @parent builder none
34313  * Dialog which provides adjustments for working with a layout in a Dialog.
34314  * Add your necessary layout config options to the dialog's config.<br>
34315  * Example usage (including a nested layout):
34316  * <pre><code>
34317 if(!dialog){
34318     dialog = new Roo.LayoutDialog("download-dlg", {
34319         modal: true,
34320         width:600,
34321         height:450,
34322         shadow:true,
34323         minWidth:500,
34324         minHeight:350,
34325         autoTabs:true,
34326         proxyDrag:true,
34327         // layout config merges with the dialog config
34328         center:{
34329             tabPosition: "top",
34330             alwaysShowTabs: true
34331         }
34332     });
34333     dialog.addKeyListener(27, dialog.hide, dialog);
34334     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34335     dialog.addButton("Build It!", this.getDownload, this);
34336
34337     // we can even add nested layouts
34338     var innerLayout = new Roo.BorderLayout("dl-inner", {
34339         east: {
34340             initialSize: 200,
34341             autoScroll:true,
34342             split:true
34343         },
34344         center: {
34345             autoScroll:true
34346         }
34347     });
34348     innerLayout.beginUpdate();
34349     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34350     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34351     innerLayout.endUpdate(true);
34352
34353     var layout = dialog.getLayout();
34354     layout.beginUpdate();
34355     layout.add("center", new Roo.ContentPanel("standard-panel",
34356                         {title: "Download the Source", fitToFrame:true}));
34357     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34358                {title: "Build your own roo.js"}));
34359     layout.getRegion("center").showPanel(sp);
34360     layout.endUpdate();
34361 }
34362 </code></pre>
34363     * @constructor
34364     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34365     * @param {Object} config configuration options
34366   */
34367 Roo.LayoutDialog = function(el, cfg){
34368     
34369     var config=  cfg;
34370     if (typeof(cfg) == 'undefined') {
34371         config = Roo.apply({}, el);
34372         // not sure why we use documentElement here.. - it should always be body.
34373         // IE7 borks horribly if we use documentElement.
34374         // webkit also does not like documentElement - it creates a body element...
34375         el = Roo.get( document.body || document.documentElement ).createChild();
34376         //config.autoCreate = true;
34377     }
34378     
34379     
34380     config.autoTabs = false;
34381     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34382     this.body.setStyle({overflow:"hidden", position:"relative"});
34383     this.layout = new Roo.BorderLayout(this.body.dom, config);
34384     this.layout.monitorWindowResize = false;
34385     this.el.addClass("x-dlg-auto-layout");
34386     // fix case when center region overwrites center function
34387     this.center = Roo.BasicDialog.prototype.center;
34388     this.on("show", this.layout.layout, this.layout, true);
34389     if (config.items) {
34390         var xitems = config.items;
34391         delete config.items;
34392         Roo.each(xitems, this.addxtype, this);
34393     }
34394     
34395     
34396 };
34397 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34398     
34399     
34400     /**
34401      * @cfg {Roo.LayoutRegion} east  
34402      */
34403     /**
34404      * @cfg {Roo.LayoutRegion} west
34405      */
34406     /**
34407      * @cfg {Roo.LayoutRegion} south
34408      */
34409     /**
34410      * @cfg {Roo.LayoutRegion} north
34411      */
34412     /**
34413      * @cfg {Roo.LayoutRegion} center
34414      */
34415     /**
34416      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34417      */
34418     
34419     
34420     /**
34421      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34422      * @deprecated
34423      */
34424     endUpdate : function(){
34425         this.layout.endUpdate();
34426     },
34427
34428     /**
34429      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34430      *  @deprecated
34431      */
34432     beginUpdate : function(){
34433         this.layout.beginUpdate();
34434     },
34435
34436     /**
34437      * Get the BorderLayout for this dialog
34438      * @return {Roo.BorderLayout}
34439      */
34440     getLayout : function(){
34441         return this.layout;
34442     },
34443
34444     showEl : function(){
34445         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34446         if(Roo.isIE7){
34447             this.layout.layout();
34448         }
34449     },
34450
34451     // private
34452     // Use the syncHeightBeforeShow config option to control this automatically
34453     syncBodyHeight : function(){
34454         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34455         if(this.layout){this.layout.layout();}
34456     },
34457     
34458       /**
34459      * Add an xtype element (actually adds to the layout.)
34460      * @return {Object} xdata xtype object data.
34461      */
34462     
34463     addxtype : function(c) {
34464         return this.layout.addxtype(c);
34465     }
34466 });/*
34467  * Based on:
34468  * Ext JS Library 1.1.1
34469  * Copyright(c) 2006-2007, Ext JS, LLC.
34470  *
34471  * Originally Released Under LGPL - original licence link has changed is not relivant.
34472  *
34473  * Fork - LGPL
34474  * <script type="text/javascript">
34475  */
34476  
34477 /**
34478  * @class Roo.MessageBox
34479  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34480  * Example usage:
34481  *<pre><code>
34482 // Basic alert:
34483 Roo.Msg.alert('Status', 'Changes saved successfully.');
34484
34485 // Prompt for user data:
34486 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34487     if (btn == 'ok'){
34488         // process text value...
34489     }
34490 });
34491
34492 // Show a dialog using config options:
34493 Roo.Msg.show({
34494    title:'Save Changes?',
34495    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34496    buttons: Roo.Msg.YESNOCANCEL,
34497    fn: processResult,
34498    animEl: 'elId'
34499 });
34500 </code></pre>
34501  * @static
34502  */
34503 Roo.MessageBox = function(){
34504     var dlg, opt, mask, waitTimer;
34505     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34506     var buttons, activeTextEl, bwidth;
34507
34508     // private
34509     var handleButton = function(button){
34510         dlg.hide();
34511         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34512     };
34513
34514     // private
34515     var handleHide = function(){
34516         if(opt && opt.cls){
34517             dlg.el.removeClass(opt.cls);
34518         }
34519         if(waitTimer){
34520             Roo.TaskMgr.stop(waitTimer);
34521             waitTimer = null;
34522         }
34523     };
34524
34525     // private
34526     var updateButtons = function(b){
34527         var width = 0;
34528         if(!b){
34529             buttons["ok"].hide();
34530             buttons["cancel"].hide();
34531             buttons["yes"].hide();
34532             buttons["no"].hide();
34533             dlg.footer.dom.style.display = 'none';
34534             return width;
34535         }
34536         dlg.footer.dom.style.display = '';
34537         for(var k in buttons){
34538             if(typeof buttons[k] != "function"){
34539                 if(b[k]){
34540                     buttons[k].show();
34541                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34542                     width += buttons[k].el.getWidth()+15;
34543                 }else{
34544                     buttons[k].hide();
34545                 }
34546             }
34547         }
34548         return width;
34549     };
34550
34551     // private
34552     var handleEsc = function(d, k, e){
34553         if(opt && opt.closable !== false){
34554             dlg.hide();
34555         }
34556         if(e){
34557             e.stopEvent();
34558         }
34559     };
34560
34561     return {
34562         /**
34563          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34564          * @return {Roo.BasicDialog} The BasicDialog element
34565          */
34566         getDialog : function(){
34567            if(!dlg){
34568                 dlg = new Roo.BasicDialog("x-msg-box", {
34569                     autoCreate : true,
34570                     shadow: true,
34571                     draggable: true,
34572                     resizable:false,
34573                     constraintoviewport:false,
34574                     fixedcenter:true,
34575                     collapsible : false,
34576                     shim:true,
34577                     modal: true,
34578                     width:400, height:100,
34579                     buttonAlign:"center",
34580                     closeClick : function(){
34581                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34582                             handleButton("no");
34583                         }else{
34584                             handleButton("cancel");
34585                         }
34586                     }
34587                 });
34588                 dlg.on("hide", handleHide);
34589                 mask = dlg.mask;
34590                 dlg.addKeyListener(27, handleEsc);
34591                 buttons = {};
34592                 var bt = this.buttonText;
34593                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34594                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34595                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34596                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34597                 bodyEl = dlg.body.createChild({
34598
34599                     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>'
34600                 });
34601                 msgEl = bodyEl.dom.firstChild;
34602                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34603                 textboxEl.enableDisplayMode();
34604                 textboxEl.addKeyListener([10,13], function(){
34605                     if(dlg.isVisible() && opt && opt.buttons){
34606                         if(opt.buttons.ok){
34607                             handleButton("ok");
34608                         }else if(opt.buttons.yes){
34609                             handleButton("yes");
34610                         }
34611                     }
34612                 });
34613                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34614                 textareaEl.enableDisplayMode();
34615                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34616                 progressEl.enableDisplayMode();
34617                 var pf = progressEl.dom.firstChild;
34618                 if (pf) {
34619                     pp = Roo.get(pf.firstChild);
34620                     pp.setHeight(pf.offsetHeight);
34621                 }
34622                 
34623             }
34624             return dlg;
34625         },
34626
34627         /**
34628          * Updates the message box body text
34629          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34630          * the XHTML-compliant non-breaking space character '&amp;#160;')
34631          * @return {Roo.MessageBox} This message box
34632          */
34633         updateText : function(text){
34634             if(!dlg.isVisible() && !opt.width){
34635                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34636             }
34637             msgEl.innerHTML = text || '&#160;';
34638       
34639             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34640             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34641             var w = Math.max(
34642                     Math.min(opt.width || cw , this.maxWidth), 
34643                     Math.max(opt.minWidth || this.minWidth, bwidth)
34644             );
34645             if(opt.prompt){
34646                 activeTextEl.setWidth(w);
34647             }
34648             if(dlg.isVisible()){
34649                 dlg.fixedcenter = false;
34650             }
34651             // to big, make it scroll. = But as usual stupid IE does not support
34652             // !important..
34653             
34654             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34655                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34656                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34657             } else {
34658                 bodyEl.dom.style.height = '';
34659                 bodyEl.dom.style.overflowY = '';
34660             }
34661             if (cw > w) {
34662                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34663             } else {
34664                 bodyEl.dom.style.overflowX = '';
34665             }
34666             
34667             dlg.setContentSize(w, bodyEl.getHeight());
34668             if(dlg.isVisible()){
34669                 dlg.fixedcenter = true;
34670             }
34671             return this;
34672         },
34673
34674         /**
34675          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34676          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34677          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34678          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34679          * @return {Roo.MessageBox} This message box
34680          */
34681         updateProgress : function(value, text){
34682             if(text){
34683                 this.updateText(text);
34684             }
34685             if (pp) { // weird bug on my firefox - for some reason this is not defined
34686                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34687             }
34688             return this;
34689         },        
34690
34691         /**
34692          * Returns true if the message box is currently displayed
34693          * @return {Boolean} True if the message box is visible, else false
34694          */
34695         isVisible : function(){
34696             return dlg && dlg.isVisible();  
34697         },
34698
34699         /**
34700          * Hides the message box if it is displayed
34701          */
34702         hide : function(){
34703             if(this.isVisible()){
34704                 dlg.hide();
34705             }  
34706         },
34707
34708         /**
34709          * Displays a new message box, or reinitializes an existing message box, based on the config options
34710          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34711          * The following config object properties are supported:
34712          * <pre>
34713 Property    Type             Description
34714 ----------  ---------------  ------------------------------------------------------------------------------------
34715 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34716                                    closes (defaults to undefined)
34717 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34718                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34719 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34720                                    progress and wait dialogs will ignore this property and always hide the
34721                                    close button as they can only be closed programmatically.
34722 cls               String           A custom CSS class to apply to the message box element
34723 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34724                                    displayed (defaults to 75)
34725 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34726                                    function will be btn (the name of the button that was clicked, if applicable,
34727                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34728                                    Progress and wait dialogs will ignore this option since they do not respond to
34729                                    user actions and can only be closed programmatically, so any required function
34730                                    should be called by the same code after it closes the dialog.
34731 icon              String           A CSS class that provides a background image to be used as an icon for
34732                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34733 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34734 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34735 modal             Boolean          False to allow user interaction with the page while the message box is
34736                                    displayed (defaults to true)
34737 msg               String           A string that will replace the existing message box body text (defaults
34738                                    to the XHTML-compliant non-breaking space character '&#160;')
34739 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34740 progress          Boolean          True to display a progress bar (defaults to false)
34741 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34742 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34743 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34744 title             String           The title text
34745 value             String           The string value to set into the active textbox element if displayed
34746 wait              Boolean          True to display a progress bar (defaults to false)
34747 width             Number           The width of the dialog in pixels
34748 </pre>
34749          *
34750          * Example usage:
34751          * <pre><code>
34752 Roo.Msg.show({
34753    title: 'Address',
34754    msg: 'Please enter your address:',
34755    width: 300,
34756    buttons: Roo.MessageBox.OKCANCEL,
34757    multiline: true,
34758    fn: saveAddress,
34759    animEl: 'addAddressBtn'
34760 });
34761 </code></pre>
34762          * @param {Object} config Configuration options
34763          * @return {Roo.MessageBox} This message box
34764          */
34765         show : function(options)
34766         {
34767             
34768             // this causes nightmares if you show one dialog after another
34769             // especially on callbacks..
34770              
34771             if(this.isVisible()){
34772                 
34773                 this.hide();
34774                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34775                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34776                 Roo.log("New Dialog Message:" +  options.msg )
34777                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34778                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34779                 
34780             }
34781             var d = this.getDialog();
34782             opt = options;
34783             d.setTitle(opt.title || "&#160;");
34784             d.close.setDisplayed(opt.closable !== false);
34785             activeTextEl = textboxEl;
34786             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34787             if(opt.prompt){
34788                 if(opt.multiline){
34789                     textboxEl.hide();
34790                     textareaEl.show();
34791                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34792                         opt.multiline : this.defaultTextHeight);
34793                     activeTextEl = textareaEl;
34794                 }else{
34795                     textboxEl.show();
34796                     textareaEl.hide();
34797                 }
34798             }else{
34799                 textboxEl.hide();
34800                 textareaEl.hide();
34801             }
34802             progressEl.setDisplayed(opt.progress === true);
34803             this.updateProgress(0);
34804             activeTextEl.dom.value = opt.value || "";
34805             if(opt.prompt){
34806                 dlg.setDefaultButton(activeTextEl);
34807             }else{
34808                 var bs = opt.buttons;
34809                 var db = null;
34810                 if(bs && bs.ok){
34811                     db = buttons["ok"];
34812                 }else if(bs && bs.yes){
34813                     db = buttons["yes"];
34814                 }
34815                 dlg.setDefaultButton(db);
34816             }
34817             bwidth = updateButtons(opt.buttons);
34818             this.updateText(opt.msg);
34819             if(opt.cls){
34820                 d.el.addClass(opt.cls);
34821             }
34822             d.proxyDrag = opt.proxyDrag === true;
34823             d.modal = opt.modal !== false;
34824             d.mask = opt.modal !== false ? mask : false;
34825             if(!d.isVisible()){
34826                 // force it to the end of the z-index stack so it gets a cursor in FF
34827                 document.body.appendChild(dlg.el.dom);
34828                 d.animateTarget = null;
34829                 d.show(options.animEl);
34830             }
34831             return this;
34832         },
34833
34834         /**
34835          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34836          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34837          * and closing the message box when the process is complete.
34838          * @param {String} title The title bar text
34839          * @param {String} msg The message box body text
34840          * @return {Roo.MessageBox} This message box
34841          */
34842         progress : function(title, msg){
34843             this.show({
34844                 title : title,
34845                 msg : msg,
34846                 buttons: false,
34847                 progress:true,
34848                 closable:false,
34849                 minWidth: this.minProgressWidth,
34850                 modal : true
34851             });
34852             return this;
34853         },
34854
34855         /**
34856          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34857          * If a callback function is passed it will be called after the user clicks the button, and the
34858          * id of the button that was clicked will be passed as the only parameter to the callback
34859          * (could also be the top-right close button).
34860          * @param {String} title The title bar text
34861          * @param {String} msg The message box body text
34862          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34863          * @param {Object} scope (optional) The scope of the callback function
34864          * @return {Roo.MessageBox} This message box
34865          */
34866         alert : function(title, msg, fn, scope){
34867             this.show({
34868                 title : title,
34869                 msg : msg,
34870                 buttons: this.OK,
34871                 fn: fn,
34872                 scope : scope,
34873                 modal : true
34874             });
34875             return this;
34876         },
34877
34878         /**
34879          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34880          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34881          * You are responsible for closing the message box when the process is complete.
34882          * @param {String} msg The message box body text
34883          * @param {String} title (optional) The title bar text
34884          * @return {Roo.MessageBox} This message box
34885          */
34886         wait : function(msg, title){
34887             this.show({
34888                 title : title,
34889                 msg : msg,
34890                 buttons: false,
34891                 closable:false,
34892                 progress:true,
34893                 modal:true,
34894                 width:300,
34895                 wait:true
34896             });
34897             waitTimer = Roo.TaskMgr.start({
34898                 run: function(i){
34899                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34900                 },
34901                 interval: 1000
34902             });
34903             return this;
34904         },
34905
34906         /**
34907          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34908          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34909          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34910          * @param {String} title The title bar text
34911          * @param {String} msg The message box body text
34912          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34913          * @param {Object} scope (optional) The scope of the callback function
34914          * @return {Roo.MessageBox} This message box
34915          */
34916         confirm : function(title, msg, fn, scope){
34917             this.show({
34918                 title : title,
34919                 msg : msg,
34920                 buttons: this.YESNO,
34921                 fn: fn,
34922                 scope : scope,
34923                 modal : true
34924             });
34925             return this;
34926         },
34927
34928         /**
34929          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34930          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34931          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34932          * (could also be the top-right close button) and the text that was entered will be passed as the two
34933          * parameters to the callback.
34934          * @param {String} title The title bar text
34935          * @param {String} msg The message box body text
34936          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34937          * @param {Object} scope (optional) The scope of the callback function
34938          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34939          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34940          * @return {Roo.MessageBox} This message box
34941          */
34942         prompt : function(title, msg, fn, scope, multiline){
34943             this.show({
34944                 title : title,
34945                 msg : msg,
34946                 buttons: this.OKCANCEL,
34947                 fn: fn,
34948                 minWidth:250,
34949                 scope : scope,
34950                 prompt:true,
34951                 multiline: multiline,
34952                 modal : true
34953             });
34954             return this;
34955         },
34956
34957         /**
34958          * Button config that displays a single OK button
34959          * @type Object
34960          */
34961         OK : {ok:true},
34962         /**
34963          * Button config that displays Yes and No buttons
34964          * @type Object
34965          */
34966         YESNO : {yes:true, no:true},
34967         /**
34968          * Button config that displays OK and Cancel buttons
34969          * @type Object
34970          */
34971         OKCANCEL : {ok:true, cancel:true},
34972         /**
34973          * Button config that displays Yes, No and Cancel buttons
34974          * @type Object
34975          */
34976         YESNOCANCEL : {yes:true, no:true, cancel:true},
34977
34978         /**
34979          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34980          * @type Number
34981          */
34982         defaultTextHeight : 75,
34983         /**
34984          * The maximum width in pixels of the message box (defaults to 600)
34985          * @type Number
34986          */
34987         maxWidth : 600,
34988         /**
34989          * The minimum width in pixels of the message box (defaults to 100)
34990          * @type Number
34991          */
34992         minWidth : 100,
34993         /**
34994          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34995          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34996          * @type Number
34997          */
34998         minProgressWidth : 250,
34999         /**
35000          * An object containing the default button text strings that can be overriden for localized language support.
35001          * Supported properties are: ok, cancel, yes and no.
35002          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35003          * @type Object
35004          */
35005         buttonText : {
35006             ok : "OK",
35007             cancel : "Cancel",
35008             yes : "Yes",
35009             no : "No"
35010         }
35011     };
35012 }();
35013
35014 /**
35015  * Shorthand for {@link Roo.MessageBox}
35016  */
35017 Roo.Msg = Roo.MessageBox;/*
35018  * Based on:
35019  * Ext JS Library 1.1.1
35020  * Copyright(c) 2006-2007, Ext JS, LLC.
35021  *
35022  * Originally Released Under LGPL - original licence link has changed is not relivant.
35023  *
35024  * Fork - LGPL
35025  * <script type="text/javascript">
35026  */
35027 /**
35028  * @class Roo.QuickTips
35029  * Provides attractive and customizable tooltips for any element.
35030  * @static
35031  */
35032 Roo.QuickTips = function(){
35033     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35034     var ce, bd, xy, dd;
35035     var visible = false, disabled = true, inited = false;
35036     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35037     
35038     var onOver = function(e){
35039         if(disabled){
35040             return;
35041         }
35042         var t = e.getTarget();
35043         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35044             return;
35045         }
35046         if(ce && t == ce.el){
35047             clearTimeout(hideProc);
35048             return;
35049         }
35050         if(t && tagEls[t.id]){
35051             tagEls[t.id].el = t;
35052             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35053             return;
35054         }
35055         var ttp, et = Roo.fly(t);
35056         var ns = cfg.namespace;
35057         if(tm.interceptTitles && t.title){
35058             ttp = t.title;
35059             t.qtip = ttp;
35060             t.removeAttribute("title");
35061             e.preventDefault();
35062         }else{
35063             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35064         }
35065         if(ttp){
35066             showProc = show.defer(tm.showDelay, tm, [{
35067                 el: t, 
35068                 text: ttp.replace(/\\n/g,'<br/>'),
35069                 width: et.getAttributeNS(ns, cfg.width),
35070                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35071                 title: et.getAttributeNS(ns, cfg.title),
35072                     cls: et.getAttributeNS(ns, cfg.cls)
35073             }]);
35074         }
35075     };
35076     
35077     var onOut = function(e){
35078         clearTimeout(showProc);
35079         var t = e.getTarget();
35080         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35081             hideProc = setTimeout(hide, tm.hideDelay);
35082         }
35083     };
35084     
35085     var onMove = function(e){
35086         if(disabled){
35087             return;
35088         }
35089         xy = e.getXY();
35090         xy[1] += 18;
35091         if(tm.trackMouse && ce){
35092             el.setXY(xy);
35093         }
35094     };
35095     
35096     var onDown = function(e){
35097         clearTimeout(showProc);
35098         clearTimeout(hideProc);
35099         if(!e.within(el)){
35100             if(tm.hideOnClick){
35101                 hide();
35102                 tm.disable();
35103                 tm.enable.defer(100, tm);
35104             }
35105         }
35106     };
35107     
35108     var getPad = function(){
35109         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35110     };
35111
35112     var show = function(o){
35113         if(disabled){
35114             return;
35115         }
35116         clearTimeout(dismissProc);
35117         ce = o;
35118         if(removeCls){ // in case manually hidden
35119             el.removeClass(removeCls);
35120             removeCls = null;
35121         }
35122         if(ce.cls){
35123             el.addClass(ce.cls);
35124             removeCls = ce.cls;
35125         }
35126         if(ce.title){
35127             tipTitle.update(ce.title);
35128             tipTitle.show();
35129         }else{
35130             tipTitle.update('');
35131             tipTitle.hide();
35132         }
35133         el.dom.style.width  = tm.maxWidth+'px';
35134         //tipBody.dom.style.width = '';
35135         tipBodyText.update(o.text);
35136         var p = getPad(), w = ce.width;
35137         if(!w){
35138             var td = tipBodyText.dom;
35139             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35140             if(aw > tm.maxWidth){
35141                 w = tm.maxWidth;
35142             }else if(aw < tm.minWidth){
35143                 w = tm.minWidth;
35144             }else{
35145                 w = aw;
35146             }
35147         }
35148         //tipBody.setWidth(w);
35149         el.setWidth(parseInt(w, 10) + p);
35150         if(ce.autoHide === false){
35151             close.setDisplayed(true);
35152             if(dd){
35153                 dd.unlock();
35154             }
35155         }else{
35156             close.setDisplayed(false);
35157             if(dd){
35158                 dd.lock();
35159             }
35160         }
35161         if(xy){
35162             el.avoidY = xy[1]-18;
35163             el.setXY(xy);
35164         }
35165         if(tm.animate){
35166             el.setOpacity(.1);
35167             el.setStyle("visibility", "visible");
35168             el.fadeIn({callback: afterShow});
35169         }else{
35170             afterShow();
35171         }
35172     };
35173     
35174     var afterShow = function(){
35175         if(ce){
35176             el.show();
35177             esc.enable();
35178             if(tm.autoDismiss && ce.autoHide !== false){
35179                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35180             }
35181         }
35182     };
35183     
35184     var hide = function(noanim){
35185         clearTimeout(dismissProc);
35186         clearTimeout(hideProc);
35187         ce = null;
35188         if(el.isVisible()){
35189             esc.disable();
35190             if(noanim !== true && tm.animate){
35191                 el.fadeOut({callback: afterHide});
35192             }else{
35193                 afterHide();
35194             } 
35195         }
35196     };
35197     
35198     var afterHide = function(){
35199         el.hide();
35200         if(removeCls){
35201             el.removeClass(removeCls);
35202             removeCls = null;
35203         }
35204     };
35205     
35206     return {
35207         /**
35208         * @cfg {Number} minWidth
35209         * The minimum width of the quick tip (defaults to 40)
35210         */
35211        minWidth : 40,
35212         /**
35213         * @cfg {Number} maxWidth
35214         * The maximum width of the quick tip (defaults to 300)
35215         */
35216        maxWidth : 300,
35217         /**
35218         * @cfg {Boolean} interceptTitles
35219         * True to automatically use the element's DOM title value if available (defaults to false)
35220         */
35221        interceptTitles : false,
35222         /**
35223         * @cfg {Boolean} trackMouse
35224         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35225         */
35226        trackMouse : false,
35227         /**
35228         * @cfg {Boolean} hideOnClick
35229         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35230         */
35231        hideOnClick : true,
35232         /**
35233         * @cfg {Number} showDelay
35234         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35235         */
35236        showDelay : 500,
35237         /**
35238         * @cfg {Number} hideDelay
35239         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35240         */
35241        hideDelay : 200,
35242         /**
35243         * @cfg {Boolean} autoHide
35244         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35245         * Used in conjunction with hideDelay.
35246         */
35247        autoHide : true,
35248         /**
35249         * @cfg {Boolean}
35250         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35251         * (defaults to true).  Used in conjunction with autoDismissDelay.
35252         */
35253        autoDismiss : true,
35254         /**
35255         * @cfg {Number}
35256         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35257         */
35258        autoDismissDelay : 5000,
35259        /**
35260         * @cfg {Boolean} animate
35261         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35262         */
35263        animate : false,
35264
35265        /**
35266         * @cfg {String} title
35267         * Title text to display (defaults to '').  This can be any valid HTML markup.
35268         */
35269         title: '',
35270        /**
35271         * @cfg {String} text
35272         * Body text to display (defaults to '').  This can be any valid HTML markup.
35273         */
35274         text : '',
35275        /**
35276         * @cfg {String} cls
35277         * A CSS class to apply to the base quick tip element (defaults to '').
35278         */
35279         cls : '',
35280        /**
35281         * @cfg {Number} width
35282         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35283         * minWidth or maxWidth.
35284         */
35285         width : null,
35286
35287     /**
35288      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35289      * or display QuickTips in a page.
35290      */
35291        init : function(){
35292           tm = Roo.QuickTips;
35293           cfg = tm.tagConfig;
35294           if(!inited){
35295               if(!Roo.isReady){ // allow calling of init() before onReady
35296                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35297                   return;
35298               }
35299               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35300               el.fxDefaults = {stopFx: true};
35301               // maximum custom styling
35302               //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>');
35303               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>');              
35304               tipTitle = el.child('h3');
35305               tipTitle.enableDisplayMode("block");
35306               tipBody = el.child('div.x-tip-bd');
35307               tipBodyText = el.child('div.x-tip-bd-inner');
35308               //bdLeft = el.child('div.x-tip-bd-left');
35309               //bdRight = el.child('div.x-tip-bd-right');
35310               close = el.child('div.x-tip-close');
35311               close.enableDisplayMode("block");
35312               close.on("click", hide);
35313               var d = Roo.get(document);
35314               d.on("mousedown", onDown);
35315               d.on("mouseover", onOver);
35316               d.on("mouseout", onOut);
35317               d.on("mousemove", onMove);
35318               esc = d.addKeyListener(27, hide);
35319               esc.disable();
35320               if(Roo.dd.DD){
35321                   dd = el.initDD("default", null, {
35322                       onDrag : function(){
35323                           el.sync();  
35324                       }
35325                   });
35326                   dd.setHandleElId(tipTitle.id);
35327                   dd.lock();
35328               }
35329               inited = true;
35330           }
35331           this.enable(); 
35332        },
35333
35334     /**
35335      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35336      * are supported:
35337      * <pre>
35338 Property    Type                   Description
35339 ----------  ---------------------  ------------------------------------------------------------------------
35340 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35341      * </ul>
35342      * @param {Object} config The config object
35343      */
35344        register : function(config){
35345            var cs = config instanceof Array ? config : arguments;
35346            for(var i = 0, len = cs.length; i < len; i++) {
35347                var c = cs[i];
35348                var target = c.target;
35349                if(target){
35350                    if(target instanceof Array){
35351                        for(var j = 0, jlen = target.length; j < jlen; j++){
35352                            tagEls[target[j]] = c;
35353                        }
35354                    }else{
35355                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35356                    }
35357                }
35358            }
35359        },
35360
35361     /**
35362      * Removes this quick tip from its element and destroys it.
35363      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35364      */
35365        unregister : function(el){
35366            delete tagEls[Roo.id(el)];
35367        },
35368
35369     /**
35370      * Enable this quick tip.
35371      */
35372        enable : function(){
35373            if(inited && disabled){
35374                locks.pop();
35375                if(locks.length < 1){
35376                    disabled = false;
35377                }
35378            }
35379        },
35380
35381     /**
35382      * Disable this quick tip.
35383      */
35384        disable : function(){
35385           disabled = true;
35386           clearTimeout(showProc);
35387           clearTimeout(hideProc);
35388           clearTimeout(dismissProc);
35389           if(ce){
35390               hide(true);
35391           }
35392           locks.push(1);
35393        },
35394
35395     /**
35396      * Returns true if the quick tip is enabled, else false.
35397      */
35398        isEnabled : function(){
35399             return !disabled;
35400        },
35401
35402         // private
35403        tagConfig : {
35404            namespace : "roo", // was ext?? this may break..
35405            alt_namespace : "ext",
35406            attribute : "qtip",
35407            width : "width",
35408            target : "target",
35409            title : "qtitle",
35410            hide : "hide",
35411            cls : "qclass"
35412        }
35413    };
35414 }();
35415
35416 // backwards compat
35417 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35418  * Based on:
35419  * Ext JS Library 1.1.1
35420  * Copyright(c) 2006-2007, Ext JS, LLC.
35421  *
35422  * Originally Released Under LGPL - original licence link has changed is not relivant.
35423  *
35424  * Fork - LGPL
35425  * <script type="text/javascript">
35426  */
35427  
35428
35429 /**
35430  * @class Roo.tree.TreePanel
35431  * @extends Roo.data.Tree
35432  * @cfg {Roo.tree.TreeNode} root The root node
35433  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35434  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35435  * @cfg {Boolean} enableDD true to enable drag and drop
35436  * @cfg {Boolean} enableDrag true to enable just drag
35437  * @cfg {Boolean} enableDrop true to enable just drop
35438  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35439  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35440  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35441  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35442  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35443  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35444  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35445  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35446  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35447  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35448  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35449  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35450  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35451  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35452  * @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>
35453  * @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>
35454  * 
35455  * @constructor
35456  * @param {String/HTMLElement/Element} el The container element
35457  * @param {Object} config
35458  */
35459 Roo.tree.TreePanel = function(el, config){
35460     var root = false;
35461     var loader = false;
35462     if (config.root) {
35463         root = config.root;
35464         delete config.root;
35465     }
35466     if (config.loader) {
35467         loader = config.loader;
35468         delete config.loader;
35469     }
35470     
35471     Roo.apply(this, config);
35472     Roo.tree.TreePanel.superclass.constructor.call(this);
35473     this.el = Roo.get(el);
35474     this.el.addClass('x-tree');
35475     //console.log(root);
35476     if (root) {
35477         this.setRootNode( Roo.factory(root, Roo.tree));
35478     }
35479     if (loader) {
35480         this.loader = Roo.factory(loader, Roo.tree);
35481     }
35482    /**
35483     * Read-only. The id of the container element becomes this TreePanel's id.
35484     */
35485     this.id = this.el.id;
35486     this.addEvents({
35487         /**
35488         * @event beforeload
35489         * Fires before a node is loaded, return false to cancel
35490         * @param {Node} node The node being loaded
35491         */
35492         "beforeload" : true,
35493         /**
35494         * @event load
35495         * Fires when a node is loaded
35496         * @param {Node} node The node that was loaded
35497         */
35498         "load" : true,
35499         /**
35500         * @event textchange
35501         * Fires when the text for a node is changed
35502         * @param {Node} node The node
35503         * @param {String} text The new text
35504         * @param {String} oldText The old text
35505         */
35506         "textchange" : true,
35507         /**
35508         * @event beforeexpand
35509         * Fires before a node is expanded, return false to cancel.
35510         * @param {Node} node The node
35511         * @param {Boolean} deep
35512         * @param {Boolean} anim
35513         */
35514         "beforeexpand" : true,
35515         /**
35516         * @event beforecollapse
35517         * Fires before a node is collapsed, return false to cancel.
35518         * @param {Node} node The node
35519         * @param {Boolean} deep
35520         * @param {Boolean} anim
35521         */
35522         "beforecollapse" : true,
35523         /**
35524         * @event expand
35525         * Fires when a node is expanded
35526         * @param {Node} node The node
35527         */
35528         "expand" : true,
35529         /**
35530         * @event disabledchange
35531         * Fires when the disabled status of a node changes
35532         * @param {Node} node The node
35533         * @param {Boolean} disabled
35534         */
35535         "disabledchange" : true,
35536         /**
35537         * @event collapse
35538         * Fires when a node is collapsed
35539         * @param {Node} node The node
35540         */
35541         "collapse" : true,
35542         /**
35543         * @event beforeclick
35544         * Fires before click processing on a node. Return false to cancel the default action.
35545         * @param {Node} node The node
35546         * @param {Roo.EventObject} e The event object
35547         */
35548         "beforeclick":true,
35549         /**
35550         * @event checkchange
35551         * Fires when a node with a checkbox's checked property changes
35552         * @param {Node} this This node
35553         * @param {Boolean} checked
35554         */
35555         "checkchange":true,
35556         /**
35557         * @event click
35558         * Fires when a node is clicked
35559         * @param {Node} node The node
35560         * @param {Roo.EventObject} e The event object
35561         */
35562         "click":true,
35563         /**
35564         * @event dblclick
35565         * Fires when a node is double clicked
35566         * @param {Node} node The node
35567         * @param {Roo.EventObject} e The event object
35568         */
35569         "dblclick":true,
35570         /**
35571         * @event contextmenu
35572         * Fires when a node is right clicked
35573         * @param {Node} node The node
35574         * @param {Roo.EventObject} e The event object
35575         */
35576         "contextmenu":true,
35577         /**
35578         * @event beforechildrenrendered
35579         * Fires right before the child nodes for a node are rendered
35580         * @param {Node} node The node
35581         */
35582         "beforechildrenrendered":true,
35583         /**
35584         * @event startdrag
35585         * Fires when a node starts being dragged
35586         * @param {Roo.tree.TreePanel} this
35587         * @param {Roo.tree.TreeNode} node
35588         * @param {event} e The raw browser event
35589         */ 
35590        "startdrag" : true,
35591        /**
35592         * @event enddrag
35593         * Fires when a drag operation is complete
35594         * @param {Roo.tree.TreePanel} this
35595         * @param {Roo.tree.TreeNode} node
35596         * @param {event} e The raw browser event
35597         */
35598        "enddrag" : true,
35599        /**
35600         * @event dragdrop
35601         * Fires when a dragged node is dropped on a valid DD target
35602         * @param {Roo.tree.TreePanel} this
35603         * @param {Roo.tree.TreeNode} node
35604         * @param {DD} dd The dd it was dropped on
35605         * @param {event} e The raw browser event
35606         */
35607        "dragdrop" : true,
35608        /**
35609         * @event beforenodedrop
35610         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35611         * passed to handlers has the following properties:<br />
35612         * <ul style="padding:5px;padding-left:16px;">
35613         * <li>tree - The TreePanel</li>
35614         * <li>target - The node being targeted for the drop</li>
35615         * <li>data - The drag data from the drag source</li>
35616         * <li>point - The point of the drop - append, above or below</li>
35617         * <li>source - The drag source</li>
35618         * <li>rawEvent - Raw mouse event</li>
35619         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35620         * to be inserted by setting them on this object.</li>
35621         * <li>cancel - Set this to true to cancel the drop.</li>
35622         * </ul>
35623         * @param {Object} dropEvent
35624         */
35625        "beforenodedrop" : true,
35626        /**
35627         * @event nodedrop
35628         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35629         * passed to handlers has the following properties:<br />
35630         * <ul style="padding:5px;padding-left:16px;">
35631         * <li>tree - The TreePanel</li>
35632         * <li>target - The node being targeted for the drop</li>
35633         * <li>data - The drag data from the drag source</li>
35634         * <li>point - The point of the drop - append, above or below</li>
35635         * <li>source - The drag source</li>
35636         * <li>rawEvent - Raw mouse event</li>
35637         * <li>dropNode - Dropped node(s).</li>
35638         * </ul>
35639         * @param {Object} dropEvent
35640         */
35641        "nodedrop" : true,
35642         /**
35643         * @event nodedragover
35644         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35645         * passed to handlers has the following properties:<br />
35646         * <ul style="padding:5px;padding-left:16px;">
35647         * <li>tree - The TreePanel</li>
35648         * <li>target - The node being targeted for the drop</li>
35649         * <li>data - The drag data from the drag source</li>
35650         * <li>point - The point of the drop - append, above or below</li>
35651         * <li>source - The drag source</li>
35652         * <li>rawEvent - Raw mouse event</li>
35653         * <li>dropNode - Drop node(s) provided by the source.</li>
35654         * <li>cancel - Set this to true to signal drop not allowed.</li>
35655         * </ul>
35656         * @param {Object} dragOverEvent
35657         */
35658        "nodedragover" : true,
35659        /**
35660         * @event appendnode
35661         * Fires when append node to the tree
35662         * @param {Roo.tree.TreePanel} this
35663         * @param {Roo.tree.TreeNode} node
35664         * @param {Number} index The index of the newly appended node
35665         */
35666        "appendnode" : true
35667         
35668     });
35669     if(this.singleExpand){
35670        this.on("beforeexpand", this.restrictExpand, this);
35671     }
35672     if (this.editor) {
35673         this.editor.tree = this;
35674         this.editor = Roo.factory(this.editor, Roo.tree);
35675     }
35676     
35677     if (this.selModel) {
35678         this.selModel = Roo.factory(this.selModel, Roo.tree);
35679     }
35680    
35681 };
35682 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35683     rootVisible : true,
35684     animate: Roo.enableFx,
35685     lines : true,
35686     enableDD : false,
35687     hlDrop : Roo.enableFx,
35688   
35689     renderer: false,
35690     
35691     rendererTip: false,
35692     // private
35693     restrictExpand : function(node){
35694         var p = node.parentNode;
35695         if(p){
35696             if(p.expandedChild && p.expandedChild.parentNode == p){
35697                 p.expandedChild.collapse();
35698             }
35699             p.expandedChild = node;
35700         }
35701     },
35702
35703     // private override
35704     setRootNode : function(node){
35705         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35706         if(!this.rootVisible){
35707             node.ui = new Roo.tree.RootTreeNodeUI(node);
35708         }
35709         return node;
35710     },
35711
35712     /**
35713      * Returns the container element for this TreePanel
35714      */
35715     getEl : function(){
35716         return this.el;
35717     },
35718
35719     /**
35720      * Returns the default TreeLoader for this TreePanel
35721      */
35722     getLoader : function(){
35723         return this.loader;
35724     },
35725
35726     /**
35727      * Expand all nodes
35728      */
35729     expandAll : function(){
35730         this.root.expand(true);
35731     },
35732
35733     /**
35734      * Collapse all nodes
35735      */
35736     collapseAll : function(){
35737         this.root.collapse(true);
35738     },
35739
35740     /**
35741      * Returns the selection model used by this TreePanel
35742      */
35743     getSelectionModel : function(){
35744         if(!this.selModel){
35745             this.selModel = new Roo.tree.DefaultSelectionModel();
35746         }
35747         return this.selModel;
35748     },
35749
35750     /**
35751      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35752      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35753      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35754      * @return {Array}
35755      */
35756     getChecked : function(a, startNode){
35757         startNode = startNode || this.root;
35758         var r = [];
35759         var f = function(){
35760             if(this.attributes.checked){
35761                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35762             }
35763         }
35764         startNode.cascade(f);
35765         return r;
35766     },
35767
35768     /**
35769      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35770      * @param {String} path
35771      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35772      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35773      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35774      */
35775     expandPath : function(path, attr, callback){
35776         attr = attr || "id";
35777         var keys = path.split(this.pathSeparator);
35778         var curNode = this.root;
35779         if(curNode.attributes[attr] != keys[1]){ // invalid root
35780             if(callback){
35781                 callback(false, null);
35782             }
35783             return;
35784         }
35785         var index = 1;
35786         var f = function(){
35787             if(++index == keys.length){
35788                 if(callback){
35789                     callback(true, curNode);
35790                 }
35791                 return;
35792             }
35793             var c = curNode.findChild(attr, keys[index]);
35794             if(!c){
35795                 if(callback){
35796                     callback(false, curNode);
35797                 }
35798                 return;
35799             }
35800             curNode = c;
35801             c.expand(false, false, f);
35802         };
35803         curNode.expand(false, false, f);
35804     },
35805
35806     /**
35807      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35808      * @param {String} path
35809      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35810      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35811      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35812      */
35813     selectPath : function(path, attr, callback){
35814         attr = attr || "id";
35815         var keys = path.split(this.pathSeparator);
35816         var v = keys.pop();
35817         if(keys.length > 0){
35818             var f = function(success, node){
35819                 if(success && node){
35820                     var n = node.findChild(attr, v);
35821                     if(n){
35822                         n.select();
35823                         if(callback){
35824                             callback(true, n);
35825                         }
35826                     }else if(callback){
35827                         callback(false, n);
35828                     }
35829                 }else{
35830                     if(callback){
35831                         callback(false, n);
35832                     }
35833                 }
35834             };
35835             this.expandPath(keys.join(this.pathSeparator), attr, f);
35836         }else{
35837             this.root.select();
35838             if(callback){
35839                 callback(true, this.root);
35840             }
35841         }
35842     },
35843
35844     getTreeEl : function(){
35845         return this.el;
35846     },
35847
35848     /**
35849      * Trigger rendering of this TreePanel
35850      */
35851     render : function(){
35852         if (this.innerCt) {
35853             return this; // stop it rendering more than once!!
35854         }
35855         
35856         this.innerCt = this.el.createChild({tag:"ul",
35857                cls:"x-tree-root-ct " +
35858                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35859
35860         if(this.containerScroll){
35861             Roo.dd.ScrollManager.register(this.el);
35862         }
35863         if((this.enableDD || this.enableDrop) && !this.dropZone){
35864            /**
35865             * The dropZone used by this tree if drop is enabled
35866             * @type Roo.tree.TreeDropZone
35867             */
35868              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35869                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35870            });
35871         }
35872         if((this.enableDD || this.enableDrag) && !this.dragZone){
35873            /**
35874             * The dragZone used by this tree if drag is enabled
35875             * @type Roo.tree.TreeDragZone
35876             */
35877             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35878                ddGroup: this.ddGroup || "TreeDD",
35879                scroll: this.ddScroll
35880            });
35881         }
35882         this.getSelectionModel().init(this);
35883         if (!this.root) {
35884             Roo.log("ROOT not set in tree");
35885             return this;
35886         }
35887         this.root.render();
35888         if(!this.rootVisible){
35889             this.root.renderChildren();
35890         }
35891         return this;
35892     }
35893 });/*
35894  * Based on:
35895  * Ext JS Library 1.1.1
35896  * Copyright(c) 2006-2007, Ext JS, LLC.
35897  *
35898  * Originally Released Under LGPL - original licence link has changed is not relivant.
35899  *
35900  * Fork - LGPL
35901  * <script type="text/javascript">
35902  */
35903  
35904
35905 /**
35906  * @class Roo.tree.DefaultSelectionModel
35907  * @extends Roo.util.Observable
35908  * The default single selection for a TreePanel.
35909  * @param {Object} cfg Configuration
35910  */
35911 Roo.tree.DefaultSelectionModel = function(cfg){
35912    this.selNode = null;
35913    
35914    
35915    
35916    this.addEvents({
35917        /**
35918         * @event selectionchange
35919         * Fires when the selected node changes
35920         * @param {DefaultSelectionModel} this
35921         * @param {TreeNode} node the new selection
35922         */
35923        "selectionchange" : true,
35924
35925        /**
35926         * @event beforeselect
35927         * Fires before the selected node changes, return false to cancel the change
35928         * @param {DefaultSelectionModel} this
35929         * @param {TreeNode} node the new selection
35930         * @param {TreeNode} node the old selection
35931         */
35932        "beforeselect" : true
35933    });
35934    
35935     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35936 };
35937
35938 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35939     init : function(tree){
35940         this.tree = tree;
35941         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35942         tree.on("click", this.onNodeClick, this);
35943     },
35944     
35945     onNodeClick : function(node, e){
35946         if (e.ctrlKey && this.selNode == node)  {
35947             this.unselect(node);
35948             return;
35949         }
35950         this.select(node);
35951     },
35952     
35953     /**
35954      * Select a node.
35955      * @param {TreeNode} node The node to select
35956      * @return {TreeNode} The selected node
35957      */
35958     select : function(node){
35959         var last = this.selNode;
35960         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35961             if(last){
35962                 last.ui.onSelectedChange(false);
35963             }
35964             this.selNode = node;
35965             node.ui.onSelectedChange(true);
35966             this.fireEvent("selectionchange", this, node, last);
35967         }
35968         return node;
35969     },
35970     
35971     /**
35972      * Deselect a node.
35973      * @param {TreeNode} node The node to unselect
35974      */
35975     unselect : function(node){
35976         if(this.selNode == node){
35977             this.clearSelections();
35978         }    
35979     },
35980     
35981     /**
35982      * Clear all selections
35983      */
35984     clearSelections : function(){
35985         var n = this.selNode;
35986         if(n){
35987             n.ui.onSelectedChange(false);
35988             this.selNode = null;
35989             this.fireEvent("selectionchange", this, null);
35990         }
35991         return n;
35992     },
35993     
35994     /**
35995      * Get the selected node
35996      * @return {TreeNode} The selected node
35997      */
35998     getSelectedNode : function(){
35999         return this.selNode;    
36000     },
36001     
36002     /**
36003      * Returns true if the node is selected
36004      * @param {TreeNode} node The node to check
36005      * @return {Boolean}
36006      */
36007     isSelected : function(node){
36008         return this.selNode == node;  
36009     },
36010
36011     /**
36012      * Selects the node above the selected node in the tree, intelligently walking the nodes
36013      * @return TreeNode The new selection
36014      */
36015     selectPrevious : function(){
36016         var s = this.selNode || this.lastSelNode;
36017         if(!s){
36018             return null;
36019         }
36020         var ps = s.previousSibling;
36021         if(ps){
36022             if(!ps.isExpanded() || ps.childNodes.length < 1){
36023                 return this.select(ps);
36024             } else{
36025                 var lc = ps.lastChild;
36026                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36027                     lc = lc.lastChild;
36028                 }
36029                 return this.select(lc);
36030             }
36031         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36032             return this.select(s.parentNode);
36033         }
36034         return null;
36035     },
36036
36037     /**
36038      * Selects the node above the selected node in the tree, intelligently walking the nodes
36039      * @return TreeNode The new selection
36040      */
36041     selectNext : function(){
36042         var s = this.selNode || this.lastSelNode;
36043         if(!s){
36044             return null;
36045         }
36046         if(s.firstChild && s.isExpanded()){
36047              return this.select(s.firstChild);
36048          }else if(s.nextSibling){
36049              return this.select(s.nextSibling);
36050          }else if(s.parentNode){
36051             var newS = null;
36052             s.parentNode.bubble(function(){
36053                 if(this.nextSibling){
36054                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36055                     return false;
36056                 }
36057             });
36058             return newS;
36059          }
36060         return null;
36061     },
36062
36063     onKeyDown : function(e){
36064         var s = this.selNode || this.lastSelNode;
36065         // undesirable, but required
36066         var sm = this;
36067         if(!s){
36068             return;
36069         }
36070         var k = e.getKey();
36071         switch(k){
36072              case e.DOWN:
36073                  e.stopEvent();
36074                  this.selectNext();
36075              break;
36076              case e.UP:
36077                  e.stopEvent();
36078                  this.selectPrevious();
36079              break;
36080              case e.RIGHT:
36081                  e.preventDefault();
36082                  if(s.hasChildNodes()){
36083                      if(!s.isExpanded()){
36084                          s.expand();
36085                      }else if(s.firstChild){
36086                          this.select(s.firstChild, e);
36087                      }
36088                  }
36089              break;
36090              case e.LEFT:
36091                  e.preventDefault();
36092                  if(s.hasChildNodes() && s.isExpanded()){
36093                      s.collapse();
36094                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36095                      this.select(s.parentNode, e);
36096                  }
36097              break;
36098         };
36099     }
36100 });
36101
36102 /**
36103  * @class Roo.tree.MultiSelectionModel
36104  * @extends Roo.util.Observable
36105  * Multi selection for a TreePanel.
36106  * @param {Object} cfg Configuration
36107  */
36108 Roo.tree.MultiSelectionModel = function(){
36109    this.selNodes = [];
36110    this.selMap = {};
36111    this.addEvents({
36112        /**
36113         * @event selectionchange
36114         * Fires when the selected nodes change
36115         * @param {MultiSelectionModel} this
36116         * @param {Array} nodes Array of the selected nodes
36117         */
36118        "selectionchange" : true
36119    });
36120    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36121    
36122 };
36123
36124 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36125     init : function(tree){
36126         this.tree = tree;
36127         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36128         tree.on("click", this.onNodeClick, this);
36129     },
36130     
36131     onNodeClick : function(node, e){
36132         this.select(node, e, e.ctrlKey);
36133     },
36134     
36135     /**
36136      * Select a node.
36137      * @param {TreeNode} node The node to select
36138      * @param {EventObject} e (optional) An event associated with the selection
36139      * @param {Boolean} keepExisting True to retain existing selections
36140      * @return {TreeNode} The selected node
36141      */
36142     select : function(node, e, keepExisting){
36143         if(keepExisting !== true){
36144             this.clearSelections(true);
36145         }
36146         if(this.isSelected(node)){
36147             this.lastSelNode = node;
36148             return node;
36149         }
36150         this.selNodes.push(node);
36151         this.selMap[node.id] = node;
36152         this.lastSelNode = node;
36153         node.ui.onSelectedChange(true);
36154         this.fireEvent("selectionchange", this, this.selNodes);
36155         return node;
36156     },
36157     
36158     /**
36159      * Deselect a node.
36160      * @param {TreeNode} node The node to unselect
36161      */
36162     unselect : function(node){
36163         if(this.selMap[node.id]){
36164             node.ui.onSelectedChange(false);
36165             var sn = this.selNodes;
36166             var index = -1;
36167             if(sn.indexOf){
36168                 index = sn.indexOf(node);
36169             }else{
36170                 for(var i = 0, len = sn.length; i < len; i++){
36171                     if(sn[i] == node){
36172                         index = i;
36173                         break;
36174                     }
36175                 }
36176             }
36177             if(index != -1){
36178                 this.selNodes.splice(index, 1);
36179             }
36180             delete this.selMap[node.id];
36181             this.fireEvent("selectionchange", this, this.selNodes);
36182         }
36183     },
36184     
36185     /**
36186      * Clear all selections
36187      */
36188     clearSelections : function(suppressEvent){
36189         var sn = this.selNodes;
36190         if(sn.length > 0){
36191             for(var i = 0, len = sn.length; i < len; i++){
36192                 sn[i].ui.onSelectedChange(false);
36193             }
36194             this.selNodes = [];
36195             this.selMap = {};
36196             if(suppressEvent !== true){
36197                 this.fireEvent("selectionchange", this, this.selNodes);
36198             }
36199         }
36200     },
36201     
36202     /**
36203      * Returns true if the node is selected
36204      * @param {TreeNode} node The node to check
36205      * @return {Boolean}
36206      */
36207     isSelected : function(node){
36208         return this.selMap[node.id] ? true : false;  
36209     },
36210     
36211     /**
36212      * Returns an array of the selected nodes
36213      * @return {Array}
36214      */
36215     getSelectedNodes : function(){
36216         return this.selNodes;    
36217     },
36218
36219     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36220
36221     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36222
36223     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36224 });/*
36225  * Based on:
36226  * Ext JS Library 1.1.1
36227  * Copyright(c) 2006-2007, Ext JS, LLC.
36228  *
36229  * Originally Released Under LGPL - original licence link has changed is not relivant.
36230  *
36231  * Fork - LGPL
36232  * <script type="text/javascript">
36233  */
36234  
36235 /**
36236  * @class Roo.tree.TreeNode
36237  * @extends Roo.data.Node
36238  * @cfg {String} text The text for this node
36239  * @cfg {Boolean} expanded true to start the node expanded
36240  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36241  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36242  * @cfg {Boolean} disabled true to start the node disabled
36243  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36244  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36245  * @cfg {String} cls A css class to be added to the node
36246  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36247  * @cfg {String} href URL of the link used for the node (defaults to #)
36248  * @cfg {String} hrefTarget target frame for the link
36249  * @cfg {String} qtip An Ext QuickTip for the node
36250  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36251  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36252  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36253  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36254  * (defaults to undefined with no checkbox rendered)
36255  * @constructor
36256  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36257  */
36258 Roo.tree.TreeNode = function(attributes){
36259     attributes = attributes || {};
36260     if(typeof attributes == "string"){
36261         attributes = {text: attributes};
36262     }
36263     this.childrenRendered = false;
36264     this.rendered = false;
36265     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36266     this.expanded = attributes.expanded === true;
36267     this.isTarget = attributes.isTarget !== false;
36268     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36269     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36270
36271     /**
36272      * Read-only. The text for this node. To change it use setText().
36273      * @type String
36274      */
36275     this.text = attributes.text;
36276     /**
36277      * True if this node is disabled.
36278      * @type Boolean
36279      */
36280     this.disabled = attributes.disabled === true;
36281
36282     this.addEvents({
36283         /**
36284         * @event textchange
36285         * Fires when the text for this node is changed
36286         * @param {Node} this This node
36287         * @param {String} text The new text
36288         * @param {String} oldText The old text
36289         */
36290         "textchange" : true,
36291         /**
36292         * @event beforeexpand
36293         * Fires before this node is expanded, return false to cancel.
36294         * @param {Node} this This node
36295         * @param {Boolean} deep
36296         * @param {Boolean} anim
36297         */
36298         "beforeexpand" : true,
36299         /**
36300         * @event beforecollapse
36301         * Fires before this node is collapsed, return false to cancel.
36302         * @param {Node} this This node
36303         * @param {Boolean} deep
36304         * @param {Boolean} anim
36305         */
36306         "beforecollapse" : true,
36307         /**
36308         * @event expand
36309         * Fires when this node is expanded
36310         * @param {Node} this This node
36311         */
36312         "expand" : true,
36313         /**
36314         * @event disabledchange
36315         * Fires when the disabled status of this node changes
36316         * @param {Node} this This node
36317         * @param {Boolean} disabled
36318         */
36319         "disabledchange" : true,
36320         /**
36321         * @event collapse
36322         * Fires when this node is collapsed
36323         * @param {Node} this This node
36324         */
36325         "collapse" : true,
36326         /**
36327         * @event beforeclick
36328         * Fires before click processing. Return false to cancel the default action.
36329         * @param {Node} this This node
36330         * @param {Roo.EventObject} e The event object
36331         */
36332         "beforeclick":true,
36333         /**
36334         * @event checkchange
36335         * Fires when a node with a checkbox's checked property changes
36336         * @param {Node} this This node
36337         * @param {Boolean} checked
36338         */
36339         "checkchange":true,
36340         /**
36341         * @event click
36342         * Fires when this node is clicked
36343         * @param {Node} this This node
36344         * @param {Roo.EventObject} e The event object
36345         */
36346         "click":true,
36347         /**
36348         * @event dblclick
36349         * Fires when this node is double clicked
36350         * @param {Node} this This node
36351         * @param {Roo.EventObject} e The event object
36352         */
36353         "dblclick":true,
36354         /**
36355         * @event contextmenu
36356         * Fires when this node is right clicked
36357         * @param {Node} this This node
36358         * @param {Roo.EventObject} e The event object
36359         */
36360         "contextmenu":true,
36361         /**
36362         * @event beforechildrenrendered
36363         * Fires right before the child nodes for this node are rendered
36364         * @param {Node} this This node
36365         */
36366         "beforechildrenrendered":true
36367     });
36368
36369     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36370
36371     /**
36372      * Read-only. The UI for this node
36373      * @type TreeNodeUI
36374      */
36375     this.ui = new uiClass(this);
36376     
36377     // finally support items[]
36378     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36379         return;
36380     }
36381     
36382     
36383     Roo.each(this.attributes.items, function(c) {
36384         this.appendChild(Roo.factory(c,Roo.Tree));
36385     }, this);
36386     delete this.attributes.items;
36387     
36388     
36389     
36390 };
36391 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36392     preventHScroll: true,
36393     /**
36394      * Returns true if this node is expanded
36395      * @return {Boolean}
36396      */
36397     isExpanded : function(){
36398         return this.expanded;
36399     },
36400
36401     /**
36402      * Returns the UI object for this node
36403      * @return {TreeNodeUI}
36404      */
36405     getUI : function(){
36406         return this.ui;
36407     },
36408
36409     // private override
36410     setFirstChild : function(node){
36411         var of = this.firstChild;
36412         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36413         if(this.childrenRendered && of && node != of){
36414             of.renderIndent(true, true);
36415         }
36416         if(this.rendered){
36417             this.renderIndent(true, true);
36418         }
36419     },
36420
36421     // private override
36422     setLastChild : function(node){
36423         var ol = this.lastChild;
36424         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36425         if(this.childrenRendered && ol && node != ol){
36426             ol.renderIndent(true, true);
36427         }
36428         if(this.rendered){
36429             this.renderIndent(true, true);
36430         }
36431     },
36432
36433     // these methods are overridden to provide lazy rendering support
36434     // private override
36435     appendChild : function()
36436     {
36437         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36438         if(node && this.childrenRendered){
36439             node.render();
36440         }
36441         this.ui.updateExpandIcon();
36442         return node;
36443     },
36444
36445     // private override
36446     removeChild : function(node){
36447         this.ownerTree.getSelectionModel().unselect(node);
36448         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36449         // if it's been rendered remove dom node
36450         if(this.childrenRendered){
36451             node.ui.remove();
36452         }
36453         if(this.childNodes.length < 1){
36454             this.collapse(false, false);
36455         }else{
36456             this.ui.updateExpandIcon();
36457         }
36458         if(!this.firstChild) {
36459             this.childrenRendered = false;
36460         }
36461         return node;
36462     },
36463
36464     // private override
36465     insertBefore : function(node, refNode){
36466         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36467         if(newNode && refNode && this.childrenRendered){
36468             node.render();
36469         }
36470         this.ui.updateExpandIcon();
36471         return newNode;
36472     },
36473
36474     /**
36475      * Sets the text for this node
36476      * @param {String} text
36477      */
36478     setText : function(text){
36479         var oldText = this.text;
36480         this.text = text;
36481         this.attributes.text = text;
36482         if(this.rendered){ // event without subscribing
36483             this.ui.onTextChange(this, text, oldText);
36484         }
36485         this.fireEvent("textchange", this, text, oldText);
36486     },
36487
36488     /**
36489      * Triggers selection of this node
36490      */
36491     select : function(){
36492         this.getOwnerTree().getSelectionModel().select(this);
36493     },
36494
36495     /**
36496      * Triggers deselection of this node
36497      */
36498     unselect : function(){
36499         this.getOwnerTree().getSelectionModel().unselect(this);
36500     },
36501
36502     /**
36503      * Returns true if this node is selected
36504      * @return {Boolean}
36505      */
36506     isSelected : function(){
36507         return this.getOwnerTree().getSelectionModel().isSelected(this);
36508     },
36509
36510     /**
36511      * Expand this node.
36512      * @param {Boolean} deep (optional) True to expand all children as well
36513      * @param {Boolean} anim (optional) false to cancel the default animation
36514      * @param {Function} callback (optional) A callback to be called when
36515      * expanding this node completes (does not wait for deep expand to complete).
36516      * Called with 1 parameter, this node.
36517      */
36518     expand : function(deep, anim, callback){
36519         if(!this.expanded){
36520             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36521                 return;
36522             }
36523             if(!this.childrenRendered){
36524                 this.renderChildren();
36525             }
36526             this.expanded = true;
36527             
36528             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36529                 this.ui.animExpand(function(){
36530                     this.fireEvent("expand", this);
36531                     if(typeof callback == "function"){
36532                         callback(this);
36533                     }
36534                     if(deep === true){
36535                         this.expandChildNodes(true);
36536                     }
36537                 }.createDelegate(this));
36538                 return;
36539             }else{
36540                 this.ui.expand();
36541                 this.fireEvent("expand", this);
36542                 if(typeof callback == "function"){
36543                     callback(this);
36544                 }
36545             }
36546         }else{
36547            if(typeof callback == "function"){
36548                callback(this);
36549            }
36550         }
36551         if(deep === true){
36552             this.expandChildNodes(true);
36553         }
36554     },
36555
36556     isHiddenRoot : function(){
36557         return this.isRoot && !this.getOwnerTree().rootVisible;
36558     },
36559
36560     /**
36561      * Collapse this node.
36562      * @param {Boolean} deep (optional) True to collapse all children as well
36563      * @param {Boolean} anim (optional) false to cancel the default animation
36564      */
36565     collapse : function(deep, anim){
36566         if(this.expanded && !this.isHiddenRoot()){
36567             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36568                 return;
36569             }
36570             this.expanded = false;
36571             if((this.getOwnerTree().animate && anim !== false) || anim){
36572                 this.ui.animCollapse(function(){
36573                     this.fireEvent("collapse", this);
36574                     if(deep === true){
36575                         this.collapseChildNodes(true);
36576                     }
36577                 }.createDelegate(this));
36578                 return;
36579             }else{
36580                 this.ui.collapse();
36581                 this.fireEvent("collapse", this);
36582             }
36583         }
36584         if(deep === true){
36585             var cs = this.childNodes;
36586             for(var i = 0, len = cs.length; i < len; i++) {
36587                 cs[i].collapse(true, false);
36588             }
36589         }
36590     },
36591
36592     // private
36593     delayedExpand : function(delay){
36594         if(!this.expandProcId){
36595             this.expandProcId = this.expand.defer(delay, this);
36596         }
36597     },
36598
36599     // private
36600     cancelExpand : function(){
36601         if(this.expandProcId){
36602             clearTimeout(this.expandProcId);
36603         }
36604         this.expandProcId = false;
36605     },
36606
36607     /**
36608      * Toggles expanded/collapsed state of the node
36609      */
36610     toggle : function(){
36611         if(this.expanded){
36612             this.collapse();
36613         }else{
36614             this.expand();
36615         }
36616     },
36617
36618     /**
36619      * Ensures all parent nodes are expanded
36620      */
36621     ensureVisible : function(callback){
36622         var tree = this.getOwnerTree();
36623         tree.expandPath(this.parentNode.getPath(), false, function(){
36624             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36625             Roo.callback(callback);
36626         }.createDelegate(this));
36627     },
36628
36629     /**
36630      * Expand all child nodes
36631      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36632      */
36633     expandChildNodes : function(deep){
36634         var cs = this.childNodes;
36635         for(var i = 0, len = cs.length; i < len; i++) {
36636                 cs[i].expand(deep);
36637         }
36638     },
36639
36640     /**
36641      * Collapse all child nodes
36642      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36643      */
36644     collapseChildNodes : function(deep){
36645         var cs = this.childNodes;
36646         for(var i = 0, len = cs.length; i < len; i++) {
36647                 cs[i].collapse(deep);
36648         }
36649     },
36650
36651     /**
36652      * Disables this node
36653      */
36654     disable : function(){
36655         this.disabled = true;
36656         this.unselect();
36657         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36658             this.ui.onDisableChange(this, true);
36659         }
36660         this.fireEvent("disabledchange", this, true);
36661     },
36662
36663     /**
36664      * Enables this node
36665      */
36666     enable : function(){
36667         this.disabled = false;
36668         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36669             this.ui.onDisableChange(this, false);
36670         }
36671         this.fireEvent("disabledchange", this, false);
36672     },
36673
36674     // private
36675     renderChildren : function(suppressEvent){
36676         if(suppressEvent !== false){
36677             this.fireEvent("beforechildrenrendered", this);
36678         }
36679         var cs = this.childNodes;
36680         for(var i = 0, len = cs.length; i < len; i++){
36681             cs[i].render(true);
36682         }
36683         this.childrenRendered = true;
36684     },
36685
36686     // private
36687     sort : function(fn, scope){
36688         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36689         if(this.childrenRendered){
36690             var cs = this.childNodes;
36691             for(var i = 0, len = cs.length; i < len; i++){
36692                 cs[i].render(true);
36693             }
36694         }
36695     },
36696
36697     // private
36698     render : function(bulkRender){
36699         this.ui.render(bulkRender);
36700         if(!this.rendered){
36701             this.rendered = true;
36702             if(this.expanded){
36703                 this.expanded = false;
36704                 this.expand(false, false);
36705             }
36706         }
36707     },
36708
36709     // private
36710     renderIndent : function(deep, refresh){
36711         if(refresh){
36712             this.ui.childIndent = null;
36713         }
36714         this.ui.renderIndent();
36715         if(deep === true && this.childrenRendered){
36716             var cs = this.childNodes;
36717             for(var i = 0, len = cs.length; i < len; i++){
36718                 cs[i].renderIndent(true, refresh);
36719             }
36720         }
36721     }
36722 });/*
36723  * Based on:
36724  * Ext JS Library 1.1.1
36725  * Copyright(c) 2006-2007, Ext JS, LLC.
36726  *
36727  * Originally Released Under LGPL - original licence link has changed is not relivant.
36728  *
36729  * Fork - LGPL
36730  * <script type="text/javascript">
36731  */
36732  
36733 /**
36734  * @class Roo.tree.AsyncTreeNode
36735  * @extends Roo.tree.TreeNode
36736  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36737  * @constructor
36738  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36739  */
36740  Roo.tree.AsyncTreeNode = function(config){
36741     this.loaded = false;
36742     this.loading = false;
36743     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36744     /**
36745     * @event beforeload
36746     * Fires before this node is loaded, return false to cancel
36747     * @param {Node} this This node
36748     */
36749     this.addEvents({'beforeload':true, 'load': true});
36750     /**
36751     * @event load
36752     * Fires when this node is loaded
36753     * @param {Node} this This node
36754     */
36755     /**
36756      * The loader used by this node (defaults to using the tree's defined loader)
36757      * @type TreeLoader
36758      * @property loader
36759      */
36760 };
36761 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36762     expand : function(deep, anim, callback){
36763         if(this.loading){ // if an async load is already running, waiting til it's done
36764             var timer;
36765             var f = function(){
36766                 if(!this.loading){ // done loading
36767                     clearInterval(timer);
36768                     this.expand(deep, anim, callback);
36769                 }
36770             }.createDelegate(this);
36771             timer = setInterval(f, 200);
36772             return;
36773         }
36774         if(!this.loaded){
36775             if(this.fireEvent("beforeload", this) === false){
36776                 return;
36777             }
36778             this.loading = true;
36779             this.ui.beforeLoad(this);
36780             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36781             if(loader){
36782                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36783                 return;
36784             }
36785         }
36786         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36787     },
36788     
36789     /**
36790      * Returns true if this node is currently loading
36791      * @return {Boolean}
36792      */
36793     isLoading : function(){
36794         return this.loading;  
36795     },
36796     
36797     loadComplete : function(deep, anim, callback){
36798         this.loading = false;
36799         this.loaded = true;
36800         this.ui.afterLoad(this);
36801         this.fireEvent("load", this);
36802         this.expand(deep, anim, callback);
36803     },
36804     
36805     /**
36806      * Returns true if this node has been loaded
36807      * @return {Boolean}
36808      */
36809     isLoaded : function(){
36810         return this.loaded;
36811     },
36812     
36813     hasChildNodes : function(){
36814         if(!this.isLeaf() && !this.loaded){
36815             return true;
36816         }else{
36817             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36818         }
36819     },
36820
36821     /**
36822      * Trigger a reload for this node
36823      * @param {Function} callback
36824      */
36825     reload : function(callback){
36826         this.collapse(false, false);
36827         while(this.firstChild){
36828             this.removeChild(this.firstChild);
36829         }
36830         this.childrenRendered = false;
36831         this.loaded = false;
36832         if(this.isHiddenRoot()){
36833             this.expanded = false;
36834         }
36835         this.expand(false, false, callback);
36836     }
36837 });/*
36838  * Based on:
36839  * Ext JS Library 1.1.1
36840  * Copyright(c) 2006-2007, Ext JS, LLC.
36841  *
36842  * Originally Released Under LGPL - original licence link has changed is not relivant.
36843  *
36844  * Fork - LGPL
36845  * <script type="text/javascript">
36846  */
36847  
36848 /**
36849  * @class Roo.tree.TreeNodeUI
36850  * @constructor
36851  * @param {Object} node The node to render
36852  * The TreeNode UI implementation is separate from the
36853  * tree implementation. Unless you are customizing the tree UI,
36854  * you should never have to use this directly.
36855  */
36856 Roo.tree.TreeNodeUI = function(node){
36857     this.node = node;
36858     this.rendered = false;
36859     this.animating = false;
36860     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36861 };
36862
36863 Roo.tree.TreeNodeUI.prototype = {
36864     removeChild : function(node){
36865         if(this.rendered){
36866             this.ctNode.removeChild(node.ui.getEl());
36867         }
36868     },
36869
36870     beforeLoad : function(){
36871          this.addClass("x-tree-node-loading");
36872     },
36873
36874     afterLoad : function(){
36875          this.removeClass("x-tree-node-loading");
36876     },
36877
36878     onTextChange : function(node, text, oldText){
36879         if(this.rendered){
36880             this.textNode.innerHTML = text;
36881         }
36882     },
36883
36884     onDisableChange : function(node, state){
36885         this.disabled = state;
36886         if(state){
36887             this.addClass("x-tree-node-disabled");
36888         }else{
36889             this.removeClass("x-tree-node-disabled");
36890         }
36891     },
36892
36893     onSelectedChange : function(state){
36894         if(state){
36895             this.focus();
36896             this.addClass("x-tree-selected");
36897         }else{
36898             //this.blur();
36899             this.removeClass("x-tree-selected");
36900         }
36901     },
36902
36903     onMove : function(tree, node, oldParent, newParent, index, refNode){
36904         this.childIndent = null;
36905         if(this.rendered){
36906             var targetNode = newParent.ui.getContainer();
36907             if(!targetNode){//target not rendered
36908                 this.holder = document.createElement("div");
36909                 this.holder.appendChild(this.wrap);
36910                 return;
36911             }
36912             var insertBefore = refNode ? refNode.ui.getEl() : null;
36913             if(insertBefore){
36914                 targetNode.insertBefore(this.wrap, insertBefore);
36915             }else{
36916                 targetNode.appendChild(this.wrap);
36917             }
36918             this.node.renderIndent(true);
36919         }
36920     },
36921
36922     addClass : function(cls){
36923         if(this.elNode){
36924             Roo.fly(this.elNode).addClass(cls);
36925         }
36926     },
36927
36928     removeClass : function(cls){
36929         if(this.elNode){
36930             Roo.fly(this.elNode).removeClass(cls);
36931         }
36932     },
36933
36934     remove : function(){
36935         if(this.rendered){
36936             this.holder = document.createElement("div");
36937             this.holder.appendChild(this.wrap);
36938         }
36939     },
36940
36941     fireEvent : function(){
36942         return this.node.fireEvent.apply(this.node, arguments);
36943     },
36944
36945     initEvents : function(){
36946         this.node.on("move", this.onMove, this);
36947         var E = Roo.EventManager;
36948         var a = this.anchor;
36949
36950         var el = Roo.fly(a, '_treeui');
36951
36952         if(Roo.isOpera){ // opera render bug ignores the CSS
36953             el.setStyle("text-decoration", "none");
36954         }
36955
36956         el.on("click", this.onClick, this);
36957         el.on("dblclick", this.onDblClick, this);
36958
36959         if(this.checkbox){
36960             Roo.EventManager.on(this.checkbox,
36961                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36962         }
36963
36964         el.on("contextmenu", this.onContextMenu, this);
36965
36966         var icon = Roo.fly(this.iconNode);
36967         icon.on("click", this.onClick, this);
36968         icon.on("dblclick", this.onDblClick, this);
36969         icon.on("contextmenu", this.onContextMenu, this);
36970         E.on(this.ecNode, "click", this.ecClick, this, true);
36971
36972         if(this.node.disabled){
36973             this.addClass("x-tree-node-disabled");
36974         }
36975         if(this.node.hidden){
36976             this.addClass("x-tree-node-disabled");
36977         }
36978         var ot = this.node.getOwnerTree();
36979         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36980         if(dd && (!this.node.isRoot || ot.rootVisible)){
36981             Roo.dd.Registry.register(this.elNode, {
36982                 node: this.node,
36983                 handles: this.getDDHandles(),
36984                 isHandle: false
36985             });
36986         }
36987     },
36988
36989     getDDHandles : function(){
36990         return [this.iconNode, this.textNode];
36991     },
36992
36993     hide : function(){
36994         if(this.rendered){
36995             this.wrap.style.display = "none";
36996         }
36997     },
36998
36999     show : function(){
37000         if(this.rendered){
37001             this.wrap.style.display = "";
37002         }
37003     },
37004
37005     onContextMenu : function(e){
37006         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37007             e.preventDefault();
37008             this.focus();
37009             this.fireEvent("contextmenu", this.node, e);
37010         }
37011     },
37012
37013     onClick : function(e){
37014         if(this.dropping){
37015             e.stopEvent();
37016             return;
37017         }
37018         if(this.fireEvent("beforeclick", this.node, e) !== false){
37019             if(!this.disabled && this.node.attributes.href){
37020                 this.fireEvent("click", this.node, e);
37021                 return;
37022             }
37023             e.preventDefault();
37024             if(this.disabled){
37025                 return;
37026             }
37027
37028             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37029                 this.node.toggle();
37030             }
37031
37032             this.fireEvent("click", this.node, e);
37033         }else{
37034             e.stopEvent();
37035         }
37036     },
37037
37038     onDblClick : function(e){
37039         e.preventDefault();
37040         if(this.disabled){
37041             return;
37042         }
37043         if(this.checkbox){
37044             this.toggleCheck();
37045         }
37046         if(!this.animating && this.node.hasChildNodes()){
37047             this.node.toggle();
37048         }
37049         this.fireEvent("dblclick", this.node, e);
37050     },
37051
37052     onCheckChange : function(){
37053         var checked = this.checkbox.checked;
37054         this.node.attributes.checked = checked;
37055         this.fireEvent('checkchange', this.node, checked);
37056     },
37057
37058     ecClick : function(e){
37059         if(!this.animating && this.node.hasChildNodes()){
37060             this.node.toggle();
37061         }
37062     },
37063
37064     startDrop : function(){
37065         this.dropping = true;
37066     },
37067
37068     // delayed drop so the click event doesn't get fired on a drop
37069     endDrop : function(){
37070        setTimeout(function(){
37071            this.dropping = false;
37072        }.createDelegate(this), 50);
37073     },
37074
37075     expand : function(){
37076         this.updateExpandIcon();
37077         this.ctNode.style.display = "";
37078     },
37079
37080     focus : function(){
37081         if(!this.node.preventHScroll){
37082             try{this.anchor.focus();
37083             }catch(e){}
37084         }else if(!Roo.isIE){
37085             try{
37086                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37087                 var l = noscroll.scrollLeft;
37088                 this.anchor.focus();
37089                 noscroll.scrollLeft = l;
37090             }catch(e){}
37091         }
37092     },
37093
37094     toggleCheck : function(value){
37095         var cb = this.checkbox;
37096         if(cb){
37097             cb.checked = (value === undefined ? !cb.checked : value);
37098         }
37099     },
37100
37101     blur : function(){
37102         try{
37103             this.anchor.blur();
37104         }catch(e){}
37105     },
37106
37107     animExpand : function(callback){
37108         var ct = Roo.get(this.ctNode);
37109         ct.stopFx();
37110         if(!this.node.hasChildNodes()){
37111             this.updateExpandIcon();
37112             this.ctNode.style.display = "";
37113             Roo.callback(callback);
37114             return;
37115         }
37116         this.animating = true;
37117         this.updateExpandIcon();
37118
37119         ct.slideIn('t', {
37120            callback : function(){
37121                this.animating = false;
37122                Roo.callback(callback);
37123             },
37124             scope: this,
37125             duration: this.node.ownerTree.duration || .25
37126         });
37127     },
37128
37129     highlight : function(){
37130         var tree = this.node.getOwnerTree();
37131         Roo.fly(this.wrap).highlight(
37132             tree.hlColor || "C3DAF9",
37133             {endColor: tree.hlBaseColor}
37134         );
37135     },
37136
37137     collapse : function(){
37138         this.updateExpandIcon();
37139         this.ctNode.style.display = "none";
37140     },
37141
37142     animCollapse : function(callback){
37143         var ct = Roo.get(this.ctNode);
37144         ct.enableDisplayMode('block');
37145         ct.stopFx();
37146
37147         this.animating = true;
37148         this.updateExpandIcon();
37149
37150         ct.slideOut('t', {
37151             callback : function(){
37152                this.animating = false;
37153                Roo.callback(callback);
37154             },
37155             scope: this,
37156             duration: this.node.ownerTree.duration || .25
37157         });
37158     },
37159
37160     getContainer : function(){
37161         return this.ctNode;
37162     },
37163
37164     getEl : function(){
37165         return this.wrap;
37166     },
37167
37168     appendDDGhost : function(ghostNode){
37169         ghostNode.appendChild(this.elNode.cloneNode(true));
37170     },
37171
37172     getDDRepairXY : function(){
37173         return Roo.lib.Dom.getXY(this.iconNode);
37174     },
37175
37176     onRender : function(){
37177         this.render();
37178     },
37179
37180     render : function(bulkRender){
37181         var n = this.node, a = n.attributes;
37182         var targetNode = n.parentNode ?
37183               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37184
37185         if(!this.rendered){
37186             this.rendered = true;
37187
37188             this.renderElements(n, a, targetNode, bulkRender);
37189
37190             if(a.qtip){
37191                if(this.textNode.setAttributeNS){
37192                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37193                    if(a.qtipTitle){
37194                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37195                    }
37196                }else{
37197                    this.textNode.setAttribute("ext:qtip", a.qtip);
37198                    if(a.qtipTitle){
37199                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37200                    }
37201                }
37202             }else if(a.qtipCfg){
37203                 a.qtipCfg.target = Roo.id(this.textNode);
37204                 Roo.QuickTips.register(a.qtipCfg);
37205             }
37206             this.initEvents();
37207             if(!this.node.expanded){
37208                 this.updateExpandIcon();
37209             }
37210         }else{
37211             if(bulkRender === true) {
37212                 targetNode.appendChild(this.wrap);
37213             }
37214         }
37215     },
37216
37217     renderElements : function(n, a, targetNode, bulkRender)
37218     {
37219         // add some indent caching, this helps performance when rendering a large tree
37220         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37221         var t = n.getOwnerTree();
37222         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37223         if (typeof(n.attributes.html) != 'undefined') {
37224             txt = n.attributes.html;
37225         }
37226         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37227         var cb = typeof a.checked == 'boolean';
37228         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37229         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37230             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37231             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37232             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37233             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37234             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37235              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37236                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37237             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37238             "</li>"];
37239
37240         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37241             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37242                                 n.nextSibling.ui.getEl(), buf.join(""));
37243         }else{
37244             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37245         }
37246
37247         this.elNode = this.wrap.childNodes[0];
37248         this.ctNode = this.wrap.childNodes[1];
37249         var cs = this.elNode.childNodes;
37250         this.indentNode = cs[0];
37251         this.ecNode = cs[1];
37252         this.iconNode = cs[2];
37253         var index = 3;
37254         if(cb){
37255             this.checkbox = cs[3];
37256             index++;
37257         }
37258         this.anchor = cs[index];
37259         this.textNode = cs[index].firstChild;
37260     },
37261
37262     getAnchor : function(){
37263         return this.anchor;
37264     },
37265
37266     getTextEl : function(){
37267         return this.textNode;
37268     },
37269
37270     getIconEl : function(){
37271         return this.iconNode;
37272     },
37273
37274     isChecked : function(){
37275         return this.checkbox ? this.checkbox.checked : false;
37276     },
37277
37278     updateExpandIcon : function(){
37279         if(this.rendered){
37280             var n = this.node, c1, c2;
37281             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37282             var hasChild = n.hasChildNodes();
37283             if(hasChild){
37284                 if(n.expanded){
37285                     cls += "-minus";
37286                     c1 = "x-tree-node-collapsed";
37287                     c2 = "x-tree-node-expanded";
37288                 }else{
37289                     cls += "-plus";
37290                     c1 = "x-tree-node-expanded";
37291                     c2 = "x-tree-node-collapsed";
37292                 }
37293                 if(this.wasLeaf){
37294                     this.removeClass("x-tree-node-leaf");
37295                     this.wasLeaf = false;
37296                 }
37297                 if(this.c1 != c1 || this.c2 != c2){
37298                     Roo.fly(this.elNode).replaceClass(c1, c2);
37299                     this.c1 = c1; this.c2 = c2;
37300                 }
37301             }else{
37302                 // this changes non-leafs into leafs if they have no children.
37303                 // it's not very rational behaviour..
37304                 
37305                 if(!this.wasLeaf && this.node.leaf){
37306                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37307                     delete this.c1;
37308                     delete this.c2;
37309                     this.wasLeaf = true;
37310                 }
37311             }
37312             var ecc = "x-tree-ec-icon "+cls;
37313             if(this.ecc != ecc){
37314                 this.ecNode.className = ecc;
37315                 this.ecc = ecc;
37316             }
37317         }
37318     },
37319
37320     getChildIndent : function(){
37321         if(!this.childIndent){
37322             var buf = [];
37323             var p = this.node;
37324             while(p){
37325                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37326                     if(!p.isLast()) {
37327                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37328                     } else {
37329                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37330                     }
37331                 }
37332                 p = p.parentNode;
37333             }
37334             this.childIndent = buf.join("");
37335         }
37336         return this.childIndent;
37337     },
37338
37339     renderIndent : function(){
37340         if(this.rendered){
37341             var indent = "";
37342             var p = this.node.parentNode;
37343             if(p){
37344                 indent = p.ui.getChildIndent();
37345             }
37346             if(this.indentMarkup != indent){ // don't rerender if not required
37347                 this.indentNode.innerHTML = indent;
37348                 this.indentMarkup = indent;
37349             }
37350             this.updateExpandIcon();
37351         }
37352     }
37353 };
37354
37355 Roo.tree.RootTreeNodeUI = function(){
37356     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37357 };
37358 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37359     render : function(){
37360         if(!this.rendered){
37361             var targetNode = this.node.ownerTree.innerCt.dom;
37362             this.node.expanded = true;
37363             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37364             this.wrap = this.ctNode = targetNode.firstChild;
37365         }
37366     },
37367     collapse : function(){
37368     },
37369     expand : function(){
37370     }
37371 });/*
37372  * Based on:
37373  * Ext JS Library 1.1.1
37374  * Copyright(c) 2006-2007, Ext JS, LLC.
37375  *
37376  * Originally Released Under LGPL - original licence link has changed is not relivant.
37377  *
37378  * Fork - LGPL
37379  * <script type="text/javascript">
37380  */
37381 /**
37382  * @class Roo.tree.TreeLoader
37383  * @extends Roo.util.Observable
37384  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37385  * nodes from a specified URL. The response must be a javascript Array definition
37386  * who's elements are node definition objects. eg:
37387  * <pre><code>
37388 {  success : true,
37389    data :      [
37390    
37391     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37392     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37393     ]
37394 }
37395
37396
37397 </code></pre>
37398  * <br><br>
37399  * The old style respose with just an array is still supported, but not recommended.
37400  * <br><br>
37401  *
37402  * A server request is sent, and child nodes are loaded only when a node is expanded.
37403  * The loading node's id is passed to the server under the parameter name "node" to
37404  * enable the server to produce the correct child nodes.
37405  * <br><br>
37406  * To pass extra parameters, an event handler may be attached to the "beforeload"
37407  * event, and the parameters specified in the TreeLoader's baseParams property:
37408  * <pre><code>
37409     myTreeLoader.on("beforeload", function(treeLoader, node) {
37410         this.baseParams.category = node.attributes.category;
37411     }, this);
37412     
37413 </code></pre>
37414  *
37415  * This would pass an HTTP parameter called "category" to the server containing
37416  * the value of the Node's "category" attribute.
37417  * @constructor
37418  * Creates a new Treeloader.
37419  * @param {Object} config A config object containing config properties.
37420  */
37421 Roo.tree.TreeLoader = function(config){
37422     this.baseParams = {};
37423     this.requestMethod = "POST";
37424     Roo.apply(this, config);
37425
37426     this.addEvents({
37427     
37428         /**
37429          * @event beforeload
37430          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37431          * @param {Object} This TreeLoader object.
37432          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37433          * @param {Object} callback The callback function specified in the {@link #load} call.
37434          */
37435         beforeload : true,
37436         /**
37437          * @event load
37438          * Fires when the node has been successfuly loaded.
37439          * @param {Object} This TreeLoader object.
37440          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37441          * @param {Object} response The response object containing the data from the server.
37442          */
37443         load : true,
37444         /**
37445          * @event loadexception
37446          * Fires if the network request failed.
37447          * @param {Object} This TreeLoader object.
37448          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37449          * @param {Object} response The response object containing the data from the server.
37450          */
37451         loadexception : true,
37452         /**
37453          * @event create
37454          * Fires before a node is created, enabling you to return custom Node types 
37455          * @param {Object} This TreeLoader object.
37456          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37457          */
37458         create : true
37459     });
37460
37461     Roo.tree.TreeLoader.superclass.constructor.call(this);
37462 };
37463
37464 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37465     /**
37466     * @cfg {String} dataUrl The URL from which to request a Json string which
37467     * specifies an array of node definition object representing the child nodes
37468     * to be loaded.
37469     */
37470     /**
37471     * @cfg {String} requestMethod either GET or POST
37472     * defaults to POST (due to BC)
37473     * to be loaded.
37474     */
37475     /**
37476     * @cfg {Object} baseParams (optional) An object containing properties which
37477     * specify HTTP parameters to be passed to each request for child nodes.
37478     */
37479     /**
37480     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37481     * created by this loader. If the attributes sent by the server have an attribute in this object,
37482     * they take priority.
37483     */
37484     /**
37485     * @cfg {Object} uiProviders (optional) An object containing properties which
37486     * 
37487     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37488     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37489     * <i>uiProvider</i> attribute of a returned child node is a string rather
37490     * than a reference to a TreeNodeUI implementation, this that string value
37491     * is used as a property name in the uiProviders object. You can define the provider named
37492     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37493     */
37494     uiProviders : {},
37495
37496     /**
37497     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37498     * child nodes before loading.
37499     */
37500     clearOnLoad : true,
37501
37502     /**
37503     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37504     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37505     * Grid query { data : [ .....] }
37506     */
37507     
37508     root : false,
37509      /**
37510     * @cfg {String} queryParam (optional) 
37511     * Name of the query as it will be passed on the querystring (defaults to 'node')
37512     * eg. the request will be ?node=[id]
37513     */
37514     
37515     
37516     queryParam: false,
37517     
37518     /**
37519      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37520      * This is called automatically when a node is expanded, but may be used to reload
37521      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37522      * @param {Roo.tree.TreeNode} node
37523      * @param {Function} callback
37524      */
37525     load : function(node, callback){
37526         if(this.clearOnLoad){
37527             while(node.firstChild){
37528                 node.removeChild(node.firstChild);
37529             }
37530         }
37531         if(node.attributes.children){ // preloaded json children
37532             var cs = node.attributes.children;
37533             for(var i = 0, len = cs.length; i < len; i++){
37534                 node.appendChild(this.createNode(cs[i]));
37535             }
37536             if(typeof callback == "function"){
37537                 callback();
37538             }
37539         }else if(this.dataUrl){
37540             this.requestData(node, callback);
37541         }
37542     },
37543
37544     getParams: function(node){
37545         var buf = [], bp = this.baseParams;
37546         for(var key in bp){
37547             if(typeof bp[key] != "function"){
37548                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37549             }
37550         }
37551         var n = this.queryParam === false ? 'node' : this.queryParam;
37552         buf.push(n + "=", encodeURIComponent(node.id));
37553         return buf.join("");
37554     },
37555
37556     requestData : function(node, callback){
37557         if(this.fireEvent("beforeload", this, node, callback) !== false){
37558             this.transId = Roo.Ajax.request({
37559                 method:this.requestMethod,
37560                 url: this.dataUrl||this.url,
37561                 success: this.handleResponse,
37562                 failure: this.handleFailure,
37563                 scope: this,
37564                 argument: {callback: callback, node: node},
37565                 params: this.getParams(node)
37566             });
37567         }else{
37568             // if the load is cancelled, make sure we notify
37569             // the node that we are done
37570             if(typeof callback == "function"){
37571                 callback();
37572             }
37573         }
37574     },
37575
37576     isLoading : function(){
37577         return this.transId ? true : false;
37578     },
37579
37580     abort : function(){
37581         if(this.isLoading()){
37582             Roo.Ajax.abort(this.transId);
37583         }
37584     },
37585
37586     // private
37587     createNode : function(attr)
37588     {
37589         // apply baseAttrs, nice idea Corey!
37590         if(this.baseAttrs){
37591             Roo.applyIf(attr, this.baseAttrs);
37592         }
37593         if(this.applyLoader !== false){
37594             attr.loader = this;
37595         }
37596         // uiProvider = depreciated..
37597         
37598         if(typeof(attr.uiProvider) == 'string'){
37599            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37600                 /**  eval:var:attr */ eval(attr.uiProvider);
37601         }
37602         if(typeof(this.uiProviders['default']) != 'undefined') {
37603             attr.uiProvider = this.uiProviders['default'];
37604         }
37605         
37606         this.fireEvent('create', this, attr);
37607         
37608         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37609         return(attr.leaf ?
37610                         new Roo.tree.TreeNode(attr) :
37611                         new Roo.tree.AsyncTreeNode(attr));
37612     },
37613
37614     processResponse : function(response, node, callback)
37615     {
37616         var json = response.responseText;
37617         try {
37618             
37619             var o = Roo.decode(json);
37620             
37621             if (this.root === false && typeof(o.success) != undefined) {
37622                 this.root = 'data'; // the default behaviour for list like data..
37623                 }
37624                 
37625             if (this.root !== false &&  !o.success) {
37626                 // it's a failure condition.
37627                 var a = response.argument;
37628                 this.fireEvent("loadexception", this, a.node, response);
37629                 Roo.log("Load failed - should have a handler really");
37630                 return;
37631             }
37632             
37633             
37634             
37635             if (this.root !== false) {
37636                  o = o[this.root];
37637             }
37638             
37639             for(var i = 0, len = o.length; i < len; i++){
37640                 var n = this.createNode(o[i]);
37641                 if(n){
37642                     node.appendChild(n);
37643                 }
37644             }
37645             if(typeof callback == "function"){
37646                 callback(this, node);
37647             }
37648         }catch(e){
37649             this.handleFailure(response);
37650         }
37651     },
37652
37653     handleResponse : function(response){
37654         this.transId = false;
37655         var a = response.argument;
37656         this.processResponse(response, a.node, a.callback);
37657         this.fireEvent("load", this, a.node, response);
37658     },
37659
37660     handleFailure : function(response)
37661     {
37662         // should handle failure better..
37663         this.transId = false;
37664         var a = response.argument;
37665         this.fireEvent("loadexception", this, a.node, response);
37666         if(typeof a.callback == "function"){
37667             a.callback(this, a.node);
37668         }
37669     }
37670 });/*
37671  * Based on:
37672  * Ext JS Library 1.1.1
37673  * Copyright(c) 2006-2007, Ext JS, LLC.
37674  *
37675  * Originally Released Under LGPL - original licence link has changed is not relivant.
37676  *
37677  * Fork - LGPL
37678  * <script type="text/javascript">
37679  */
37680
37681 /**
37682 * @class Roo.tree.TreeFilter
37683 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37684 * @param {TreePanel} tree
37685 * @param {Object} config (optional)
37686  */
37687 Roo.tree.TreeFilter = function(tree, config){
37688     this.tree = tree;
37689     this.filtered = {};
37690     Roo.apply(this, config);
37691 };
37692
37693 Roo.tree.TreeFilter.prototype = {
37694     clearBlank:false,
37695     reverse:false,
37696     autoClear:false,
37697     remove:false,
37698
37699      /**
37700      * Filter the data by a specific attribute.
37701      * @param {String/RegExp} value Either string that the attribute value
37702      * should start with or a RegExp to test against the attribute
37703      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37704      * @param {TreeNode} startNode (optional) The node to start the filter at.
37705      */
37706     filter : function(value, attr, startNode){
37707         attr = attr || "text";
37708         var f;
37709         if(typeof value == "string"){
37710             var vlen = value.length;
37711             // auto clear empty filter
37712             if(vlen == 0 && this.clearBlank){
37713                 this.clear();
37714                 return;
37715             }
37716             value = value.toLowerCase();
37717             f = function(n){
37718                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37719             };
37720         }else if(value.exec){ // regex?
37721             f = function(n){
37722                 return value.test(n.attributes[attr]);
37723             };
37724         }else{
37725             throw 'Illegal filter type, must be string or regex';
37726         }
37727         this.filterBy(f, null, startNode);
37728         },
37729
37730     /**
37731      * Filter by a function. The passed function will be called with each
37732      * node in the tree (or from the startNode). If the function returns true, the node is kept
37733      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37734      * @param {Function} fn The filter function
37735      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37736      */
37737     filterBy : function(fn, scope, startNode){
37738         startNode = startNode || this.tree.root;
37739         if(this.autoClear){
37740             this.clear();
37741         }
37742         var af = this.filtered, rv = this.reverse;
37743         var f = function(n){
37744             if(n == startNode){
37745                 return true;
37746             }
37747             if(af[n.id]){
37748                 return false;
37749             }
37750             var m = fn.call(scope || n, n);
37751             if(!m || rv){
37752                 af[n.id] = n;
37753                 n.ui.hide();
37754                 return false;
37755             }
37756             return true;
37757         };
37758         startNode.cascade(f);
37759         if(this.remove){
37760            for(var id in af){
37761                if(typeof id != "function"){
37762                    var n = af[id];
37763                    if(n && n.parentNode){
37764                        n.parentNode.removeChild(n);
37765                    }
37766                }
37767            }
37768         }
37769     },
37770
37771     /**
37772      * Clears the current filter. Note: with the "remove" option
37773      * set a filter cannot be cleared.
37774      */
37775     clear : function(){
37776         var t = this.tree;
37777         var af = this.filtered;
37778         for(var id in af){
37779             if(typeof id != "function"){
37780                 var n = af[id];
37781                 if(n){
37782                     n.ui.show();
37783                 }
37784             }
37785         }
37786         this.filtered = {};
37787     }
37788 };
37789 /*
37790  * Based on:
37791  * Ext JS Library 1.1.1
37792  * Copyright(c) 2006-2007, Ext JS, LLC.
37793  *
37794  * Originally Released Under LGPL - original licence link has changed is not relivant.
37795  *
37796  * Fork - LGPL
37797  * <script type="text/javascript">
37798  */
37799  
37800
37801 /**
37802  * @class Roo.tree.TreeSorter
37803  * Provides sorting of nodes in a TreePanel
37804  * 
37805  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37806  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37807  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37808  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37809  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37810  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37811  * @constructor
37812  * @param {TreePanel} tree
37813  * @param {Object} config
37814  */
37815 Roo.tree.TreeSorter = function(tree, config){
37816     Roo.apply(this, config);
37817     tree.on("beforechildrenrendered", this.doSort, this);
37818     tree.on("append", this.updateSort, this);
37819     tree.on("insert", this.updateSort, this);
37820     
37821     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37822     var p = this.property || "text";
37823     var sortType = this.sortType;
37824     var fs = this.folderSort;
37825     var cs = this.caseSensitive === true;
37826     var leafAttr = this.leafAttr || 'leaf';
37827
37828     this.sortFn = function(n1, n2){
37829         if(fs){
37830             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37831                 return 1;
37832             }
37833             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37834                 return -1;
37835             }
37836         }
37837         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37838         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37839         if(v1 < v2){
37840                         return dsc ? +1 : -1;
37841                 }else if(v1 > v2){
37842                         return dsc ? -1 : +1;
37843         }else{
37844                 return 0;
37845         }
37846     };
37847 };
37848
37849 Roo.tree.TreeSorter.prototype = {
37850     doSort : function(node){
37851         node.sort(this.sortFn);
37852     },
37853     
37854     compareNodes : function(n1, n2){
37855         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37856     },
37857     
37858     updateSort : function(tree, node){
37859         if(node.childrenRendered){
37860             this.doSort.defer(1, this, [node]);
37861         }
37862     }
37863 };/*
37864  * Based on:
37865  * Ext JS Library 1.1.1
37866  * Copyright(c) 2006-2007, Ext JS, LLC.
37867  *
37868  * Originally Released Under LGPL - original licence link has changed is not relivant.
37869  *
37870  * Fork - LGPL
37871  * <script type="text/javascript">
37872  */
37873
37874 if(Roo.dd.DropZone){
37875     
37876 Roo.tree.TreeDropZone = function(tree, config){
37877     this.allowParentInsert = false;
37878     this.allowContainerDrop = false;
37879     this.appendOnly = false;
37880     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37881     this.tree = tree;
37882     this.lastInsertClass = "x-tree-no-status";
37883     this.dragOverData = {};
37884 };
37885
37886 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37887     ddGroup : "TreeDD",
37888     scroll:  true,
37889     
37890     expandDelay : 1000,
37891     
37892     expandNode : function(node){
37893         if(node.hasChildNodes() && !node.isExpanded()){
37894             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37895         }
37896     },
37897     
37898     queueExpand : function(node){
37899         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37900     },
37901     
37902     cancelExpand : function(){
37903         if(this.expandProcId){
37904             clearTimeout(this.expandProcId);
37905             this.expandProcId = false;
37906         }
37907     },
37908     
37909     isValidDropPoint : function(n, pt, dd, e, data){
37910         if(!n || !data){ return false; }
37911         var targetNode = n.node;
37912         var dropNode = data.node;
37913         // default drop rules
37914         if(!(targetNode && targetNode.isTarget && pt)){
37915             return false;
37916         }
37917         if(pt == "append" && targetNode.allowChildren === false){
37918             return false;
37919         }
37920         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37921             return false;
37922         }
37923         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37924             return false;
37925         }
37926         // reuse the object
37927         var overEvent = this.dragOverData;
37928         overEvent.tree = this.tree;
37929         overEvent.target = targetNode;
37930         overEvent.data = data;
37931         overEvent.point = pt;
37932         overEvent.source = dd;
37933         overEvent.rawEvent = e;
37934         overEvent.dropNode = dropNode;
37935         overEvent.cancel = false;  
37936         var result = this.tree.fireEvent("nodedragover", overEvent);
37937         return overEvent.cancel === false && result !== false;
37938     },
37939     
37940     getDropPoint : function(e, n, dd)
37941     {
37942         var tn = n.node;
37943         if(tn.isRoot){
37944             return tn.allowChildren !== false ? "append" : false; // always append for root
37945         }
37946         var dragEl = n.ddel;
37947         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37948         var y = Roo.lib.Event.getPageY(e);
37949         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37950         
37951         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37952         var noAppend = tn.allowChildren === false;
37953         if(this.appendOnly || tn.parentNode.allowChildren === false){
37954             return noAppend ? false : "append";
37955         }
37956         var noBelow = false;
37957         if(!this.allowParentInsert){
37958             noBelow = tn.hasChildNodes() && tn.isExpanded();
37959         }
37960         var q = (b - t) / (noAppend ? 2 : 3);
37961         if(y >= t && y < (t + q)){
37962             return "above";
37963         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37964             return "below";
37965         }else{
37966             return "append";
37967         }
37968     },
37969     
37970     onNodeEnter : function(n, dd, e, data)
37971     {
37972         this.cancelExpand();
37973     },
37974     
37975     onNodeOver : function(n, dd, e, data)
37976     {
37977        
37978         var pt = this.getDropPoint(e, n, dd);
37979         var node = n.node;
37980         
37981         // auto node expand check
37982         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37983             this.queueExpand(node);
37984         }else if(pt != "append"){
37985             this.cancelExpand();
37986         }
37987         
37988         // set the insert point style on the target node
37989         var returnCls = this.dropNotAllowed;
37990         if(this.isValidDropPoint(n, pt, dd, e, data)){
37991            if(pt){
37992                var el = n.ddel;
37993                var cls;
37994                if(pt == "above"){
37995                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37996                    cls = "x-tree-drag-insert-above";
37997                }else if(pt == "below"){
37998                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37999                    cls = "x-tree-drag-insert-below";
38000                }else{
38001                    returnCls = "x-tree-drop-ok-append";
38002                    cls = "x-tree-drag-append";
38003                }
38004                if(this.lastInsertClass != cls){
38005                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38006                    this.lastInsertClass = cls;
38007                }
38008            }
38009        }
38010        return returnCls;
38011     },
38012     
38013     onNodeOut : function(n, dd, e, data){
38014         
38015         this.cancelExpand();
38016         this.removeDropIndicators(n);
38017     },
38018     
38019     onNodeDrop : function(n, dd, e, data){
38020         var point = this.getDropPoint(e, n, dd);
38021         var targetNode = n.node;
38022         targetNode.ui.startDrop();
38023         if(!this.isValidDropPoint(n, point, dd, e, data)){
38024             targetNode.ui.endDrop();
38025             return false;
38026         }
38027         // first try to find the drop node
38028         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38029         var dropEvent = {
38030             tree : this.tree,
38031             target: targetNode,
38032             data: data,
38033             point: point,
38034             source: dd,
38035             rawEvent: e,
38036             dropNode: dropNode,
38037             cancel: !dropNode   
38038         };
38039         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38040         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38041             targetNode.ui.endDrop();
38042             return false;
38043         }
38044         // allow target changing
38045         targetNode = dropEvent.target;
38046         if(point == "append" && !targetNode.isExpanded()){
38047             targetNode.expand(false, null, function(){
38048                 this.completeDrop(dropEvent);
38049             }.createDelegate(this));
38050         }else{
38051             this.completeDrop(dropEvent);
38052         }
38053         return true;
38054     },
38055     
38056     completeDrop : function(de){
38057         var ns = de.dropNode, p = de.point, t = de.target;
38058         if(!(ns instanceof Array)){
38059             ns = [ns];
38060         }
38061         var n;
38062         for(var i = 0, len = ns.length; i < len; i++){
38063             n = ns[i];
38064             if(p == "above"){
38065                 t.parentNode.insertBefore(n, t);
38066             }else if(p == "below"){
38067                 t.parentNode.insertBefore(n, t.nextSibling);
38068             }else{
38069                 t.appendChild(n);
38070             }
38071         }
38072         n.ui.focus();
38073         if(this.tree.hlDrop){
38074             n.ui.highlight();
38075         }
38076         t.ui.endDrop();
38077         this.tree.fireEvent("nodedrop", de);
38078     },
38079     
38080     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38081         if(this.tree.hlDrop){
38082             dropNode.ui.focus();
38083             dropNode.ui.highlight();
38084         }
38085         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38086     },
38087     
38088     getTree : function(){
38089         return this.tree;
38090     },
38091     
38092     removeDropIndicators : function(n){
38093         if(n && n.ddel){
38094             var el = n.ddel;
38095             Roo.fly(el).removeClass([
38096                     "x-tree-drag-insert-above",
38097                     "x-tree-drag-insert-below",
38098                     "x-tree-drag-append"]);
38099             this.lastInsertClass = "_noclass";
38100         }
38101     },
38102     
38103     beforeDragDrop : function(target, e, id){
38104         this.cancelExpand();
38105         return true;
38106     },
38107     
38108     afterRepair : function(data){
38109         if(data && Roo.enableFx){
38110             data.node.ui.highlight();
38111         }
38112         this.hideProxy();
38113     } 
38114     
38115 });
38116
38117 }
38118 /*
38119  * Based on:
38120  * Ext JS Library 1.1.1
38121  * Copyright(c) 2006-2007, Ext JS, LLC.
38122  *
38123  * Originally Released Under LGPL - original licence link has changed is not relivant.
38124  *
38125  * Fork - LGPL
38126  * <script type="text/javascript">
38127  */
38128  
38129
38130 if(Roo.dd.DragZone){
38131 Roo.tree.TreeDragZone = function(tree, config){
38132     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38133     this.tree = tree;
38134 };
38135
38136 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38137     ddGroup : "TreeDD",
38138    
38139     onBeforeDrag : function(data, e){
38140         var n = data.node;
38141         return n && n.draggable && !n.disabled;
38142     },
38143      
38144     
38145     onInitDrag : function(e){
38146         var data = this.dragData;
38147         this.tree.getSelectionModel().select(data.node);
38148         this.proxy.update("");
38149         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38150         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38151     },
38152     
38153     getRepairXY : function(e, data){
38154         return data.node.ui.getDDRepairXY();
38155     },
38156     
38157     onEndDrag : function(data, e){
38158         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38159         
38160         
38161     },
38162     
38163     onValidDrop : function(dd, e, id){
38164         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38165         this.hideProxy();
38166     },
38167     
38168     beforeInvalidDrop : function(e, id){
38169         // this scrolls the original position back into view
38170         var sm = this.tree.getSelectionModel();
38171         sm.clearSelections();
38172         sm.select(this.dragData.node);
38173     }
38174 });
38175 }/*
38176  * Based on:
38177  * Ext JS Library 1.1.1
38178  * Copyright(c) 2006-2007, Ext JS, LLC.
38179  *
38180  * Originally Released Under LGPL - original licence link has changed is not relivant.
38181  *
38182  * Fork - LGPL
38183  * <script type="text/javascript">
38184  */
38185 /**
38186  * @class Roo.tree.TreeEditor
38187  * @extends Roo.Editor
38188  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38189  * as the editor field.
38190  * @constructor
38191  * @param {Object} config (used to be the tree panel.)
38192  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38193  * 
38194  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38195  * @cfg {Roo.form.TextField} field [required] The field configuration
38196  *
38197  * 
38198  */
38199 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38200     var tree = config;
38201     var field;
38202     if (oldconfig) { // old style..
38203         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38204     } else {
38205         // new style..
38206         tree = config.tree;
38207         config.field = config.field  || {};
38208         config.field.xtype = 'TextField';
38209         field = Roo.factory(config.field, Roo.form);
38210     }
38211     config = config || {};
38212     
38213     
38214     this.addEvents({
38215         /**
38216          * @event beforenodeedit
38217          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38218          * false from the handler of this event.
38219          * @param {Editor} this
38220          * @param {Roo.tree.Node} node 
38221          */
38222         "beforenodeedit" : true
38223     });
38224     
38225     //Roo.log(config);
38226     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38227
38228     this.tree = tree;
38229
38230     tree.on('beforeclick', this.beforeNodeClick, this);
38231     tree.getTreeEl().on('mousedown', this.hide, this);
38232     this.on('complete', this.updateNode, this);
38233     this.on('beforestartedit', this.fitToTree, this);
38234     this.on('startedit', this.bindScroll, this, {delay:10});
38235     this.on('specialkey', this.onSpecialKey, this);
38236 };
38237
38238 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38239     /**
38240      * @cfg {String} alignment
38241      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38242      */
38243     alignment: "l-l",
38244     // inherit
38245     autoSize: false,
38246     /**
38247      * @cfg {Boolean} hideEl
38248      * True to hide the bound element while the editor is displayed (defaults to false)
38249      */
38250     hideEl : false,
38251     /**
38252      * @cfg {String} cls
38253      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38254      */
38255     cls: "x-small-editor x-tree-editor",
38256     /**
38257      * @cfg {Boolean} shim
38258      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38259      */
38260     shim:false,
38261     // inherit
38262     shadow:"frame",
38263     /**
38264      * @cfg {Number} maxWidth
38265      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38266      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38267      * scroll and client offsets into account prior to each edit.
38268      */
38269     maxWidth: 250,
38270
38271     editDelay : 350,
38272
38273     // private
38274     fitToTree : function(ed, el){
38275         var td = this.tree.getTreeEl().dom, nd = el.dom;
38276         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38277             td.scrollLeft = nd.offsetLeft;
38278         }
38279         var w = Math.min(
38280                 this.maxWidth,
38281                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38282         this.setSize(w, '');
38283         
38284         return this.fireEvent('beforenodeedit', this, this.editNode);
38285         
38286     },
38287
38288     // private
38289     triggerEdit : function(node){
38290         this.completeEdit();
38291         this.editNode = node;
38292         this.startEdit(node.ui.textNode, node.text);
38293     },
38294
38295     // private
38296     bindScroll : function(){
38297         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38298     },
38299
38300     // private
38301     beforeNodeClick : function(node, e){
38302         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38303         this.lastClick = new Date();
38304         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38305             e.stopEvent();
38306             this.triggerEdit(node);
38307             return false;
38308         }
38309         return true;
38310     },
38311
38312     // private
38313     updateNode : function(ed, value){
38314         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38315         this.editNode.setText(value);
38316     },
38317
38318     // private
38319     onHide : function(){
38320         Roo.tree.TreeEditor.superclass.onHide.call(this);
38321         if(this.editNode){
38322             this.editNode.ui.focus();
38323         }
38324     },
38325
38326     // private
38327     onSpecialKey : function(field, e){
38328         var k = e.getKey();
38329         if(k == e.ESC){
38330             e.stopEvent();
38331             this.cancelEdit();
38332         }else if(k == e.ENTER && !e.hasModifier()){
38333             e.stopEvent();
38334             this.completeEdit();
38335         }
38336     }
38337 });//<Script type="text/javascript">
38338 /*
38339  * Based on:
38340  * Ext JS Library 1.1.1
38341  * Copyright(c) 2006-2007, Ext JS, LLC.
38342  *
38343  * Originally Released Under LGPL - original licence link has changed is not relivant.
38344  *
38345  * Fork - LGPL
38346  * <script type="text/javascript">
38347  */
38348  
38349 /**
38350  * Not documented??? - probably should be...
38351  */
38352
38353 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38354     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38355     
38356     renderElements : function(n, a, targetNode, bulkRender){
38357         //consel.log("renderElements?");
38358         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38359
38360         var t = n.getOwnerTree();
38361         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38362         
38363         var cols = t.columns;
38364         var bw = t.borderWidth;
38365         var c = cols[0];
38366         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38367          var cb = typeof a.checked == "boolean";
38368         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38369         var colcls = 'x-t-' + tid + '-c0';
38370         var buf = [
38371             '<li class="x-tree-node">',
38372             
38373                 
38374                 '<div class="x-tree-node-el ', a.cls,'">',
38375                     // extran...
38376                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38377                 
38378                 
38379                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38380                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38381                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38382                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38383                            (a.iconCls ? ' '+a.iconCls : ''),
38384                            '" unselectable="on" />',
38385                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38386                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38387                              
38388                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38389                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38390                             '<span unselectable="on" qtip="' + tx + '">',
38391                              tx,
38392                              '</span></a>' ,
38393                     '</div>',
38394                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38395                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38396                  ];
38397         for(var i = 1, len = cols.length; i < len; i++){
38398             c = cols[i];
38399             colcls = 'x-t-' + tid + '-c' +i;
38400             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38401             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38402                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38403                       "</div>");
38404          }
38405          
38406          buf.push(
38407             '</a>',
38408             '<div class="x-clear"></div></div>',
38409             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38410             "</li>");
38411         
38412         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38413             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38414                                 n.nextSibling.ui.getEl(), buf.join(""));
38415         }else{
38416             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38417         }
38418         var el = this.wrap.firstChild;
38419         this.elRow = el;
38420         this.elNode = el.firstChild;
38421         this.ranchor = el.childNodes[1];
38422         this.ctNode = this.wrap.childNodes[1];
38423         var cs = el.firstChild.childNodes;
38424         this.indentNode = cs[0];
38425         this.ecNode = cs[1];
38426         this.iconNode = cs[2];
38427         var index = 3;
38428         if(cb){
38429             this.checkbox = cs[3];
38430             index++;
38431         }
38432         this.anchor = cs[index];
38433         
38434         this.textNode = cs[index].firstChild;
38435         
38436         //el.on("click", this.onClick, this);
38437         //el.on("dblclick", this.onDblClick, this);
38438         
38439         
38440        // console.log(this);
38441     },
38442     initEvents : function(){
38443         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38444         
38445             
38446         var a = this.ranchor;
38447
38448         var el = Roo.get(a);
38449
38450         if(Roo.isOpera){ // opera render bug ignores the CSS
38451             el.setStyle("text-decoration", "none");
38452         }
38453
38454         el.on("click", this.onClick, this);
38455         el.on("dblclick", this.onDblClick, this);
38456         el.on("contextmenu", this.onContextMenu, this);
38457         
38458     },
38459     
38460     /*onSelectedChange : function(state){
38461         if(state){
38462             this.focus();
38463             this.addClass("x-tree-selected");
38464         }else{
38465             //this.blur();
38466             this.removeClass("x-tree-selected");
38467         }
38468     },*/
38469     addClass : function(cls){
38470         if(this.elRow){
38471             Roo.fly(this.elRow).addClass(cls);
38472         }
38473         
38474     },
38475     
38476     
38477     removeClass : function(cls){
38478         if(this.elRow){
38479             Roo.fly(this.elRow).removeClass(cls);
38480         }
38481     }
38482
38483     
38484     
38485 });//<Script type="text/javascript">
38486
38487 /*
38488  * Based on:
38489  * Ext JS Library 1.1.1
38490  * Copyright(c) 2006-2007, Ext JS, LLC.
38491  *
38492  * Originally Released Under LGPL - original licence link has changed is not relivant.
38493  *
38494  * Fork - LGPL
38495  * <script type="text/javascript">
38496  */
38497  
38498
38499 /**
38500  * @class Roo.tree.ColumnTree
38501  * @extends Roo.tree.TreePanel
38502  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38503  * @cfg {int} borderWidth  compined right/left border allowance
38504  * @constructor
38505  * @param {String/HTMLElement/Element} el The container element
38506  * @param {Object} config
38507  */
38508 Roo.tree.ColumnTree =  function(el, config)
38509 {
38510    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38511    this.addEvents({
38512         /**
38513         * @event resize
38514         * Fire this event on a container when it resizes
38515         * @param {int} w Width
38516         * @param {int} h Height
38517         */
38518        "resize" : true
38519     });
38520     this.on('resize', this.onResize, this);
38521 };
38522
38523 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38524     //lines:false,
38525     
38526     
38527     borderWidth: Roo.isBorderBox ? 0 : 2, 
38528     headEls : false,
38529     
38530     render : function(){
38531         // add the header.....
38532        
38533         Roo.tree.ColumnTree.superclass.render.apply(this);
38534         
38535         this.el.addClass('x-column-tree');
38536         
38537         this.headers = this.el.createChild(
38538             {cls:'x-tree-headers'},this.innerCt.dom);
38539    
38540         var cols = this.columns, c;
38541         var totalWidth = 0;
38542         this.headEls = [];
38543         var  len = cols.length;
38544         for(var i = 0; i < len; i++){
38545              c = cols[i];
38546              totalWidth += c.width;
38547             this.headEls.push(this.headers.createChild({
38548                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38549                  cn: {
38550                      cls:'x-tree-hd-text',
38551                      html: c.header
38552                  },
38553                  style:'width:'+(c.width-this.borderWidth)+'px;'
38554              }));
38555         }
38556         this.headers.createChild({cls:'x-clear'});
38557         // prevent floats from wrapping when clipped
38558         this.headers.setWidth(totalWidth);
38559         //this.innerCt.setWidth(totalWidth);
38560         this.innerCt.setStyle({ overflow: 'auto' });
38561         this.onResize(this.width, this.height);
38562              
38563         
38564     },
38565     onResize : function(w,h)
38566     {
38567         this.height = h;
38568         this.width = w;
38569         // resize cols..
38570         this.innerCt.setWidth(this.width);
38571         this.innerCt.setHeight(this.height-20);
38572         
38573         // headers...
38574         var cols = this.columns, c;
38575         var totalWidth = 0;
38576         var expEl = false;
38577         var len = cols.length;
38578         for(var i = 0; i < len; i++){
38579             c = cols[i];
38580             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38581                 // it's the expander..
38582                 expEl  = this.headEls[i];
38583                 continue;
38584             }
38585             totalWidth += c.width;
38586             
38587         }
38588         if (expEl) {
38589             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38590         }
38591         this.headers.setWidth(w-20);
38592
38593         
38594         
38595         
38596     }
38597 });
38598 /*
38599  * Based on:
38600  * Ext JS Library 1.1.1
38601  * Copyright(c) 2006-2007, Ext JS, LLC.
38602  *
38603  * Originally Released Under LGPL - original licence link has changed is not relivant.
38604  *
38605  * Fork - LGPL
38606  * <script type="text/javascript">
38607  */
38608  
38609 /**
38610  * @class Roo.menu.Menu
38611  * @extends Roo.util.Observable
38612  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38613  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38614  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38615  * @constructor
38616  * Creates a new Menu
38617  * @param {Object} config Configuration options
38618  */
38619 Roo.menu.Menu = function(config){
38620     
38621     Roo.menu.Menu.superclass.constructor.call(this, config);
38622     
38623     this.id = this.id || Roo.id();
38624     this.addEvents({
38625         /**
38626          * @event beforeshow
38627          * Fires before this menu is displayed
38628          * @param {Roo.menu.Menu} this
38629          */
38630         beforeshow : true,
38631         /**
38632          * @event beforehide
38633          * Fires before this menu is hidden
38634          * @param {Roo.menu.Menu} this
38635          */
38636         beforehide : true,
38637         /**
38638          * @event show
38639          * Fires after this menu is displayed
38640          * @param {Roo.menu.Menu} this
38641          */
38642         show : true,
38643         /**
38644          * @event hide
38645          * Fires after this menu is hidden
38646          * @param {Roo.menu.Menu} this
38647          */
38648         hide : true,
38649         /**
38650          * @event click
38651          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38652          * @param {Roo.menu.Menu} this
38653          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38654          * @param {Roo.EventObject} e
38655          */
38656         click : true,
38657         /**
38658          * @event mouseover
38659          * Fires when the mouse is hovering over this menu
38660          * @param {Roo.menu.Menu} this
38661          * @param {Roo.EventObject} e
38662          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38663          */
38664         mouseover : true,
38665         /**
38666          * @event mouseout
38667          * Fires when the mouse exits this menu
38668          * @param {Roo.menu.Menu} this
38669          * @param {Roo.EventObject} e
38670          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38671          */
38672         mouseout : true,
38673         /**
38674          * @event itemclick
38675          * Fires when a menu item contained in this menu is clicked
38676          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38677          * @param {Roo.EventObject} e
38678          */
38679         itemclick: true
38680     });
38681     if (this.registerMenu) {
38682         Roo.menu.MenuMgr.register(this);
38683     }
38684     
38685     var mis = this.items;
38686     this.items = new Roo.util.MixedCollection();
38687     if(mis){
38688         this.add.apply(this, mis);
38689     }
38690 };
38691
38692 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38693     /**
38694      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38695      */
38696     minWidth : 120,
38697     /**
38698      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38699      * for bottom-right shadow (defaults to "sides")
38700      */
38701     shadow : "sides",
38702     /**
38703      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38704      * this menu (defaults to "tl-tr?")
38705      */
38706     subMenuAlign : "tl-tr?",
38707     /**
38708      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38709      * relative to its element of origin (defaults to "tl-bl?")
38710      */
38711     defaultAlign : "tl-bl?",
38712     /**
38713      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38714      */
38715     allowOtherMenus : false,
38716     /**
38717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38718      */
38719     registerMenu : true,
38720
38721     hidden:true,
38722
38723     // private
38724     render : function(){
38725         if(this.el){
38726             return;
38727         }
38728         var el = this.el = new Roo.Layer({
38729             cls: "x-menu",
38730             shadow:this.shadow,
38731             constrain: false,
38732             parentEl: this.parentEl || document.body,
38733             zindex:15000
38734         });
38735
38736         this.keyNav = new Roo.menu.MenuNav(this);
38737
38738         if(this.plain){
38739             el.addClass("x-menu-plain");
38740         }
38741         if(this.cls){
38742             el.addClass(this.cls);
38743         }
38744         // generic focus element
38745         this.focusEl = el.createChild({
38746             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38747         });
38748         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38749         //disabling touch- as it's causing issues ..
38750         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38751         ul.on('click'   , this.onClick, this);
38752         
38753         
38754         ul.on("mouseover", this.onMouseOver, this);
38755         ul.on("mouseout", this.onMouseOut, this);
38756         this.items.each(function(item){
38757             if (item.hidden) {
38758                 return;
38759             }
38760             
38761             var li = document.createElement("li");
38762             li.className = "x-menu-list-item";
38763             ul.dom.appendChild(li);
38764             item.render(li, this);
38765         }, this);
38766         this.ul = ul;
38767         this.autoWidth();
38768     },
38769
38770     // private
38771     autoWidth : function(){
38772         var el = this.el, ul = this.ul;
38773         if(!el){
38774             return;
38775         }
38776         var w = this.width;
38777         if(w){
38778             el.setWidth(w);
38779         }else if(Roo.isIE){
38780             el.setWidth(this.minWidth);
38781             var t = el.dom.offsetWidth; // force recalc
38782             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38783         }
38784     },
38785
38786     // private
38787     delayAutoWidth : function(){
38788         if(this.rendered){
38789             if(!this.awTask){
38790                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38791             }
38792             this.awTask.delay(20);
38793         }
38794     },
38795
38796     // private
38797     findTargetItem : function(e){
38798         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38799         if(t && t.menuItemId){
38800             return this.items.get(t.menuItemId);
38801         }
38802     },
38803
38804     // private
38805     onClick : function(e){
38806         Roo.log("menu.onClick");
38807         var t = this.findTargetItem(e);
38808         if(!t){
38809             return;
38810         }
38811         Roo.log(e);
38812         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38813             if(t == this.activeItem && t.shouldDeactivate(e)){
38814                 this.activeItem.deactivate();
38815                 delete this.activeItem;
38816                 return;
38817             }
38818             if(t.canActivate){
38819                 this.setActiveItem(t, true);
38820             }
38821             return;
38822             
38823             
38824         }
38825         
38826         t.onClick(e);
38827         this.fireEvent("click", this, t, e);
38828     },
38829
38830     // private
38831     setActiveItem : function(item, autoExpand){
38832         if(item != this.activeItem){
38833             if(this.activeItem){
38834                 this.activeItem.deactivate();
38835             }
38836             this.activeItem = item;
38837             item.activate(autoExpand);
38838         }else if(autoExpand){
38839             item.expandMenu();
38840         }
38841     },
38842
38843     // private
38844     tryActivate : function(start, step){
38845         var items = this.items;
38846         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38847             var item = items.get(i);
38848             if(!item.disabled && item.canActivate){
38849                 this.setActiveItem(item, false);
38850                 return item;
38851             }
38852         }
38853         return false;
38854     },
38855
38856     // private
38857     onMouseOver : function(e){
38858         var t;
38859         if(t = this.findTargetItem(e)){
38860             if(t.canActivate && !t.disabled){
38861                 this.setActiveItem(t, true);
38862             }
38863         }
38864         this.fireEvent("mouseover", this, e, t);
38865     },
38866
38867     // private
38868     onMouseOut : function(e){
38869         var t;
38870         if(t = this.findTargetItem(e)){
38871             if(t == this.activeItem && t.shouldDeactivate(e)){
38872                 this.activeItem.deactivate();
38873                 delete this.activeItem;
38874             }
38875         }
38876         this.fireEvent("mouseout", this, e, t);
38877     },
38878
38879     /**
38880      * Read-only.  Returns true if the menu is currently displayed, else false.
38881      * @type Boolean
38882      */
38883     isVisible : function(){
38884         return this.el && !this.hidden;
38885     },
38886
38887     /**
38888      * Displays this menu relative to another element
38889      * @param {String/HTMLElement/Roo.Element} element The element to align to
38890      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38891      * the element (defaults to this.defaultAlign)
38892      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38893      */
38894     show : function(el, pos, parentMenu){
38895         this.parentMenu = parentMenu;
38896         if(!this.el){
38897             this.render();
38898         }
38899         this.fireEvent("beforeshow", this);
38900         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38901     },
38902
38903     /**
38904      * Displays this menu at a specific xy position
38905      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38906      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38907      */
38908     showAt : function(xy, parentMenu, /* private: */_e){
38909         this.parentMenu = parentMenu;
38910         if(!this.el){
38911             this.render();
38912         }
38913         if(_e !== false){
38914             this.fireEvent("beforeshow", this);
38915             xy = this.el.adjustForConstraints(xy);
38916         }
38917         this.el.setXY(xy);
38918         this.el.show();
38919         this.hidden = false;
38920         this.focus();
38921         this.fireEvent("show", this);
38922     },
38923
38924     focus : function(){
38925         if(!this.hidden){
38926             this.doFocus.defer(50, this);
38927         }
38928     },
38929
38930     doFocus : function(){
38931         if(!this.hidden){
38932             this.focusEl.focus();
38933         }
38934     },
38935
38936     /**
38937      * Hides this menu and optionally all parent menus
38938      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38939      */
38940     hide : function(deep){
38941         if(this.el && this.isVisible()){
38942             this.fireEvent("beforehide", this);
38943             if(this.activeItem){
38944                 this.activeItem.deactivate();
38945                 this.activeItem = null;
38946             }
38947             this.el.hide();
38948             this.hidden = true;
38949             this.fireEvent("hide", this);
38950         }
38951         if(deep === true && this.parentMenu){
38952             this.parentMenu.hide(true);
38953         }
38954     },
38955
38956     /**
38957      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38958      * Any of the following are valid:
38959      * <ul>
38960      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38961      * <li>An HTMLElement object which will be converted to a menu item</li>
38962      * <li>A menu item config object that will be created as a new menu item</li>
38963      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38964      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38965      * </ul>
38966      * Usage:
38967      * <pre><code>
38968 // Create the menu
38969 var menu = new Roo.menu.Menu();
38970
38971 // Create a menu item to add by reference
38972 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38973
38974 // Add a bunch of items at once using different methods.
38975 // Only the last item added will be returned.
38976 var item = menu.add(
38977     menuItem,                // add existing item by ref
38978     'Dynamic Item',          // new TextItem
38979     '-',                     // new separator
38980     { text: 'Config Item' }  // new item by config
38981 );
38982 </code></pre>
38983      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38984      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38985      */
38986     add : function(){
38987         var a = arguments, l = a.length, item;
38988         for(var i = 0; i < l; i++){
38989             var el = a[i];
38990             if ((typeof(el) == "object") && el.xtype && el.xns) {
38991                 el = Roo.factory(el, Roo.menu);
38992             }
38993             
38994             if(el.render){ // some kind of Item
38995                 item = this.addItem(el);
38996             }else if(typeof el == "string"){ // string
38997                 if(el == "separator" || el == "-"){
38998                     item = this.addSeparator();
38999                 }else{
39000                     item = this.addText(el);
39001                 }
39002             }else if(el.tagName || el.el){ // element
39003                 item = this.addElement(el);
39004             }else if(typeof el == "object"){ // must be menu item config?
39005                 item = this.addMenuItem(el);
39006             }
39007         }
39008         return item;
39009     },
39010
39011     /**
39012      * Returns this menu's underlying {@link Roo.Element} object
39013      * @return {Roo.Element} The element
39014      */
39015     getEl : function(){
39016         if(!this.el){
39017             this.render();
39018         }
39019         return this.el;
39020     },
39021
39022     /**
39023      * Adds a separator bar to the menu
39024      * @return {Roo.menu.Item} The menu item that was added
39025      */
39026     addSeparator : function(){
39027         return this.addItem(new Roo.menu.Separator());
39028     },
39029
39030     /**
39031      * Adds an {@link Roo.Element} object to the menu
39032      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39033      * @return {Roo.menu.Item} The menu item that was added
39034      */
39035     addElement : function(el){
39036         return this.addItem(new Roo.menu.BaseItem(el));
39037     },
39038
39039     /**
39040      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39041      * @param {Roo.menu.Item} item The menu item to add
39042      * @return {Roo.menu.Item} The menu item that was added
39043      */
39044     addItem : function(item){
39045         this.items.add(item);
39046         if(this.ul){
39047             var li = document.createElement("li");
39048             li.className = "x-menu-list-item";
39049             this.ul.dom.appendChild(li);
39050             item.render(li, this);
39051             this.delayAutoWidth();
39052         }
39053         return item;
39054     },
39055
39056     /**
39057      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39058      * @param {Object} config A MenuItem config object
39059      * @return {Roo.menu.Item} The menu item that was added
39060      */
39061     addMenuItem : function(config){
39062         if(!(config instanceof Roo.menu.Item)){
39063             if(typeof config.checked == "boolean"){ // must be check menu item config?
39064                 config = new Roo.menu.CheckItem(config);
39065             }else{
39066                 config = new Roo.menu.Item(config);
39067             }
39068         }
39069         return this.addItem(config);
39070     },
39071
39072     /**
39073      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39074      * @param {String} text The text to display in the menu item
39075      * @return {Roo.menu.Item} The menu item that was added
39076      */
39077     addText : function(text){
39078         return this.addItem(new Roo.menu.TextItem({ text : text }));
39079     },
39080
39081     /**
39082      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39083      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39084      * @param {Roo.menu.Item} item The menu item to add
39085      * @return {Roo.menu.Item} The menu item that was added
39086      */
39087     insert : function(index, item){
39088         this.items.insert(index, item);
39089         if(this.ul){
39090             var li = document.createElement("li");
39091             li.className = "x-menu-list-item";
39092             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39093             item.render(li, this);
39094             this.delayAutoWidth();
39095         }
39096         return item;
39097     },
39098
39099     /**
39100      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39101      * @param {Roo.menu.Item} item The menu item to remove
39102      */
39103     remove : function(item){
39104         this.items.removeKey(item.id);
39105         item.destroy();
39106     },
39107
39108     /**
39109      * Removes and destroys all items in the menu
39110      */
39111     removeAll : function(){
39112         var f;
39113         while(f = this.items.first()){
39114             this.remove(f);
39115         }
39116     }
39117 });
39118
39119 // MenuNav is a private utility class used internally by the Menu
39120 Roo.menu.MenuNav = function(menu){
39121     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39122     this.scope = this.menu = menu;
39123 };
39124
39125 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39126     doRelay : function(e, h){
39127         var k = e.getKey();
39128         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39129             this.menu.tryActivate(0, 1);
39130             return false;
39131         }
39132         return h.call(this.scope || this, e, this.menu);
39133     },
39134
39135     up : function(e, m){
39136         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39137             m.tryActivate(m.items.length-1, -1);
39138         }
39139     },
39140
39141     down : function(e, m){
39142         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39143             m.tryActivate(0, 1);
39144         }
39145     },
39146
39147     right : function(e, m){
39148         if(m.activeItem){
39149             m.activeItem.expandMenu(true);
39150         }
39151     },
39152
39153     left : function(e, m){
39154         m.hide();
39155         if(m.parentMenu && m.parentMenu.activeItem){
39156             m.parentMenu.activeItem.activate();
39157         }
39158     },
39159
39160     enter : function(e, m){
39161         if(m.activeItem){
39162             e.stopPropagation();
39163             m.activeItem.onClick(e);
39164             m.fireEvent("click", this, m.activeItem);
39165             return true;
39166         }
39167     }
39168 });/*
39169  * Based on:
39170  * Ext JS Library 1.1.1
39171  * Copyright(c) 2006-2007, Ext JS, LLC.
39172  *
39173  * Originally Released Under LGPL - original licence link has changed is not relivant.
39174  *
39175  * Fork - LGPL
39176  * <script type="text/javascript">
39177  */
39178  
39179 /**
39180  * @class Roo.menu.MenuMgr
39181  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39182  * @static
39183  */
39184 Roo.menu.MenuMgr = function(){
39185    var menus, active, groups = {}, attached = false, lastShow = new Date();
39186
39187    // private - called when first menu is created
39188    function init(){
39189        menus = {};
39190        active = new Roo.util.MixedCollection();
39191        Roo.get(document).addKeyListener(27, function(){
39192            if(active.length > 0){
39193                hideAll();
39194            }
39195        });
39196    }
39197
39198    // private
39199    function hideAll(){
39200        if(active && active.length > 0){
39201            var c = active.clone();
39202            c.each(function(m){
39203                m.hide();
39204            });
39205        }
39206    }
39207
39208    // private
39209    function onHide(m){
39210        active.remove(m);
39211        if(active.length < 1){
39212            Roo.get(document).un("mousedown", onMouseDown);
39213            attached = false;
39214        }
39215    }
39216
39217    // private
39218    function onShow(m){
39219        var last = active.last();
39220        lastShow = new Date();
39221        active.add(m);
39222        if(!attached){
39223            Roo.get(document).on("mousedown", onMouseDown);
39224            attached = true;
39225        }
39226        if(m.parentMenu){
39227           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39228           m.parentMenu.activeChild = m;
39229        }else if(last && last.isVisible()){
39230           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39231        }
39232    }
39233
39234    // private
39235    function onBeforeHide(m){
39236        if(m.activeChild){
39237            m.activeChild.hide();
39238        }
39239        if(m.autoHideTimer){
39240            clearTimeout(m.autoHideTimer);
39241            delete m.autoHideTimer;
39242        }
39243    }
39244
39245    // private
39246    function onBeforeShow(m){
39247        var pm = m.parentMenu;
39248        if(!pm && !m.allowOtherMenus){
39249            hideAll();
39250        }else if(pm && pm.activeChild && active != m){
39251            pm.activeChild.hide();
39252        }
39253    }
39254
39255    // private
39256    function onMouseDown(e){
39257        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39258            hideAll();
39259        }
39260    }
39261
39262    // private
39263    function onBeforeCheck(mi, state){
39264        if(state){
39265            var g = groups[mi.group];
39266            for(var i = 0, l = g.length; i < l; i++){
39267                if(g[i] != mi){
39268                    g[i].setChecked(false);
39269                }
39270            }
39271        }
39272    }
39273
39274    return {
39275
39276        /**
39277         * Hides all menus that are currently visible
39278         */
39279        hideAll : function(){
39280             hideAll();  
39281        },
39282
39283        // private
39284        register : function(menu){
39285            if(!menus){
39286                init();
39287            }
39288            menus[menu.id] = menu;
39289            menu.on("beforehide", onBeforeHide);
39290            menu.on("hide", onHide);
39291            menu.on("beforeshow", onBeforeShow);
39292            menu.on("show", onShow);
39293            var g = menu.group;
39294            if(g && menu.events["checkchange"]){
39295                if(!groups[g]){
39296                    groups[g] = [];
39297                }
39298                groups[g].push(menu);
39299                menu.on("checkchange", onCheck);
39300            }
39301        },
39302
39303         /**
39304          * Returns a {@link Roo.menu.Menu} object
39305          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39306          * be used to generate and return a new Menu instance.
39307          */
39308        get : function(menu){
39309            if(typeof menu == "string"){ // menu id
39310                return menus[menu];
39311            }else if(menu.events){  // menu instance
39312                return menu;
39313            }else if(typeof menu.length == 'number'){ // array of menu items?
39314                return new Roo.menu.Menu({items:menu});
39315            }else{ // otherwise, must be a config
39316                return new Roo.menu.Menu(menu);
39317            }
39318        },
39319
39320        // private
39321        unregister : function(menu){
39322            delete menus[menu.id];
39323            menu.un("beforehide", onBeforeHide);
39324            menu.un("hide", onHide);
39325            menu.un("beforeshow", onBeforeShow);
39326            menu.un("show", onShow);
39327            var g = menu.group;
39328            if(g && menu.events["checkchange"]){
39329                groups[g].remove(menu);
39330                menu.un("checkchange", onCheck);
39331            }
39332        },
39333
39334        // private
39335        registerCheckable : function(menuItem){
39336            var g = menuItem.group;
39337            if(g){
39338                if(!groups[g]){
39339                    groups[g] = [];
39340                }
39341                groups[g].push(menuItem);
39342                menuItem.on("beforecheckchange", onBeforeCheck);
39343            }
39344        },
39345
39346        // private
39347        unregisterCheckable : function(menuItem){
39348            var g = menuItem.group;
39349            if(g){
39350                groups[g].remove(menuItem);
39351                menuItem.un("beforecheckchange", onBeforeCheck);
39352            }
39353        }
39354    };
39355 }();/*
39356  * Based on:
39357  * Ext JS Library 1.1.1
39358  * Copyright(c) 2006-2007, Ext JS, LLC.
39359  *
39360  * Originally Released Under LGPL - original licence link has changed is not relivant.
39361  *
39362  * Fork - LGPL
39363  * <script type="text/javascript">
39364  */
39365  
39366
39367 /**
39368  * @class Roo.menu.BaseItem
39369  * @extends Roo.Component
39370  * @abstract
39371  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39372  * management and base configuration options shared by all menu components.
39373  * @constructor
39374  * Creates a new BaseItem
39375  * @param {Object} config Configuration options
39376  */
39377 Roo.menu.BaseItem = function(config){
39378     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39379
39380     this.addEvents({
39381         /**
39382          * @event click
39383          * Fires when this item is clicked
39384          * @param {Roo.menu.BaseItem} this
39385          * @param {Roo.EventObject} e
39386          */
39387         click: true,
39388         /**
39389          * @event activate
39390          * Fires when this item is activated
39391          * @param {Roo.menu.BaseItem} this
39392          */
39393         activate : true,
39394         /**
39395          * @event deactivate
39396          * Fires when this item is deactivated
39397          * @param {Roo.menu.BaseItem} this
39398          */
39399         deactivate : true
39400     });
39401
39402     if(this.handler){
39403         this.on("click", this.handler, this.scope, true);
39404     }
39405 };
39406
39407 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39408     /**
39409      * @cfg {Function} handler
39410      * A function that will handle the click event of this menu item (defaults to undefined)
39411      */
39412     /**
39413      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39414      */
39415     canActivate : false,
39416     
39417      /**
39418      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39419      */
39420     hidden: false,
39421     
39422     /**
39423      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39424      */
39425     activeClass : "x-menu-item-active",
39426     /**
39427      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39428      */
39429     hideOnClick : true,
39430     /**
39431      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39432      */
39433     hideDelay : 100,
39434
39435     // private
39436     ctype: "Roo.menu.BaseItem",
39437
39438     // private
39439     actionMode : "container",
39440
39441     // private
39442     render : function(container, parentMenu){
39443         this.parentMenu = parentMenu;
39444         Roo.menu.BaseItem.superclass.render.call(this, container);
39445         this.container.menuItemId = this.id;
39446     },
39447
39448     // private
39449     onRender : function(container, position){
39450         this.el = Roo.get(this.el);
39451         container.dom.appendChild(this.el.dom);
39452     },
39453
39454     // private
39455     onClick : function(e){
39456         if(!this.disabled && this.fireEvent("click", this, e) !== false
39457                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39458             this.handleClick(e);
39459         }else{
39460             e.stopEvent();
39461         }
39462     },
39463
39464     // private
39465     activate : function(){
39466         if(this.disabled){
39467             return false;
39468         }
39469         var li = this.container;
39470         li.addClass(this.activeClass);
39471         this.region = li.getRegion().adjust(2, 2, -2, -2);
39472         this.fireEvent("activate", this);
39473         return true;
39474     },
39475
39476     // private
39477     deactivate : function(){
39478         this.container.removeClass(this.activeClass);
39479         this.fireEvent("deactivate", this);
39480     },
39481
39482     // private
39483     shouldDeactivate : function(e){
39484         return !this.region || !this.region.contains(e.getPoint());
39485     },
39486
39487     // private
39488     handleClick : function(e){
39489         if(this.hideOnClick){
39490             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39491         }
39492     },
39493
39494     // private
39495     expandMenu : function(autoActivate){
39496         // do nothing
39497     },
39498
39499     // private
39500     hideMenu : function(){
39501         // do nothing
39502     }
39503 });/*
39504  * Based on:
39505  * Ext JS Library 1.1.1
39506  * Copyright(c) 2006-2007, Ext JS, LLC.
39507  *
39508  * Originally Released Under LGPL - original licence link has changed is not relivant.
39509  *
39510  * Fork - LGPL
39511  * <script type="text/javascript">
39512  */
39513  
39514 /**
39515  * @class Roo.menu.Adapter
39516  * @extends Roo.menu.BaseItem
39517  * @abstract
39518  * 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.
39519  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39520  * @constructor
39521  * Creates a new Adapter
39522  * @param {Object} config Configuration options
39523  */
39524 Roo.menu.Adapter = function(component, config){
39525     Roo.menu.Adapter.superclass.constructor.call(this, config);
39526     this.component = component;
39527 };
39528 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39529     // private
39530     canActivate : true,
39531
39532     // private
39533     onRender : function(container, position){
39534         this.component.render(container);
39535         this.el = this.component.getEl();
39536     },
39537
39538     // private
39539     activate : function(){
39540         if(this.disabled){
39541             return false;
39542         }
39543         this.component.focus();
39544         this.fireEvent("activate", this);
39545         return true;
39546     },
39547
39548     // private
39549     deactivate : function(){
39550         this.fireEvent("deactivate", this);
39551     },
39552
39553     // private
39554     disable : function(){
39555         this.component.disable();
39556         Roo.menu.Adapter.superclass.disable.call(this);
39557     },
39558
39559     // private
39560     enable : function(){
39561         this.component.enable();
39562         Roo.menu.Adapter.superclass.enable.call(this);
39563     }
39564 });/*
39565  * Based on:
39566  * Ext JS Library 1.1.1
39567  * Copyright(c) 2006-2007, Ext JS, LLC.
39568  *
39569  * Originally Released Under LGPL - original licence link has changed is not relivant.
39570  *
39571  * Fork - LGPL
39572  * <script type="text/javascript">
39573  */
39574
39575 /**
39576  * @class Roo.menu.TextItem
39577  * @extends Roo.menu.BaseItem
39578  * Adds a static text string to a menu, usually used as either a heading or group separator.
39579  * Note: old style constructor with text is still supported.
39580  * 
39581  * @constructor
39582  * Creates a new TextItem
39583  * @param {Object} cfg Configuration
39584  */
39585 Roo.menu.TextItem = function(cfg){
39586     if (typeof(cfg) == 'string') {
39587         this.text = cfg;
39588     } else {
39589         Roo.apply(this,cfg);
39590     }
39591     
39592     Roo.menu.TextItem.superclass.constructor.call(this);
39593 };
39594
39595 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39596     /**
39597      * @cfg {String} text Text to show on item.
39598      */
39599     text : '',
39600     
39601     /**
39602      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39603      */
39604     hideOnClick : false,
39605     /**
39606      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39607      */
39608     itemCls : "x-menu-text",
39609
39610     // private
39611     onRender : function(){
39612         var s = document.createElement("span");
39613         s.className = this.itemCls;
39614         s.innerHTML = this.text;
39615         this.el = s;
39616         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39617     }
39618 });/*
39619  * Based on:
39620  * Ext JS Library 1.1.1
39621  * Copyright(c) 2006-2007, Ext JS, LLC.
39622  *
39623  * Originally Released Under LGPL - original licence link has changed is not relivant.
39624  *
39625  * Fork - LGPL
39626  * <script type="text/javascript">
39627  */
39628
39629 /**
39630  * @class Roo.menu.Separator
39631  * @extends Roo.menu.BaseItem
39632  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39633  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39634  * @constructor
39635  * @param {Object} config Configuration options
39636  */
39637 Roo.menu.Separator = function(config){
39638     Roo.menu.Separator.superclass.constructor.call(this, config);
39639 };
39640
39641 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39642     /**
39643      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39644      */
39645     itemCls : "x-menu-sep",
39646     /**
39647      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39648      */
39649     hideOnClick : false,
39650
39651     // private
39652     onRender : function(li){
39653         var s = document.createElement("span");
39654         s.className = this.itemCls;
39655         s.innerHTML = "&#160;";
39656         this.el = s;
39657         li.addClass("x-menu-sep-li");
39658         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39659     }
39660 });/*
39661  * Based on:
39662  * Ext JS Library 1.1.1
39663  * Copyright(c) 2006-2007, Ext JS, LLC.
39664  *
39665  * Originally Released Under LGPL - original licence link has changed is not relivant.
39666  *
39667  * Fork - LGPL
39668  * <script type="text/javascript">
39669  */
39670 /**
39671  * @class Roo.menu.Item
39672  * @extends Roo.menu.BaseItem
39673  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39674  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39675  * activation and click handling.
39676  * @constructor
39677  * Creates a new Item
39678  * @param {Object} config Configuration options
39679  */
39680 Roo.menu.Item = function(config){
39681     Roo.menu.Item.superclass.constructor.call(this, config);
39682     if(this.menu){
39683         this.menu = Roo.menu.MenuMgr.get(this.menu);
39684     }
39685 };
39686 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39687     /**
39688      * @cfg {Roo.menu.Menu} menu
39689      * A Sub menu
39690      */
39691     /**
39692      * @cfg {String} text
39693      * The text to show on the menu item.
39694      */
39695     text: '',
39696      /**
39697      * @cfg {String} HTML to render in menu
39698      * The text to show on the menu item (HTML version).
39699      */
39700     html: '',
39701     /**
39702      * @cfg {String} icon
39703      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39704      */
39705     icon: undefined,
39706     /**
39707      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39708      */
39709     itemCls : "x-menu-item",
39710     /**
39711      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39712      */
39713     canActivate : true,
39714     /**
39715      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39716      */
39717     showDelay: 200,
39718     // doc'd in BaseItem
39719     hideDelay: 200,
39720
39721     // private
39722     ctype: "Roo.menu.Item",
39723     
39724     // private
39725     onRender : function(container, position){
39726         var el = document.createElement("a");
39727         el.hideFocus = true;
39728         el.unselectable = "on";
39729         el.href = this.href || "#";
39730         if(this.hrefTarget){
39731             el.target = this.hrefTarget;
39732         }
39733         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39734         
39735         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39736         
39737         el.innerHTML = String.format(
39738                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39739                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39740         this.el = el;
39741         Roo.menu.Item.superclass.onRender.call(this, container, position);
39742     },
39743
39744     /**
39745      * Sets the text to display in this menu item
39746      * @param {String} text The text to display
39747      * @param {Boolean} isHTML true to indicate text is pure html.
39748      */
39749     setText : function(text, isHTML){
39750         if (isHTML) {
39751             this.html = text;
39752         } else {
39753             this.text = text;
39754             this.html = '';
39755         }
39756         if(this.rendered){
39757             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39758      
39759             this.el.update(String.format(
39760                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39761                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39762             this.parentMenu.autoWidth();
39763         }
39764     },
39765
39766     // private
39767     handleClick : function(e){
39768         if(!this.href){ // if no link defined, stop the event automatically
39769             e.stopEvent();
39770         }
39771         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39772     },
39773
39774     // private
39775     activate : function(autoExpand){
39776         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39777             this.focus();
39778             if(autoExpand){
39779                 this.expandMenu();
39780             }
39781         }
39782         return true;
39783     },
39784
39785     // private
39786     shouldDeactivate : function(e){
39787         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39788             if(this.menu && this.menu.isVisible()){
39789                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39790             }
39791             return true;
39792         }
39793         return false;
39794     },
39795
39796     // private
39797     deactivate : function(){
39798         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39799         this.hideMenu();
39800     },
39801
39802     // private
39803     expandMenu : function(autoActivate){
39804         if(!this.disabled && this.menu){
39805             clearTimeout(this.hideTimer);
39806             delete this.hideTimer;
39807             if(!this.menu.isVisible() && !this.showTimer){
39808                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39809             }else if (this.menu.isVisible() && autoActivate){
39810                 this.menu.tryActivate(0, 1);
39811             }
39812         }
39813     },
39814
39815     // private
39816     deferExpand : function(autoActivate){
39817         delete this.showTimer;
39818         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39819         if(autoActivate){
39820             this.menu.tryActivate(0, 1);
39821         }
39822     },
39823
39824     // private
39825     hideMenu : function(){
39826         clearTimeout(this.showTimer);
39827         delete this.showTimer;
39828         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39829             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39830         }
39831     },
39832
39833     // private
39834     deferHide : function(){
39835         delete this.hideTimer;
39836         this.menu.hide();
39837     }
39838 });/*
39839  * Based on:
39840  * Ext JS Library 1.1.1
39841  * Copyright(c) 2006-2007, Ext JS, LLC.
39842  *
39843  * Originally Released Under LGPL - original licence link has changed is not relivant.
39844  *
39845  * Fork - LGPL
39846  * <script type="text/javascript">
39847  */
39848  
39849 /**
39850  * @class Roo.menu.CheckItem
39851  * @extends Roo.menu.Item
39852  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39853  * @constructor
39854  * Creates a new CheckItem
39855  * @param {Object} config Configuration options
39856  */
39857 Roo.menu.CheckItem = function(config){
39858     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39859     this.addEvents({
39860         /**
39861          * @event beforecheckchange
39862          * Fires before the checked value is set, providing an opportunity to cancel if needed
39863          * @param {Roo.menu.CheckItem} this
39864          * @param {Boolean} checked The new checked value that will be set
39865          */
39866         "beforecheckchange" : true,
39867         /**
39868          * @event checkchange
39869          * Fires after the checked value has been set
39870          * @param {Roo.menu.CheckItem} this
39871          * @param {Boolean} checked The checked value that was set
39872          */
39873         "checkchange" : true
39874     });
39875     if(this.checkHandler){
39876         this.on('checkchange', this.checkHandler, this.scope);
39877     }
39878 };
39879 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39880     /**
39881      * @cfg {String} group
39882      * All check items with the same group name will automatically be grouped into a single-select
39883      * radio button group (defaults to '')
39884      */
39885     /**
39886      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39887      */
39888     itemCls : "x-menu-item x-menu-check-item",
39889     /**
39890      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39891      */
39892     groupClass : "x-menu-group-item",
39893
39894     /**
39895      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39896      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39897      * initialized with checked = true will be rendered as checked.
39898      */
39899     checked: false,
39900
39901     // private
39902     ctype: "Roo.menu.CheckItem",
39903
39904     // private
39905     onRender : function(c){
39906         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39907         if(this.group){
39908             this.el.addClass(this.groupClass);
39909         }
39910         Roo.menu.MenuMgr.registerCheckable(this);
39911         if(this.checked){
39912             this.checked = false;
39913             this.setChecked(true, true);
39914         }
39915     },
39916
39917     // private
39918     destroy : function(){
39919         if(this.rendered){
39920             Roo.menu.MenuMgr.unregisterCheckable(this);
39921         }
39922         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39923     },
39924
39925     /**
39926      * Set the checked state of this item
39927      * @param {Boolean} checked The new checked value
39928      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39929      */
39930     setChecked : function(state, suppressEvent){
39931         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39932             if(this.container){
39933                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39934             }
39935             this.checked = state;
39936             if(suppressEvent !== true){
39937                 this.fireEvent("checkchange", this, state);
39938             }
39939         }
39940     },
39941
39942     // private
39943     handleClick : function(e){
39944        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39945            this.setChecked(!this.checked);
39946        }
39947        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39948     }
39949 });/*
39950  * Based on:
39951  * Ext JS Library 1.1.1
39952  * Copyright(c) 2006-2007, Ext JS, LLC.
39953  *
39954  * Originally Released Under LGPL - original licence link has changed is not relivant.
39955  *
39956  * Fork - LGPL
39957  * <script type="text/javascript">
39958  */
39959  
39960 /**
39961  * @class Roo.menu.DateItem
39962  * @extends Roo.menu.Adapter
39963  * A menu item that wraps the {@link Roo.DatPicker} component.
39964  * @constructor
39965  * Creates a new DateItem
39966  * @param {Object} config Configuration options
39967  */
39968 Roo.menu.DateItem = function(config){
39969     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39970     /** The Roo.DatePicker object @type Roo.DatePicker */
39971     this.picker = this.component;
39972     this.addEvents({select: true});
39973     
39974     this.picker.on("render", function(picker){
39975         picker.getEl().swallowEvent("click");
39976         picker.container.addClass("x-menu-date-item");
39977     });
39978
39979     this.picker.on("select", this.onSelect, this);
39980 };
39981
39982 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39983     // private
39984     onSelect : function(picker, date){
39985         this.fireEvent("select", this, date, picker);
39986         Roo.menu.DateItem.superclass.handleClick.call(this);
39987     }
39988 });/*
39989  * Based on:
39990  * Ext JS Library 1.1.1
39991  * Copyright(c) 2006-2007, Ext JS, LLC.
39992  *
39993  * Originally Released Under LGPL - original licence link has changed is not relivant.
39994  *
39995  * Fork - LGPL
39996  * <script type="text/javascript">
39997  */
39998  
39999 /**
40000  * @class Roo.menu.ColorItem
40001  * @extends Roo.menu.Adapter
40002  * A menu item that wraps the {@link Roo.ColorPalette} component.
40003  * @constructor
40004  * Creates a new ColorItem
40005  * @param {Object} config Configuration options
40006  */
40007 Roo.menu.ColorItem = function(config){
40008     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40009     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40010     this.palette = this.component;
40011     this.relayEvents(this.palette, ["select"]);
40012     if(this.selectHandler){
40013         this.on('select', this.selectHandler, this.scope);
40014     }
40015 };
40016 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40017  * Based on:
40018  * Ext JS Library 1.1.1
40019  * Copyright(c) 2006-2007, Ext JS, LLC.
40020  *
40021  * Originally Released Under LGPL - original licence link has changed is not relivant.
40022  *
40023  * Fork - LGPL
40024  * <script type="text/javascript">
40025  */
40026  
40027
40028 /**
40029  * @class Roo.menu.DateMenu
40030  * @extends Roo.menu.Menu
40031  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40032  * @constructor
40033  * Creates a new DateMenu
40034  * @param {Object} config Configuration options
40035  */
40036 Roo.menu.DateMenu = function(config){
40037     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40038     this.plain = true;
40039     var di = new Roo.menu.DateItem(config);
40040     this.add(di);
40041     /**
40042      * The {@link Roo.DatePicker} instance for this DateMenu
40043      * @type DatePicker
40044      */
40045     this.picker = di.picker;
40046     /**
40047      * @event select
40048      * @param {DatePicker} picker
40049      * @param {Date} date
40050      */
40051     this.relayEvents(di, ["select"]);
40052     this.on('beforeshow', function(){
40053         if(this.picker){
40054             this.picker.hideMonthPicker(false);
40055         }
40056     }, this);
40057 };
40058 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40059     cls:'x-date-menu'
40060 });/*
40061  * Based on:
40062  * Ext JS Library 1.1.1
40063  * Copyright(c) 2006-2007, Ext JS, LLC.
40064  *
40065  * Originally Released Under LGPL - original licence link has changed is not relivant.
40066  *
40067  * Fork - LGPL
40068  * <script type="text/javascript">
40069  */
40070  
40071
40072 /**
40073  * @class Roo.menu.ColorMenu
40074  * @extends Roo.menu.Menu
40075  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40076  * @constructor
40077  * Creates a new ColorMenu
40078  * @param {Object} config Configuration options
40079  */
40080 Roo.menu.ColorMenu = function(config){
40081     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40082     this.plain = true;
40083     var ci = new Roo.menu.ColorItem(config);
40084     this.add(ci);
40085     /**
40086      * The {@link Roo.ColorPalette} instance for this ColorMenu
40087      * @type ColorPalette
40088      */
40089     this.palette = ci.palette;
40090     /**
40091      * @event select
40092      * @param {ColorPalette} palette
40093      * @param {String} color
40094      */
40095     this.relayEvents(ci, ["select"]);
40096 };
40097 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40098  * Based on:
40099  * Ext JS Library 1.1.1
40100  * Copyright(c) 2006-2007, Ext JS, LLC.
40101  *
40102  * Originally Released Under LGPL - original licence link has changed is not relivant.
40103  *
40104  * Fork - LGPL
40105  * <script type="text/javascript">
40106  */
40107  
40108 /**
40109  * @class Roo.form.TextItem
40110  * @extends Roo.BoxComponent
40111  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40112  * @constructor
40113  * Creates a new TextItem
40114  * @param {Object} config Configuration options
40115  */
40116 Roo.form.TextItem = function(config){
40117     Roo.form.TextItem.superclass.constructor.call(this, config);
40118 };
40119
40120 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40121     
40122     /**
40123      * @cfg {String} tag the tag for this item (default div)
40124      */
40125     tag : 'div',
40126     /**
40127      * @cfg {String} html the content for this item
40128      */
40129     html : '',
40130     
40131     getAutoCreate : function()
40132     {
40133         var cfg = {
40134             id: this.id,
40135             tag: this.tag,
40136             html: this.html,
40137             cls: 'x-form-item'
40138         };
40139         
40140         return cfg;
40141         
40142     },
40143     
40144     onRender : function(ct, position)
40145     {
40146         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40147         
40148         if(!this.el){
40149             var cfg = this.getAutoCreate();
40150             if(!cfg.name){
40151                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40152             }
40153             if (!cfg.name.length) {
40154                 delete cfg.name;
40155             }
40156             this.el = ct.createChild(cfg, position);
40157         }
40158     },
40159     /*
40160      * setHTML
40161      * @param {String} html update the Contents of the element.
40162      */
40163     setHTML : function(html)
40164     {
40165         this.fieldEl.dom.innerHTML = html;
40166     }
40167     
40168 });/*
40169  * Based on:
40170  * Ext JS Library 1.1.1
40171  * Copyright(c) 2006-2007, Ext JS, LLC.
40172  *
40173  * Originally Released Under LGPL - original licence link has changed is not relivant.
40174  *
40175  * Fork - LGPL
40176  * <script type="text/javascript">
40177  */
40178  
40179 /**
40180  * @class Roo.form.Field
40181  * @extends Roo.BoxComponent
40182  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40183  * @constructor
40184  * Creates a new Field
40185  * @param {Object} config Configuration options
40186  */
40187 Roo.form.Field = function(config){
40188     Roo.form.Field.superclass.constructor.call(this, config);
40189 };
40190
40191 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40192     /**
40193      * @cfg {String} fieldLabel Label to use when rendering a form.
40194      */
40195        /**
40196      * @cfg {String} qtip Mouse over tip
40197      */
40198      
40199     /**
40200      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40201      */
40202     invalidClass : "x-form-invalid",
40203     /**
40204      * @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")
40205      */
40206     invalidText : "The value in this field is invalid",
40207     /**
40208      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40209      */
40210     focusClass : "x-form-focus",
40211     /**
40212      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40213       automatic validation (defaults to "keyup").
40214      */
40215     validationEvent : "keyup",
40216     /**
40217      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40218      */
40219     validateOnBlur : true,
40220     /**
40221      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40222      */
40223     validationDelay : 250,
40224     /**
40225      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40226      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40227      */
40228     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40229     /**
40230      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40231      */
40232     fieldClass : "x-form-field",
40233     /**
40234      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40235      *<pre>
40236 Value         Description
40237 -----------   ----------------------------------------------------------------------
40238 qtip          Display a quick tip when the user hovers over the field
40239 title         Display a default browser title attribute popup
40240 under         Add a block div beneath the field containing the error text
40241 side          Add an error icon to the right of the field with a popup on hover
40242 [element id]  Add the error text directly to the innerHTML of the specified element
40243 </pre>
40244      */
40245     msgTarget : 'qtip',
40246     /**
40247      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40248      */
40249     msgFx : 'normal',
40250
40251     /**
40252      * @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.
40253      */
40254     readOnly : false,
40255
40256     /**
40257      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40258      */
40259     disabled : false,
40260
40261     /**
40262      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40263      */
40264     inputType : undefined,
40265     
40266     /**
40267      * @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).
40268          */
40269         tabIndex : undefined,
40270         
40271     // private
40272     isFormField : true,
40273
40274     // private
40275     hasFocus : false,
40276     /**
40277      * @property {Roo.Element} fieldEl
40278      * Element Containing the rendered Field (with label etc.)
40279      */
40280     /**
40281      * @cfg {Mixed} value A value to initialize this field with.
40282      */
40283     value : undefined,
40284
40285     /**
40286      * @cfg {String} name The field's HTML name attribute.
40287      */
40288     /**
40289      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40290      */
40291     // private
40292     loadedValue : false,
40293      
40294      
40295         // private ??
40296         initComponent : function(){
40297         Roo.form.Field.superclass.initComponent.call(this);
40298         this.addEvents({
40299             /**
40300              * @event focus
40301              * Fires when this field receives input focus.
40302              * @param {Roo.form.Field} this
40303              */
40304             focus : true,
40305             /**
40306              * @event blur
40307              * Fires when this field loses input focus.
40308              * @param {Roo.form.Field} this
40309              */
40310             blur : true,
40311             /**
40312              * @event specialkey
40313              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40314              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40315              * @param {Roo.form.Field} this
40316              * @param {Roo.EventObject} e The event object
40317              */
40318             specialkey : true,
40319             /**
40320              * @event change
40321              * Fires just before the field blurs if the field value has changed.
40322              * @param {Roo.form.Field} this
40323              * @param {Mixed} newValue The new value
40324              * @param {Mixed} oldValue The original value
40325              */
40326             change : true,
40327             /**
40328              * @event invalid
40329              * Fires after the field has been marked as invalid.
40330              * @param {Roo.form.Field} this
40331              * @param {String} msg The validation message
40332              */
40333             invalid : true,
40334             /**
40335              * @event valid
40336              * Fires after the field has been validated with no errors.
40337              * @param {Roo.form.Field} this
40338              */
40339             valid : true,
40340              /**
40341              * @event keyup
40342              * Fires after the key up
40343              * @param {Roo.form.Field} this
40344              * @param {Roo.EventObject}  e The event Object
40345              */
40346             keyup : true
40347         });
40348     },
40349
40350     /**
40351      * Returns the name attribute of the field if available
40352      * @return {String} name The field name
40353      */
40354     getName: function(){
40355          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40356     },
40357
40358     // private
40359     onRender : function(ct, position){
40360         Roo.form.Field.superclass.onRender.call(this, ct, position);
40361         if(!this.el){
40362             var cfg = this.getAutoCreate();
40363             if(!cfg.name){
40364                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40365             }
40366             if (!cfg.name.length) {
40367                 delete cfg.name;
40368             }
40369             if(this.inputType){
40370                 cfg.type = this.inputType;
40371             }
40372             this.el = ct.createChild(cfg, position);
40373         }
40374         var type = this.el.dom.type;
40375         if(type){
40376             if(type == 'password'){
40377                 type = 'text';
40378             }
40379             this.el.addClass('x-form-'+type);
40380         }
40381         if(this.readOnly){
40382             this.el.dom.readOnly = true;
40383         }
40384         if(this.tabIndex !== undefined){
40385             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40386         }
40387
40388         this.el.addClass([this.fieldClass, this.cls]);
40389         this.initValue();
40390     },
40391
40392     /**
40393      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40394      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40395      * @return {Roo.form.Field} this
40396      */
40397     applyTo : function(target){
40398         this.allowDomMove = false;
40399         this.el = Roo.get(target);
40400         this.render(this.el.dom.parentNode);
40401         return this;
40402     },
40403
40404     // private
40405     initValue : function(){
40406         if(this.value !== undefined){
40407             this.setValue(this.value);
40408         }else if(this.el.dom.value.length > 0){
40409             this.setValue(this.el.dom.value);
40410         }
40411     },
40412
40413     /**
40414      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40415      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40416      */
40417     isDirty : function() {
40418         if(this.disabled) {
40419             return false;
40420         }
40421         return String(this.getValue()) !== String(this.originalValue);
40422     },
40423
40424     /**
40425      * stores the current value in loadedValue
40426      */
40427     resetHasChanged : function()
40428     {
40429         this.loadedValue = String(this.getValue());
40430     },
40431     /**
40432      * checks the current value against the 'loaded' value.
40433      * Note - will return false if 'resetHasChanged' has not been called first.
40434      */
40435     hasChanged : function()
40436     {
40437         if(this.disabled || this.readOnly) {
40438             return false;
40439         }
40440         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40441     },
40442     
40443     
40444     
40445     // private
40446     afterRender : function(){
40447         Roo.form.Field.superclass.afterRender.call(this);
40448         this.initEvents();
40449     },
40450
40451     // private
40452     fireKey : function(e){
40453         //Roo.log('field ' + e.getKey());
40454         if(e.isNavKeyPress()){
40455             this.fireEvent("specialkey", this, e);
40456         }
40457     },
40458
40459     /**
40460      * Resets the current field value to the originally loaded value and clears any validation messages
40461      */
40462     reset : function(){
40463         this.setValue(this.resetValue);
40464         this.originalValue = this.getValue();
40465         this.clearInvalid();
40466     },
40467
40468     // private
40469     initEvents : function(){
40470         // safari killled keypress - so keydown is now used..
40471         this.el.on("keydown" , this.fireKey,  this);
40472         this.el.on("focus", this.onFocus,  this);
40473         this.el.on("blur", this.onBlur,  this);
40474         this.el.relayEvent('keyup', this);
40475
40476         // reference to original value for reset
40477         this.originalValue = this.getValue();
40478         this.resetValue =  this.getValue();
40479     },
40480
40481     // private
40482     onFocus : function(){
40483         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40484             this.el.addClass(this.focusClass);
40485         }
40486         if(!this.hasFocus){
40487             this.hasFocus = true;
40488             this.startValue = this.getValue();
40489             this.fireEvent("focus", this);
40490         }
40491     },
40492
40493     beforeBlur : Roo.emptyFn,
40494
40495     // private
40496     onBlur : function(){
40497         this.beforeBlur();
40498         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40499             this.el.removeClass(this.focusClass);
40500         }
40501         this.hasFocus = false;
40502         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40503             this.validate();
40504         }
40505         var v = this.getValue();
40506         if(String(v) !== String(this.startValue)){
40507             this.fireEvent('change', this, v, this.startValue);
40508         }
40509         this.fireEvent("blur", this);
40510     },
40511
40512     /**
40513      * Returns whether or not the field value is currently valid
40514      * @param {Boolean} preventMark True to disable marking the field invalid
40515      * @return {Boolean} True if the value is valid, else false
40516      */
40517     isValid : function(preventMark){
40518         if(this.disabled){
40519             return true;
40520         }
40521         var restore = this.preventMark;
40522         this.preventMark = preventMark === true;
40523         var v = this.validateValue(this.processValue(this.getRawValue()));
40524         this.preventMark = restore;
40525         return v;
40526     },
40527
40528     /**
40529      * Validates the field value
40530      * @return {Boolean} True if the value is valid, else false
40531      */
40532     validate : function(){
40533         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40534             this.clearInvalid();
40535             return true;
40536         }
40537         return false;
40538     },
40539
40540     processValue : function(value){
40541         return value;
40542     },
40543
40544     // private
40545     // Subclasses should provide the validation implementation by overriding this
40546     validateValue : function(value){
40547         return true;
40548     },
40549
40550     /**
40551      * Mark this field as invalid
40552      * @param {String} msg The validation message
40553      */
40554     markInvalid : function(msg){
40555         if(!this.rendered || this.preventMark){ // not rendered
40556             return;
40557         }
40558         
40559         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40560         
40561         obj.el.addClass(this.invalidClass);
40562         msg = msg || this.invalidText;
40563         switch(this.msgTarget){
40564             case 'qtip':
40565                 obj.el.dom.qtip = msg;
40566                 obj.el.dom.qclass = 'x-form-invalid-tip';
40567                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40568                     Roo.QuickTips.enable();
40569                 }
40570                 break;
40571             case 'title':
40572                 this.el.dom.title = msg;
40573                 break;
40574             case 'under':
40575                 if(!this.errorEl){
40576                     var elp = this.el.findParent('.x-form-element', 5, true);
40577                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40578                     this.errorEl.setWidth(elp.getWidth(true)-20);
40579                 }
40580                 this.errorEl.update(msg);
40581                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40582                 break;
40583             case 'side':
40584                 if(!this.errorIcon){
40585                     var elp = this.el.findParent('.x-form-element', 5, true);
40586                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40587                 }
40588                 this.alignErrorIcon();
40589                 this.errorIcon.dom.qtip = msg;
40590                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40591                 this.errorIcon.show();
40592                 this.on('resize', this.alignErrorIcon, this);
40593                 break;
40594             default:
40595                 var t = Roo.getDom(this.msgTarget);
40596                 t.innerHTML = msg;
40597                 t.style.display = this.msgDisplay;
40598                 break;
40599         }
40600         this.fireEvent('invalid', this, msg);
40601     },
40602
40603     // private
40604     alignErrorIcon : function(){
40605         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40606     },
40607
40608     /**
40609      * Clear any invalid styles/messages for this field
40610      */
40611     clearInvalid : function(){
40612         if(!this.rendered || this.preventMark){ // not rendered
40613             return;
40614         }
40615         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40616         
40617         obj.el.removeClass(this.invalidClass);
40618         switch(this.msgTarget){
40619             case 'qtip':
40620                 obj.el.dom.qtip = '';
40621                 break;
40622             case 'title':
40623                 this.el.dom.title = '';
40624                 break;
40625             case 'under':
40626                 if(this.errorEl){
40627                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40628                 }
40629                 break;
40630             case 'side':
40631                 if(this.errorIcon){
40632                     this.errorIcon.dom.qtip = '';
40633                     this.errorIcon.hide();
40634                     this.un('resize', this.alignErrorIcon, this);
40635                 }
40636                 break;
40637             default:
40638                 var t = Roo.getDom(this.msgTarget);
40639                 t.innerHTML = '';
40640                 t.style.display = 'none';
40641                 break;
40642         }
40643         this.fireEvent('valid', this);
40644     },
40645
40646     /**
40647      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40648      * @return {Mixed} value The field value
40649      */
40650     getRawValue : function(){
40651         var v = this.el.getValue();
40652         
40653         return v;
40654     },
40655
40656     /**
40657      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40658      * @return {Mixed} value The field value
40659      */
40660     getValue : function(){
40661         var v = this.el.getValue();
40662          
40663         return v;
40664     },
40665
40666     /**
40667      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40668      * @param {Mixed} value The value to set
40669      */
40670     setRawValue : function(v){
40671         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40672     },
40673
40674     /**
40675      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40676      * @param {Mixed} value The value to set
40677      */
40678     setValue : function(v){
40679         this.value = v;
40680         if(this.rendered){
40681             this.el.dom.value = (v === null || v === undefined ? '' : v);
40682              this.validate();
40683         }
40684     },
40685
40686     adjustSize : function(w, h){
40687         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40688         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40689         return s;
40690     },
40691
40692     adjustWidth : function(tag, w){
40693         tag = tag.toLowerCase();
40694         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40695             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40696                 if(tag == 'input'){
40697                     return w + 2;
40698                 }
40699                 if(tag == 'textarea'){
40700                     return w-2;
40701                 }
40702             }else if(Roo.isOpera){
40703                 if(tag == 'input'){
40704                     return w + 2;
40705                 }
40706                 if(tag == 'textarea'){
40707                     return w-2;
40708                 }
40709             }
40710         }
40711         return w;
40712     }
40713 });
40714
40715
40716 // anything other than normal should be considered experimental
40717 Roo.form.Field.msgFx = {
40718     normal : {
40719         show: function(msgEl, f){
40720             msgEl.setDisplayed('block');
40721         },
40722
40723         hide : function(msgEl, f){
40724             msgEl.setDisplayed(false).update('');
40725         }
40726     },
40727
40728     slide : {
40729         show: function(msgEl, f){
40730             msgEl.slideIn('t', {stopFx:true});
40731         },
40732
40733         hide : function(msgEl, f){
40734             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40735         }
40736     },
40737
40738     slideRight : {
40739         show: function(msgEl, f){
40740             msgEl.fixDisplay();
40741             msgEl.alignTo(f.el, 'tl-tr');
40742             msgEl.slideIn('l', {stopFx:true});
40743         },
40744
40745         hide : function(msgEl, f){
40746             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40747         }
40748     }
40749 };/*
40750  * Based on:
40751  * Ext JS Library 1.1.1
40752  * Copyright(c) 2006-2007, Ext JS, LLC.
40753  *
40754  * Originally Released Under LGPL - original licence link has changed is not relivant.
40755  *
40756  * Fork - LGPL
40757  * <script type="text/javascript">
40758  */
40759  
40760
40761 /**
40762  * @class Roo.form.TextField
40763  * @extends Roo.form.Field
40764  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40765  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40766  * @constructor
40767  * Creates a new TextField
40768  * @param {Object} config Configuration options
40769  */
40770 Roo.form.TextField = function(config){
40771     Roo.form.TextField.superclass.constructor.call(this, config);
40772     this.addEvents({
40773         /**
40774          * @event autosize
40775          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40776          * according to the default logic, but this event provides a hook for the developer to apply additional
40777          * logic at runtime to resize the field if needed.
40778              * @param {Roo.form.Field} this This text field
40779              * @param {Number} width The new field width
40780              */
40781         autosize : true
40782     });
40783 };
40784
40785 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40786     /**
40787      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40788      */
40789     grow : false,
40790     /**
40791      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40792      */
40793     growMin : 30,
40794     /**
40795      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40796      */
40797     growMax : 800,
40798     /**
40799      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40800      */
40801     vtype : null,
40802     /**
40803      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40804      */
40805     maskRe : null,
40806     /**
40807      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40808      */
40809     disableKeyFilter : false,
40810     /**
40811      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40812      */
40813     allowBlank : true,
40814     /**
40815      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40816      */
40817     minLength : 0,
40818     /**
40819      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40820      */
40821     maxLength : Number.MAX_VALUE,
40822     /**
40823      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40824      */
40825     minLengthText : "The minimum length for this field is {0}",
40826     /**
40827      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40828      */
40829     maxLengthText : "The maximum length for this field is {0}",
40830     /**
40831      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40832      */
40833     selectOnFocus : false,
40834     /**
40835      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40836      */    
40837     allowLeadingSpace : false,
40838     /**
40839      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40840      */
40841     blankText : "This field is required",
40842     /**
40843      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40844      * If available, this function will be called only after the basic validators all return true, and will be passed the
40845      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40846      */
40847     validator : null,
40848     /**
40849      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40850      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40851      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40852      */
40853     regex : null,
40854     /**
40855      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40856      */
40857     regexText : "",
40858     /**
40859      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40860      */
40861     emptyText : null,
40862    
40863
40864     // private
40865     initEvents : function()
40866     {
40867         if (this.emptyText) {
40868             this.el.attr('placeholder', this.emptyText);
40869         }
40870         
40871         Roo.form.TextField.superclass.initEvents.call(this);
40872         if(this.validationEvent == 'keyup'){
40873             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40874             this.el.on('keyup', this.filterValidation, this);
40875         }
40876         else if(this.validationEvent !== false){
40877             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40878         }
40879         
40880         if(this.selectOnFocus){
40881             this.on("focus", this.preFocus, this);
40882         }
40883         if (!this.allowLeadingSpace) {
40884             this.on('blur', this.cleanLeadingSpace, this);
40885         }
40886         
40887         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40888             this.el.on("keypress", this.filterKeys, this);
40889         }
40890         if(this.grow){
40891             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40892             this.el.on("click", this.autoSize,  this);
40893         }
40894         if(this.el.is('input[type=password]') && Roo.isSafari){
40895             this.el.on('keydown', this.SafariOnKeyDown, this);
40896         }
40897     },
40898
40899     processValue : function(value){
40900         if(this.stripCharsRe){
40901             var newValue = value.replace(this.stripCharsRe, '');
40902             if(newValue !== value){
40903                 this.setRawValue(newValue);
40904                 return newValue;
40905             }
40906         }
40907         return value;
40908     },
40909
40910     filterValidation : function(e){
40911         if(!e.isNavKeyPress()){
40912             this.validationTask.delay(this.validationDelay);
40913         }
40914     },
40915
40916     // private
40917     onKeyUp : function(e){
40918         if(!e.isNavKeyPress()){
40919             this.autoSize();
40920         }
40921     },
40922     // private - clean the leading white space
40923     cleanLeadingSpace : function(e)
40924     {
40925         if ( this.inputType == 'file') {
40926             return;
40927         }
40928         
40929         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40930     },
40931     /**
40932      * Resets the current field value to the originally-loaded value and clears any validation messages.
40933      *  
40934      */
40935     reset : function(){
40936         Roo.form.TextField.superclass.reset.call(this);
40937        
40938     }, 
40939     // private
40940     preFocus : function(){
40941         
40942         if(this.selectOnFocus){
40943             this.el.dom.select();
40944         }
40945     },
40946
40947     
40948     // private
40949     filterKeys : function(e){
40950         var k = e.getKey();
40951         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40952             return;
40953         }
40954         var c = e.getCharCode(), cc = String.fromCharCode(c);
40955         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40956             return;
40957         }
40958         if(!this.maskRe.test(cc)){
40959             e.stopEvent();
40960         }
40961     },
40962
40963     setValue : function(v){
40964         
40965         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40966         
40967         this.autoSize();
40968     },
40969
40970     /**
40971      * Validates a value according to the field's validation rules and marks the field as invalid
40972      * if the validation fails
40973      * @param {Mixed} value The value to validate
40974      * @return {Boolean} True if the value is valid, else false
40975      */
40976     validateValue : function(value){
40977         if(value.length < 1)  { // if it's blank
40978              if(this.allowBlank){
40979                 this.clearInvalid();
40980                 return true;
40981              }else{
40982                 this.markInvalid(this.blankText);
40983                 return false;
40984              }
40985         }
40986         if(value.length < this.minLength){
40987             this.markInvalid(String.format(this.minLengthText, this.minLength));
40988             return false;
40989         }
40990         if(value.length > this.maxLength){
40991             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40992             return false;
40993         }
40994         if(this.vtype){
40995             var vt = Roo.form.VTypes;
40996             if(!vt[this.vtype](value, this)){
40997                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40998                 return false;
40999             }
41000         }
41001         if(typeof this.validator == "function"){
41002             var msg = this.validator(value);
41003             if(msg !== true){
41004                 this.markInvalid(msg);
41005                 return false;
41006             }
41007         }
41008         if(this.regex && !this.regex.test(value)){
41009             this.markInvalid(this.regexText);
41010             return false;
41011         }
41012         return true;
41013     },
41014
41015     /**
41016      * Selects text in this field
41017      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41018      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41019      */
41020     selectText : function(start, end){
41021         var v = this.getRawValue();
41022         if(v.length > 0){
41023             start = start === undefined ? 0 : start;
41024             end = end === undefined ? v.length : end;
41025             var d = this.el.dom;
41026             if(d.setSelectionRange){
41027                 d.setSelectionRange(start, end);
41028             }else if(d.createTextRange){
41029                 var range = d.createTextRange();
41030                 range.moveStart("character", start);
41031                 range.moveEnd("character", v.length-end);
41032                 range.select();
41033             }
41034         }
41035     },
41036
41037     /**
41038      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41039      * This only takes effect if grow = true, and fires the autosize event.
41040      */
41041     autoSize : function(){
41042         if(!this.grow || !this.rendered){
41043             return;
41044         }
41045         if(!this.metrics){
41046             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41047         }
41048         var el = this.el;
41049         var v = el.dom.value;
41050         var d = document.createElement('div');
41051         d.appendChild(document.createTextNode(v));
41052         v = d.innerHTML;
41053         d = null;
41054         v += "&#160;";
41055         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41056         this.el.setWidth(w);
41057         this.fireEvent("autosize", this, w);
41058     },
41059     
41060     // private
41061     SafariOnKeyDown : function(event)
41062     {
41063         // this is a workaround for a password hang bug on chrome/ webkit.
41064         
41065         var isSelectAll = false;
41066         
41067         if(this.el.dom.selectionEnd > 0){
41068             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41069         }
41070         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41071             event.preventDefault();
41072             this.setValue('');
41073             return;
41074         }
41075         
41076         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41077             
41078             event.preventDefault();
41079             // this is very hacky as keydown always get's upper case.
41080             
41081             var cc = String.fromCharCode(event.getCharCode());
41082             
41083             
41084             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41085             
41086         }
41087         
41088         
41089     }
41090 });/*
41091  * Based on:
41092  * Ext JS Library 1.1.1
41093  * Copyright(c) 2006-2007, Ext JS, LLC.
41094  *
41095  * Originally Released Under LGPL - original licence link has changed is not relivant.
41096  *
41097  * Fork - LGPL
41098  * <script type="text/javascript">
41099  */
41100  
41101 /**
41102  * @class Roo.form.Hidden
41103  * @extends Roo.form.TextField
41104  * Simple Hidden element used on forms 
41105  * 
41106  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41107  * 
41108  * @constructor
41109  * Creates a new Hidden form element.
41110  * @param {Object} config Configuration options
41111  */
41112
41113
41114
41115 // easy hidden field...
41116 Roo.form.Hidden = function(config){
41117     Roo.form.Hidden.superclass.constructor.call(this, config);
41118 };
41119   
41120 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41121     fieldLabel:      '',
41122     inputType:      'hidden',
41123     width:          50,
41124     allowBlank:     true,
41125     labelSeparator: '',
41126     hidden:         true,
41127     itemCls :       'x-form-item-display-none'
41128
41129
41130 });
41131
41132
41133 /*
41134  * Based on:
41135  * Ext JS Library 1.1.1
41136  * Copyright(c) 2006-2007, Ext JS, LLC.
41137  *
41138  * Originally Released Under LGPL - original licence link has changed is not relivant.
41139  *
41140  * Fork - LGPL
41141  * <script type="text/javascript">
41142  */
41143  
41144 /**
41145  * @class Roo.form.TriggerField
41146  * @extends Roo.form.TextField
41147  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41148  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41149  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41150  * for which you can provide a custom implementation.  For example:
41151  * <pre><code>
41152 var trigger = new Roo.form.TriggerField();
41153 trigger.onTriggerClick = myTriggerFn;
41154 trigger.applyTo('my-field');
41155 </code></pre>
41156  *
41157  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41158  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41159  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41160  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41161  * @constructor
41162  * Create a new TriggerField.
41163  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41164  * to the base TextField)
41165  */
41166 Roo.form.TriggerField = function(config){
41167     this.mimicing = false;
41168     Roo.form.TriggerField.superclass.constructor.call(this, config);
41169 };
41170
41171 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41172     /**
41173      * @cfg {String} triggerClass A CSS class to apply to the trigger
41174      */
41175     /**
41176      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41177      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41178      */
41179     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41180     /**
41181      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41182      */
41183     hideTrigger:false,
41184
41185     /** @cfg {Boolean} grow @hide */
41186     /** @cfg {Number} growMin @hide */
41187     /** @cfg {Number} growMax @hide */
41188
41189     /**
41190      * @hide 
41191      * @method
41192      */
41193     autoSize: Roo.emptyFn,
41194     // private
41195     monitorTab : true,
41196     // private
41197     deferHeight : true,
41198
41199     
41200     actionMode : 'wrap',
41201     // private
41202     onResize : function(w, h){
41203         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41204         if(typeof w == 'number'){
41205             var x = w - this.trigger.getWidth();
41206             this.el.setWidth(this.adjustWidth('input', x));
41207             this.trigger.setStyle('left', x+'px');
41208         }
41209     },
41210
41211     // private
41212     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41213
41214     // private
41215     getResizeEl : function(){
41216         return this.wrap;
41217     },
41218
41219     // private
41220     getPositionEl : function(){
41221         return this.wrap;
41222     },
41223
41224     // private
41225     alignErrorIcon : function(){
41226         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41227     },
41228
41229     // private
41230     onRender : function(ct, position){
41231         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41232         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41233         this.trigger = this.wrap.createChild(this.triggerConfig ||
41234                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41235         if(this.hideTrigger){
41236             this.trigger.setDisplayed(false);
41237         }
41238         this.initTrigger();
41239         if(!this.width){
41240             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41241         }
41242     },
41243
41244     // private
41245     initTrigger : function(){
41246         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41247         this.trigger.addClassOnOver('x-form-trigger-over');
41248         this.trigger.addClassOnClick('x-form-trigger-click');
41249     },
41250
41251     // private
41252     onDestroy : function(){
41253         if(this.trigger){
41254             this.trigger.removeAllListeners();
41255             this.trigger.remove();
41256         }
41257         if(this.wrap){
41258             this.wrap.remove();
41259         }
41260         Roo.form.TriggerField.superclass.onDestroy.call(this);
41261     },
41262
41263     // private
41264     onFocus : function(){
41265         Roo.form.TriggerField.superclass.onFocus.call(this);
41266         if(!this.mimicing){
41267             this.wrap.addClass('x-trigger-wrap-focus');
41268             this.mimicing = true;
41269             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41270             if(this.monitorTab){
41271                 this.el.on("keydown", this.checkTab, this);
41272             }
41273         }
41274     },
41275
41276     // private
41277     checkTab : function(e){
41278         if(e.getKey() == e.TAB){
41279             this.triggerBlur();
41280         }
41281     },
41282
41283     // private
41284     onBlur : function(){
41285         // do nothing
41286     },
41287
41288     // private
41289     mimicBlur : function(e, t){
41290         if(!this.wrap.contains(t) && this.validateBlur()){
41291             this.triggerBlur();
41292         }
41293     },
41294
41295     // private
41296     triggerBlur : function(){
41297         this.mimicing = false;
41298         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41299         if(this.monitorTab){
41300             this.el.un("keydown", this.checkTab, this);
41301         }
41302         this.wrap.removeClass('x-trigger-wrap-focus');
41303         Roo.form.TriggerField.superclass.onBlur.call(this);
41304     },
41305
41306     // private
41307     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41308     validateBlur : function(e, t){
41309         return true;
41310     },
41311
41312     // private
41313     onDisable : function(){
41314         Roo.form.TriggerField.superclass.onDisable.call(this);
41315         if(this.wrap){
41316             this.wrap.addClass('x-item-disabled');
41317         }
41318     },
41319
41320     // private
41321     onEnable : function(){
41322         Roo.form.TriggerField.superclass.onEnable.call(this);
41323         if(this.wrap){
41324             this.wrap.removeClass('x-item-disabled');
41325         }
41326     },
41327
41328     // private
41329     onShow : function(){
41330         var ae = this.getActionEl();
41331         
41332         if(ae){
41333             ae.dom.style.display = '';
41334             ae.dom.style.visibility = 'visible';
41335         }
41336     },
41337
41338     // private
41339     
41340     onHide : function(){
41341         var ae = this.getActionEl();
41342         ae.dom.style.display = 'none';
41343     },
41344
41345     /**
41346      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41347      * by an implementing function.
41348      * @method
41349      * @param {EventObject} e
41350      */
41351     onTriggerClick : Roo.emptyFn
41352 });
41353
41354 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41355 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41356 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41357 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41358     initComponent : function(){
41359         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41360
41361         this.triggerConfig = {
41362             tag:'span', cls:'x-form-twin-triggers', cn:[
41363             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41364             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41365         ]};
41366     },
41367
41368     getTrigger : function(index){
41369         return this.triggers[index];
41370     },
41371
41372     initTrigger : function(){
41373         var ts = this.trigger.select('.x-form-trigger', true);
41374         this.wrap.setStyle('overflow', 'hidden');
41375         var triggerField = this;
41376         ts.each(function(t, all, index){
41377             t.hide = function(){
41378                 var w = triggerField.wrap.getWidth();
41379                 this.dom.style.display = 'none';
41380                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41381             };
41382             t.show = function(){
41383                 var w = triggerField.wrap.getWidth();
41384                 this.dom.style.display = '';
41385                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41386             };
41387             var triggerIndex = 'Trigger'+(index+1);
41388
41389             if(this['hide'+triggerIndex]){
41390                 t.dom.style.display = 'none';
41391             }
41392             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41393             t.addClassOnOver('x-form-trigger-over');
41394             t.addClassOnClick('x-form-trigger-click');
41395         }, this);
41396         this.triggers = ts.elements;
41397     },
41398
41399     onTrigger1Click : Roo.emptyFn,
41400     onTrigger2Click : Roo.emptyFn
41401 });/*
41402  * Based on:
41403  * Ext JS Library 1.1.1
41404  * Copyright(c) 2006-2007, Ext JS, LLC.
41405  *
41406  * Originally Released Under LGPL - original licence link has changed is not relivant.
41407  *
41408  * Fork - LGPL
41409  * <script type="text/javascript">
41410  */
41411  
41412 /**
41413  * @class Roo.form.TextArea
41414  * @extends Roo.form.TextField
41415  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41416  * support for auto-sizing.
41417  * @constructor
41418  * Creates a new TextArea
41419  * @param {Object} config Configuration options
41420  */
41421 Roo.form.TextArea = function(config){
41422     Roo.form.TextArea.superclass.constructor.call(this, config);
41423     // these are provided exchanges for backwards compat
41424     // minHeight/maxHeight were replaced by growMin/growMax to be
41425     // compatible with TextField growing config values
41426     if(this.minHeight !== undefined){
41427         this.growMin = this.minHeight;
41428     }
41429     if(this.maxHeight !== undefined){
41430         this.growMax = this.maxHeight;
41431     }
41432 };
41433
41434 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41435     /**
41436      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41437      */
41438     growMin : 60,
41439     /**
41440      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41441      */
41442     growMax: 1000,
41443     /**
41444      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41445      * in the field (equivalent to setting overflow: hidden, defaults to false)
41446      */
41447     preventScrollbars: false,
41448     /**
41449      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41450      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41451      */
41452
41453     // private
41454     onRender : function(ct, position){
41455         if(!this.el){
41456             this.defaultAutoCreate = {
41457                 tag: "textarea",
41458                 style:"width:300px;height:60px;",
41459                 autocomplete: "new-password"
41460             };
41461         }
41462         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41463         if(this.grow){
41464             this.textSizeEl = Roo.DomHelper.append(document.body, {
41465                 tag: "pre", cls: "x-form-grow-sizer"
41466             });
41467             if(this.preventScrollbars){
41468                 this.el.setStyle("overflow", "hidden");
41469             }
41470             this.el.setHeight(this.growMin);
41471         }
41472     },
41473
41474     onDestroy : function(){
41475         if(this.textSizeEl){
41476             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41477         }
41478         Roo.form.TextArea.superclass.onDestroy.call(this);
41479     },
41480
41481     // private
41482     onKeyUp : function(e){
41483         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41484             this.autoSize();
41485         }
41486     },
41487
41488     /**
41489      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41490      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41491      */
41492     autoSize : function(){
41493         if(!this.grow || !this.textSizeEl){
41494             return;
41495         }
41496         var el = this.el;
41497         var v = el.dom.value;
41498         var ts = this.textSizeEl;
41499
41500         ts.innerHTML = '';
41501         ts.appendChild(document.createTextNode(v));
41502         v = ts.innerHTML;
41503
41504         Roo.fly(ts).setWidth(this.el.getWidth());
41505         if(v.length < 1){
41506             v = "&#160;&#160;";
41507         }else{
41508             if(Roo.isIE){
41509                 v = v.replace(/\n/g, '<p>&#160;</p>');
41510             }
41511             v += "&#160;\n&#160;";
41512         }
41513         ts.innerHTML = v;
41514         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41515         if(h != this.lastHeight){
41516             this.lastHeight = h;
41517             this.el.setHeight(h);
41518             this.fireEvent("autosize", this, h);
41519         }
41520     }
41521 });/*
41522  * Based on:
41523  * Ext JS Library 1.1.1
41524  * Copyright(c) 2006-2007, Ext JS, LLC.
41525  *
41526  * Originally Released Under LGPL - original licence link has changed is not relivant.
41527  *
41528  * Fork - LGPL
41529  * <script type="text/javascript">
41530  */
41531  
41532
41533 /**
41534  * @class Roo.form.NumberField
41535  * @extends Roo.form.TextField
41536  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41537  * @constructor
41538  * Creates a new NumberField
41539  * @param {Object} config Configuration options
41540  */
41541 Roo.form.NumberField = function(config){
41542     Roo.form.NumberField.superclass.constructor.call(this, config);
41543 };
41544
41545 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41546     /**
41547      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41548      */
41549     fieldClass: "x-form-field x-form-num-field",
41550     /**
41551      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41552      */
41553     allowDecimals : true,
41554     /**
41555      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41556      */
41557     decimalSeparator : ".",
41558     /**
41559      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41560      */
41561     decimalPrecision : 2,
41562     /**
41563      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41564      */
41565     allowNegative : true,
41566     /**
41567      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41568      */
41569     minValue : Number.NEGATIVE_INFINITY,
41570     /**
41571      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41572      */
41573     maxValue : Number.MAX_VALUE,
41574     /**
41575      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41576      */
41577     minText : "The minimum value for this field is {0}",
41578     /**
41579      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41580      */
41581     maxText : "The maximum value for this field is {0}",
41582     /**
41583      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41584      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41585      */
41586     nanText : "{0} is not a valid number",
41587
41588     // private
41589     initEvents : function(){
41590         Roo.form.NumberField.superclass.initEvents.call(this);
41591         var allowed = "0123456789";
41592         if(this.allowDecimals){
41593             allowed += this.decimalSeparator;
41594         }
41595         if(this.allowNegative){
41596             allowed += "-";
41597         }
41598         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41599         var keyPress = function(e){
41600             var k = e.getKey();
41601             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41602                 return;
41603             }
41604             var c = e.getCharCode();
41605             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41606                 e.stopEvent();
41607             }
41608         };
41609         this.el.on("keypress", keyPress, this);
41610     },
41611
41612     // private
41613     validateValue : function(value){
41614         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41615             return false;
41616         }
41617         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41618              return true;
41619         }
41620         var num = this.parseValue(value);
41621         if(isNaN(num)){
41622             this.markInvalid(String.format(this.nanText, value));
41623             return false;
41624         }
41625         if(num < this.minValue){
41626             this.markInvalid(String.format(this.minText, this.minValue));
41627             return false;
41628         }
41629         if(num > this.maxValue){
41630             this.markInvalid(String.format(this.maxText, this.maxValue));
41631             return false;
41632         }
41633         return true;
41634     },
41635
41636     getValue : function(){
41637         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41638     },
41639
41640     // private
41641     parseValue : function(value){
41642         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41643         return isNaN(value) ? '' : value;
41644     },
41645
41646     // private
41647     fixPrecision : function(value){
41648         var nan = isNaN(value);
41649         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41650             return nan ? '' : value;
41651         }
41652         return parseFloat(value).toFixed(this.decimalPrecision);
41653     },
41654
41655     setValue : function(v){
41656         v = this.fixPrecision(v);
41657         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41658     },
41659
41660     // private
41661     decimalPrecisionFcn : function(v){
41662         return Math.floor(v);
41663     },
41664
41665     beforeBlur : function(){
41666         var v = this.parseValue(this.getRawValue());
41667         if(v){
41668             this.setValue(v);
41669         }
41670     }
41671 });/*
41672  * Based on:
41673  * Ext JS Library 1.1.1
41674  * Copyright(c) 2006-2007, Ext JS, LLC.
41675  *
41676  * Originally Released Under LGPL - original licence link has changed is not relivant.
41677  *
41678  * Fork - LGPL
41679  * <script type="text/javascript">
41680  */
41681  
41682 /**
41683  * @class Roo.form.DateField
41684  * @extends Roo.form.TriggerField
41685  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41686 * @constructor
41687 * Create a new DateField
41688 * @param {Object} config
41689  */
41690 Roo.form.DateField = function(config)
41691 {
41692     Roo.form.DateField.superclass.constructor.call(this, config);
41693     
41694       this.addEvents({
41695          
41696         /**
41697          * @event select
41698          * Fires when a date is selected
41699              * @param {Roo.form.DateField} combo This combo box
41700              * @param {Date} date The date selected
41701              */
41702         'select' : true
41703          
41704     });
41705     
41706     
41707     if(typeof this.minValue == "string") {
41708         this.minValue = this.parseDate(this.minValue);
41709     }
41710     if(typeof this.maxValue == "string") {
41711         this.maxValue = this.parseDate(this.maxValue);
41712     }
41713     this.ddMatch = null;
41714     if(this.disabledDates){
41715         var dd = this.disabledDates;
41716         var re = "(?:";
41717         for(var i = 0; i < dd.length; i++){
41718             re += dd[i];
41719             if(i != dd.length-1) {
41720                 re += "|";
41721             }
41722         }
41723         this.ddMatch = new RegExp(re + ")");
41724     }
41725 };
41726
41727 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41728     /**
41729      * @cfg {String} format
41730      * The default date format string which can be overriden for localization support.  The format must be
41731      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41732      */
41733     format : "m/d/y",
41734     /**
41735      * @cfg {String} altFormats
41736      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41737      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41738      */
41739     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41740     /**
41741      * @cfg {Array} disabledDays
41742      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41743      */
41744     disabledDays : null,
41745     /**
41746      * @cfg {String} disabledDaysText
41747      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41748      */
41749     disabledDaysText : "Disabled",
41750     /**
41751      * @cfg {Array} disabledDates
41752      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41753      * expression so they are very powerful. Some examples:
41754      * <ul>
41755      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41756      * <li>["03/08", "09/16"] would disable those days for every year</li>
41757      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41758      * <li>["03/../2006"] would disable every day in March 2006</li>
41759      * <li>["^03"] would disable every day in every March</li>
41760      * </ul>
41761      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41762      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41763      */
41764     disabledDates : null,
41765     /**
41766      * @cfg {String} disabledDatesText
41767      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41768      */
41769     disabledDatesText : "Disabled",
41770         
41771         
41772         /**
41773      * @cfg {Date/String} zeroValue
41774      * if the date is less that this number, then the field is rendered as empty
41775      * default is 1800
41776      */
41777         zeroValue : '1800-01-01',
41778         
41779         
41780     /**
41781      * @cfg {Date/String} minValue
41782      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41783      * valid format (defaults to null).
41784      */
41785     minValue : null,
41786     /**
41787      * @cfg {Date/String} maxValue
41788      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41789      * valid format (defaults to null).
41790      */
41791     maxValue : null,
41792     /**
41793      * @cfg {String} minText
41794      * The error text to display when the date in the cell is before minValue (defaults to
41795      * 'The date in this field must be after {minValue}').
41796      */
41797     minText : "The date in this field must be equal to or after {0}",
41798     /**
41799      * @cfg {String} maxText
41800      * The error text to display when the date in the cell is after maxValue (defaults to
41801      * 'The date in this field must be before {maxValue}').
41802      */
41803     maxText : "The date in this field must be equal to or before {0}",
41804     /**
41805      * @cfg {String} invalidText
41806      * The error text to display when the date in the field is invalid (defaults to
41807      * '{value} is not a valid date - it must be in the format {format}').
41808      */
41809     invalidText : "{0} is not a valid date - it must be in the format {1}",
41810     /**
41811      * @cfg {String} triggerClass
41812      * An additional CSS class used to style the trigger button.  The trigger will always get the
41813      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41814      * which displays a calendar icon).
41815      */
41816     triggerClass : 'x-form-date-trigger',
41817     
41818
41819     /**
41820      * @cfg {Boolean} useIso
41821      * if enabled, then the date field will use a hidden field to store the 
41822      * real value as iso formated date. default (false)
41823      */ 
41824     useIso : false,
41825     /**
41826      * @cfg {String/Object} autoCreate
41827      * A DomHelper element spec, or true for a default element spec (defaults to
41828      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41829      */ 
41830     // private
41831     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41832     
41833     // private
41834     hiddenField: false,
41835     
41836     onRender : function(ct, position)
41837     {
41838         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41839         if (this.useIso) {
41840             //this.el.dom.removeAttribute('name'); 
41841             Roo.log("Changing name?");
41842             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41843             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41844                     'before', true);
41845             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41846             // prevent input submission
41847             this.hiddenName = this.name;
41848         }
41849             
41850             
41851     },
41852     
41853     // private
41854     validateValue : function(value)
41855     {
41856         value = this.formatDate(value);
41857         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41858             Roo.log('super failed');
41859             return false;
41860         }
41861         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41862              return true;
41863         }
41864         var svalue = value;
41865         value = this.parseDate(value);
41866         if(!value){
41867             Roo.log('parse date failed' + svalue);
41868             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41869             return false;
41870         }
41871         var time = value.getTime();
41872         if(this.minValue && time < this.minValue.getTime()){
41873             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41874             return false;
41875         }
41876         if(this.maxValue && time > this.maxValue.getTime()){
41877             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41878             return false;
41879         }
41880         if(this.disabledDays){
41881             var day = value.getDay();
41882             for(var i = 0; i < this.disabledDays.length; i++) {
41883                 if(day === this.disabledDays[i]){
41884                     this.markInvalid(this.disabledDaysText);
41885                     return false;
41886                 }
41887             }
41888         }
41889         var fvalue = this.formatDate(value);
41890         if(this.ddMatch && this.ddMatch.test(fvalue)){
41891             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41892             return false;
41893         }
41894         return true;
41895     },
41896
41897     // private
41898     // Provides logic to override the default TriggerField.validateBlur which just returns true
41899     validateBlur : function(){
41900         return !this.menu || !this.menu.isVisible();
41901     },
41902     
41903     getName: function()
41904     {
41905         // returns hidden if it's set..
41906         if (!this.rendered) {return ''};
41907         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41908         
41909     },
41910
41911     /**
41912      * Returns the current date value of the date field.
41913      * @return {Date} The date value
41914      */
41915     getValue : function(){
41916         
41917         return  this.hiddenField ?
41918                 this.hiddenField.value :
41919                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41920     },
41921
41922     /**
41923      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41924      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41925      * (the default format used is "m/d/y").
41926      * <br />Usage:
41927      * <pre><code>
41928 //All of these calls set the same date value (May 4, 2006)
41929
41930 //Pass a date object:
41931 var dt = new Date('5/4/06');
41932 dateField.setValue(dt);
41933
41934 //Pass a date string (default format):
41935 dateField.setValue('5/4/06');
41936
41937 //Pass a date string (custom format):
41938 dateField.format = 'Y-m-d';
41939 dateField.setValue('2006-5-4');
41940 </code></pre>
41941      * @param {String/Date} date The date or valid date string
41942      */
41943     setValue : function(date){
41944         if (this.hiddenField) {
41945             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41946         }
41947         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41948         // make sure the value field is always stored as a date..
41949         this.value = this.parseDate(date);
41950         
41951         
41952     },
41953
41954     // private
41955     parseDate : function(value){
41956                 
41957                 if (value instanceof Date) {
41958                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41959                                 return  '';
41960                         }
41961                         return value;
41962                 }
41963                 
41964                 
41965         if(!value || value instanceof Date){
41966             return value;
41967         }
41968         var v = Date.parseDate(value, this.format);
41969          if (!v && this.useIso) {
41970             v = Date.parseDate(value, 'Y-m-d');
41971         }
41972         if(!v && this.altFormats){
41973             if(!this.altFormatsArray){
41974                 this.altFormatsArray = this.altFormats.split("|");
41975             }
41976             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41977                 v = Date.parseDate(value, this.altFormatsArray[i]);
41978             }
41979         }
41980                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41981                         v = '';
41982                 }
41983         return v;
41984     },
41985
41986     // private
41987     formatDate : function(date, fmt){
41988         return (!date || !(date instanceof Date)) ?
41989                date : date.dateFormat(fmt || this.format);
41990     },
41991
41992     // private
41993     menuListeners : {
41994         select: function(m, d){
41995             
41996             this.setValue(d);
41997             this.fireEvent('select', this, d);
41998         },
41999         show : function(){ // retain focus styling
42000             this.onFocus();
42001         },
42002         hide : function(){
42003             this.focus.defer(10, this);
42004             var ml = this.menuListeners;
42005             this.menu.un("select", ml.select,  this);
42006             this.menu.un("show", ml.show,  this);
42007             this.menu.un("hide", ml.hide,  this);
42008         }
42009     },
42010
42011     // private
42012     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42013     onTriggerClick : function(){
42014         if(this.disabled){
42015             return;
42016         }
42017         if(this.menu == null){
42018             this.menu = new Roo.menu.DateMenu();
42019         }
42020         Roo.apply(this.menu.picker,  {
42021             showClear: this.allowBlank,
42022             minDate : this.minValue,
42023             maxDate : this.maxValue,
42024             disabledDatesRE : this.ddMatch,
42025             disabledDatesText : this.disabledDatesText,
42026             disabledDays : this.disabledDays,
42027             disabledDaysText : this.disabledDaysText,
42028             format : this.useIso ? 'Y-m-d' : this.format,
42029             minText : String.format(this.minText, this.formatDate(this.minValue)),
42030             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42031         });
42032         this.menu.on(Roo.apply({}, this.menuListeners, {
42033             scope:this
42034         }));
42035         this.menu.picker.setValue(this.getValue() || new Date());
42036         this.menu.show(this.el, "tl-bl?");
42037     },
42038
42039     beforeBlur : function(){
42040         var v = this.parseDate(this.getRawValue());
42041         if(v){
42042             this.setValue(v);
42043         }
42044     },
42045
42046     /*@
42047      * overide
42048      * 
42049      */
42050     isDirty : function() {
42051         if(this.disabled) {
42052             return false;
42053         }
42054         
42055         if(typeof(this.startValue) === 'undefined'){
42056             return false;
42057         }
42058         
42059         return String(this.getValue()) !== String(this.startValue);
42060         
42061     },
42062     // @overide
42063     cleanLeadingSpace : function(e)
42064     {
42065        return;
42066     }
42067     
42068 });/*
42069  * Based on:
42070  * Ext JS Library 1.1.1
42071  * Copyright(c) 2006-2007, Ext JS, LLC.
42072  *
42073  * Originally Released Under LGPL - original licence link has changed is not relivant.
42074  *
42075  * Fork - LGPL
42076  * <script type="text/javascript">
42077  */
42078  
42079 /**
42080  * @class Roo.form.MonthField
42081  * @extends Roo.form.TriggerField
42082  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42083 * @constructor
42084 * Create a new MonthField
42085 * @param {Object} config
42086  */
42087 Roo.form.MonthField = function(config){
42088     
42089     Roo.form.MonthField.superclass.constructor.call(this, config);
42090     
42091       this.addEvents({
42092          
42093         /**
42094          * @event select
42095          * Fires when a date is selected
42096              * @param {Roo.form.MonthFieeld} combo This combo box
42097              * @param {Date} date The date selected
42098              */
42099         'select' : true
42100          
42101     });
42102     
42103     
42104     if(typeof this.minValue == "string") {
42105         this.minValue = this.parseDate(this.minValue);
42106     }
42107     if(typeof this.maxValue == "string") {
42108         this.maxValue = this.parseDate(this.maxValue);
42109     }
42110     this.ddMatch = null;
42111     if(this.disabledDates){
42112         var dd = this.disabledDates;
42113         var re = "(?:";
42114         for(var i = 0; i < dd.length; i++){
42115             re += dd[i];
42116             if(i != dd.length-1) {
42117                 re += "|";
42118             }
42119         }
42120         this.ddMatch = new RegExp(re + ")");
42121     }
42122 };
42123
42124 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42125     /**
42126      * @cfg {String} format
42127      * The default date format string which can be overriden for localization support.  The format must be
42128      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42129      */
42130     format : "M Y",
42131     /**
42132      * @cfg {String} altFormats
42133      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42134      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42135      */
42136     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42137     /**
42138      * @cfg {Array} disabledDays
42139      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42140      */
42141     disabledDays : [0,1,2,3,4,5,6],
42142     /**
42143      * @cfg {String} disabledDaysText
42144      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42145      */
42146     disabledDaysText : "Disabled",
42147     /**
42148      * @cfg {Array} disabledDates
42149      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42150      * expression so they are very powerful. Some examples:
42151      * <ul>
42152      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42153      * <li>["03/08", "09/16"] would disable those days for every year</li>
42154      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42155      * <li>["03/../2006"] would disable every day in March 2006</li>
42156      * <li>["^03"] would disable every day in every March</li>
42157      * </ul>
42158      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42159      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42160      */
42161     disabledDates : null,
42162     /**
42163      * @cfg {String} disabledDatesText
42164      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42165      */
42166     disabledDatesText : "Disabled",
42167     /**
42168      * @cfg {Date/String} minValue
42169      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42170      * valid format (defaults to null).
42171      */
42172     minValue : null,
42173     /**
42174      * @cfg {Date/String} maxValue
42175      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42176      * valid format (defaults to null).
42177      */
42178     maxValue : null,
42179     /**
42180      * @cfg {String} minText
42181      * The error text to display when the date in the cell is before minValue (defaults to
42182      * 'The date in this field must be after {minValue}').
42183      */
42184     minText : "The date in this field must be equal to or after {0}",
42185     /**
42186      * @cfg {String} maxTextf
42187      * The error text to display when the date in the cell is after maxValue (defaults to
42188      * 'The date in this field must be before {maxValue}').
42189      */
42190     maxText : "The date in this field must be equal to or before {0}",
42191     /**
42192      * @cfg {String} invalidText
42193      * The error text to display when the date in the field is invalid (defaults to
42194      * '{value} is not a valid date - it must be in the format {format}').
42195      */
42196     invalidText : "{0} is not a valid date - it must be in the format {1}",
42197     /**
42198      * @cfg {String} triggerClass
42199      * An additional CSS class used to style the trigger button.  The trigger will always get the
42200      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42201      * which displays a calendar icon).
42202      */
42203     triggerClass : 'x-form-date-trigger',
42204     
42205
42206     /**
42207      * @cfg {Boolean} useIso
42208      * if enabled, then the date field will use a hidden field to store the 
42209      * real value as iso formated date. default (true)
42210      */ 
42211     useIso : true,
42212     /**
42213      * @cfg {String/Object} autoCreate
42214      * A DomHelper element spec, or true for a default element spec (defaults to
42215      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42216      */ 
42217     // private
42218     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42219     
42220     // private
42221     hiddenField: false,
42222     
42223     hideMonthPicker : false,
42224     
42225     onRender : function(ct, position)
42226     {
42227         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42228         if (this.useIso) {
42229             this.el.dom.removeAttribute('name'); 
42230             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42231                     'before', true);
42232             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42233             // prevent input submission
42234             this.hiddenName = this.name;
42235         }
42236             
42237             
42238     },
42239     
42240     // private
42241     validateValue : function(value)
42242     {
42243         value = this.formatDate(value);
42244         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42245             return false;
42246         }
42247         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42248              return true;
42249         }
42250         var svalue = value;
42251         value = this.parseDate(value);
42252         if(!value){
42253             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42254             return false;
42255         }
42256         var time = value.getTime();
42257         if(this.minValue && time < this.minValue.getTime()){
42258             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42259             return false;
42260         }
42261         if(this.maxValue && time > this.maxValue.getTime()){
42262             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42263             return false;
42264         }
42265         /*if(this.disabledDays){
42266             var day = value.getDay();
42267             for(var i = 0; i < this.disabledDays.length; i++) {
42268                 if(day === this.disabledDays[i]){
42269                     this.markInvalid(this.disabledDaysText);
42270                     return false;
42271                 }
42272             }
42273         }
42274         */
42275         var fvalue = this.formatDate(value);
42276         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42277             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42278             return false;
42279         }
42280         */
42281         return true;
42282     },
42283
42284     // private
42285     // Provides logic to override the default TriggerField.validateBlur which just returns true
42286     validateBlur : function(){
42287         return !this.menu || !this.menu.isVisible();
42288     },
42289
42290     /**
42291      * Returns the current date value of the date field.
42292      * @return {Date} The date value
42293      */
42294     getValue : function(){
42295         
42296         
42297         
42298         return  this.hiddenField ?
42299                 this.hiddenField.value :
42300                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42301     },
42302
42303     /**
42304      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42305      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42306      * (the default format used is "m/d/y").
42307      * <br />Usage:
42308      * <pre><code>
42309 //All of these calls set the same date value (May 4, 2006)
42310
42311 //Pass a date object:
42312 var dt = new Date('5/4/06');
42313 monthField.setValue(dt);
42314
42315 //Pass a date string (default format):
42316 monthField.setValue('5/4/06');
42317
42318 //Pass a date string (custom format):
42319 monthField.format = 'Y-m-d';
42320 monthField.setValue('2006-5-4');
42321 </code></pre>
42322      * @param {String/Date} date The date or valid date string
42323      */
42324     setValue : function(date){
42325         Roo.log('month setValue' + date);
42326         // can only be first of month..
42327         
42328         var val = this.parseDate(date);
42329         
42330         if (this.hiddenField) {
42331             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42332         }
42333         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42334         this.value = this.parseDate(date);
42335     },
42336
42337     // private
42338     parseDate : function(value){
42339         if(!value || value instanceof Date){
42340             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42341             return value;
42342         }
42343         var v = Date.parseDate(value, this.format);
42344         if (!v && this.useIso) {
42345             v = Date.parseDate(value, 'Y-m-d');
42346         }
42347         if (v) {
42348             // 
42349             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42350         }
42351         
42352         
42353         if(!v && this.altFormats){
42354             if(!this.altFormatsArray){
42355                 this.altFormatsArray = this.altFormats.split("|");
42356             }
42357             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42358                 v = Date.parseDate(value, this.altFormatsArray[i]);
42359             }
42360         }
42361         return v;
42362     },
42363
42364     // private
42365     formatDate : function(date, fmt){
42366         return (!date || !(date instanceof Date)) ?
42367                date : date.dateFormat(fmt || this.format);
42368     },
42369
42370     // private
42371     menuListeners : {
42372         select: function(m, d){
42373             this.setValue(d);
42374             this.fireEvent('select', this, d);
42375         },
42376         show : function(){ // retain focus styling
42377             this.onFocus();
42378         },
42379         hide : function(){
42380             this.focus.defer(10, this);
42381             var ml = this.menuListeners;
42382             this.menu.un("select", ml.select,  this);
42383             this.menu.un("show", ml.show,  this);
42384             this.menu.un("hide", ml.hide,  this);
42385         }
42386     },
42387     // private
42388     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42389     onTriggerClick : function(){
42390         if(this.disabled){
42391             return;
42392         }
42393         if(this.menu == null){
42394             this.menu = new Roo.menu.DateMenu();
42395            
42396         }
42397         
42398         Roo.apply(this.menu.picker,  {
42399             
42400             showClear: this.allowBlank,
42401             minDate : this.minValue,
42402             maxDate : this.maxValue,
42403             disabledDatesRE : this.ddMatch,
42404             disabledDatesText : this.disabledDatesText,
42405             
42406             format : this.useIso ? 'Y-m-d' : this.format,
42407             minText : String.format(this.minText, this.formatDate(this.minValue)),
42408             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42409             
42410         });
42411          this.menu.on(Roo.apply({}, this.menuListeners, {
42412             scope:this
42413         }));
42414        
42415         
42416         var m = this.menu;
42417         var p = m.picker;
42418         
42419         // hide month picker get's called when we called by 'before hide';
42420         
42421         var ignorehide = true;
42422         p.hideMonthPicker  = function(disableAnim){
42423             if (ignorehide) {
42424                 return;
42425             }
42426              if(this.monthPicker){
42427                 Roo.log("hideMonthPicker called");
42428                 if(disableAnim === true){
42429                     this.monthPicker.hide();
42430                 }else{
42431                     this.monthPicker.slideOut('t', {duration:.2});
42432                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42433                     p.fireEvent("select", this, this.value);
42434                     m.hide();
42435                 }
42436             }
42437         }
42438         
42439         Roo.log('picker set value');
42440         Roo.log(this.getValue());
42441         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42442         m.show(this.el, 'tl-bl?');
42443         ignorehide  = false;
42444         // this will trigger hideMonthPicker..
42445         
42446         
42447         // hidden the day picker
42448         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42449         
42450         
42451         
42452       
42453         
42454         p.showMonthPicker.defer(100, p);
42455     
42456         
42457        
42458     },
42459
42460     beforeBlur : function(){
42461         var v = this.parseDate(this.getRawValue());
42462         if(v){
42463             this.setValue(v);
42464         }
42465     }
42466
42467     /** @cfg {Boolean} grow @hide */
42468     /** @cfg {Number} growMin @hide */
42469     /** @cfg {Number} growMax @hide */
42470     /**
42471      * @hide
42472      * @method autoSize
42473      */
42474 });/*
42475  * Based on:
42476  * Ext JS Library 1.1.1
42477  * Copyright(c) 2006-2007, Ext JS, LLC.
42478  *
42479  * Originally Released Under LGPL - original licence link has changed is not relivant.
42480  *
42481  * Fork - LGPL
42482  * <script type="text/javascript">
42483  */
42484  
42485
42486 /**
42487  * @class Roo.form.ComboBox
42488  * @extends Roo.form.TriggerField
42489  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42490  * @constructor
42491  * Create a new ComboBox.
42492  * @param {Object} config Configuration options
42493  */
42494 Roo.form.ComboBox = function(config){
42495     Roo.form.ComboBox.superclass.constructor.call(this, config);
42496     this.addEvents({
42497         /**
42498          * @event expand
42499          * Fires when the dropdown list is expanded
42500              * @param {Roo.form.ComboBox} combo This combo box
42501              */
42502         'expand' : true,
42503         /**
42504          * @event collapse
42505          * Fires when the dropdown list is collapsed
42506              * @param {Roo.form.ComboBox} combo This combo box
42507              */
42508         'collapse' : true,
42509         /**
42510          * @event beforeselect
42511          * Fires before a list item is selected. Return false to cancel the selection.
42512              * @param {Roo.form.ComboBox} combo This combo box
42513              * @param {Roo.data.Record} record The data record returned from the underlying store
42514              * @param {Number} index The index of the selected item in the dropdown list
42515              */
42516         'beforeselect' : true,
42517         /**
42518          * @event select
42519          * Fires when a list item is selected
42520              * @param {Roo.form.ComboBox} combo This combo box
42521              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42522              * @param {Number} index The index of the selected item in the dropdown list
42523              */
42524         'select' : true,
42525         /**
42526          * @event beforequery
42527          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42528          * The event object passed has these properties:
42529              * @param {Roo.form.ComboBox} combo This combo box
42530              * @param {String} query The query
42531              * @param {Boolean} forceAll true to force "all" query
42532              * @param {Boolean} cancel true to cancel the query
42533              * @param {Object} e The query event object
42534              */
42535         'beforequery': true,
42536          /**
42537          * @event add
42538          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42539              * @param {Roo.form.ComboBox} combo This combo box
42540              */
42541         'add' : true,
42542         /**
42543          * @event edit
42544          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42545              * @param {Roo.form.ComboBox} combo This combo box
42546              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42547              */
42548         'edit' : true
42549         
42550         
42551     });
42552     if(this.transform){
42553         this.allowDomMove = false;
42554         var s = Roo.getDom(this.transform);
42555         if(!this.hiddenName){
42556             this.hiddenName = s.name;
42557         }
42558         if(!this.store){
42559             this.mode = 'local';
42560             var d = [], opts = s.options;
42561             for(var i = 0, len = opts.length;i < len; i++){
42562                 var o = opts[i];
42563                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42564                 if(o.selected) {
42565                     this.value = value;
42566                 }
42567                 d.push([value, o.text]);
42568             }
42569             this.store = new Roo.data.SimpleStore({
42570                 'id': 0,
42571                 fields: ['value', 'text'],
42572                 data : d
42573             });
42574             this.valueField = 'value';
42575             this.displayField = 'text';
42576         }
42577         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42578         if(!this.lazyRender){
42579             this.target = true;
42580             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42581             s.parentNode.removeChild(s); // remove it
42582             this.render(this.el.parentNode);
42583         }else{
42584             s.parentNode.removeChild(s); // remove it
42585         }
42586
42587     }
42588     if (this.store) {
42589         this.store = Roo.factory(this.store, Roo.data);
42590     }
42591     
42592     this.selectedIndex = -1;
42593     if(this.mode == 'local'){
42594         if(config.queryDelay === undefined){
42595             this.queryDelay = 10;
42596         }
42597         if(config.minChars === undefined){
42598             this.minChars = 0;
42599         }
42600     }
42601 };
42602
42603 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42604     /**
42605      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42606      */
42607     /**
42608      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42609      * rendering into an Roo.Editor, defaults to false)
42610      */
42611     /**
42612      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42613      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42614      */
42615     /**
42616      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42617      */
42618     /**
42619      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42620      * the dropdown list (defaults to undefined, with no header element)
42621      */
42622
42623      /**
42624      * @cfg {String/Roo.Template} tpl The template to use to render the output
42625      */
42626      
42627     // private
42628     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42629     /**
42630      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42631      */
42632     listWidth: undefined,
42633     /**
42634      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42635      * mode = 'remote' or 'text' if mode = 'local')
42636      */
42637     displayField: undefined,
42638     /**
42639      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42640      * mode = 'remote' or 'value' if mode = 'local'). 
42641      * Note: use of a valueField requires the user make a selection
42642      * in order for a value to be mapped.
42643      */
42644     valueField: undefined,
42645     
42646     
42647     /**
42648      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42649      * field's data value (defaults to the underlying DOM element's name)
42650      */
42651     hiddenName: undefined,
42652     /**
42653      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42654      */
42655     listClass: '',
42656     /**
42657      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42658      */
42659     selectedClass: 'x-combo-selected',
42660     /**
42661      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42662      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42663      * which displays a downward arrow icon).
42664      */
42665     triggerClass : 'x-form-arrow-trigger',
42666     /**
42667      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42668      */
42669     shadow:'sides',
42670     /**
42671      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42672      * anchor positions (defaults to 'tl-bl')
42673      */
42674     listAlign: 'tl-bl?',
42675     /**
42676      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42677      */
42678     maxHeight: 300,
42679     /**
42680      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42681      * query specified by the allQuery config option (defaults to 'query')
42682      */
42683     triggerAction: 'query',
42684     /**
42685      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42686      * (defaults to 4, does not apply if editable = false)
42687      */
42688     minChars : 4,
42689     /**
42690      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42691      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42692      */
42693     typeAhead: false,
42694     /**
42695      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42696      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42697      */
42698     queryDelay: 500,
42699     /**
42700      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42701      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42702      */
42703     pageSize: 0,
42704     /**
42705      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42706      * when editable = true (defaults to false)
42707      */
42708     selectOnFocus:false,
42709     /**
42710      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42711      */
42712     queryParam: 'query',
42713     /**
42714      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42715      * when mode = 'remote' (defaults to 'Loading...')
42716      */
42717     loadingText: 'Loading...',
42718     /**
42719      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42720      */
42721     resizable: false,
42722     /**
42723      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42724      */
42725     handleHeight : 8,
42726     /**
42727      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42728      * traditional select (defaults to true)
42729      */
42730     editable: true,
42731     /**
42732      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42733      */
42734     allQuery: '',
42735     /**
42736      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42737      */
42738     mode: 'remote',
42739     /**
42740      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42741      * listWidth has a higher value)
42742      */
42743     minListWidth : 70,
42744     /**
42745      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42746      * allow the user to set arbitrary text into the field (defaults to false)
42747      */
42748     forceSelection:false,
42749     /**
42750      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42751      * if typeAhead = true (defaults to 250)
42752      */
42753     typeAheadDelay : 250,
42754     /**
42755      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42756      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42757      */
42758     valueNotFoundText : undefined,
42759     /**
42760      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42761      */
42762     blockFocus : false,
42763     
42764     /**
42765      * @cfg {Boolean} disableClear Disable showing of clear button.
42766      */
42767     disableClear : false,
42768     /**
42769      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42770      */
42771     alwaysQuery : false,
42772     
42773     //private
42774     addicon : false,
42775     editicon: false,
42776     
42777     // element that contains real text value.. (when hidden is used..)
42778      
42779     // private
42780     onRender : function(ct, position)
42781     {
42782         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42783         
42784         if(this.hiddenName){
42785             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42786                     'before', true);
42787             this.hiddenField.value =
42788                 this.hiddenValue !== undefined ? this.hiddenValue :
42789                 this.value !== undefined ? this.value : '';
42790
42791             // prevent input submission
42792             this.el.dom.removeAttribute('name');
42793              
42794              
42795         }
42796         
42797         if(Roo.isGecko){
42798             this.el.dom.setAttribute('autocomplete', 'off');
42799         }
42800
42801         var cls = 'x-combo-list';
42802
42803         this.list = new Roo.Layer({
42804             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42805         });
42806
42807         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42808         this.list.setWidth(lw);
42809         this.list.swallowEvent('mousewheel');
42810         this.assetHeight = 0;
42811
42812         if(this.title){
42813             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42814             this.assetHeight += this.header.getHeight();
42815         }
42816
42817         this.innerList = this.list.createChild({cls:cls+'-inner'});
42818         this.innerList.on('mouseover', this.onViewOver, this);
42819         this.innerList.on('mousemove', this.onViewMove, this);
42820         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42821         
42822         if(this.allowBlank && !this.pageSize && !this.disableClear){
42823             this.footer = this.list.createChild({cls:cls+'-ft'});
42824             this.pageTb = new Roo.Toolbar(this.footer);
42825            
42826         }
42827         if(this.pageSize){
42828             this.footer = this.list.createChild({cls:cls+'-ft'});
42829             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42830                     {pageSize: this.pageSize});
42831             
42832         }
42833         
42834         if (this.pageTb && this.allowBlank && !this.disableClear) {
42835             var _this = this;
42836             this.pageTb.add(new Roo.Toolbar.Fill(), {
42837                 cls: 'x-btn-icon x-btn-clear',
42838                 text: '&#160;',
42839                 handler: function()
42840                 {
42841                     _this.collapse();
42842                     _this.clearValue();
42843                     _this.onSelect(false, -1);
42844                 }
42845             });
42846         }
42847         if (this.footer) {
42848             this.assetHeight += this.footer.getHeight();
42849         }
42850         
42851
42852         if(!this.tpl){
42853             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42854         }
42855
42856         this.view = new Roo.View(this.innerList, this.tpl, {
42857             singleSelect:true,
42858             store: this.store,
42859             selectedClass: this.selectedClass
42860         });
42861
42862         this.view.on('click', this.onViewClick, this);
42863
42864         this.store.on('beforeload', this.onBeforeLoad, this);
42865         this.store.on('load', this.onLoad, this);
42866         this.store.on('loadexception', this.onLoadException, this);
42867
42868         if(this.resizable){
42869             this.resizer = new Roo.Resizable(this.list,  {
42870                pinned:true, handles:'se'
42871             });
42872             this.resizer.on('resize', function(r, w, h){
42873                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42874                 this.listWidth = w;
42875                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42876                 this.restrictHeight();
42877             }, this);
42878             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42879         }
42880         if(!this.editable){
42881             this.editable = true;
42882             this.setEditable(false);
42883         }  
42884         
42885         
42886         if (typeof(this.events.add.listeners) != 'undefined') {
42887             
42888             this.addicon = this.wrap.createChild(
42889                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42890        
42891             this.addicon.on('click', function(e) {
42892                 this.fireEvent('add', this);
42893             }, this);
42894         }
42895         if (typeof(this.events.edit.listeners) != 'undefined') {
42896             
42897             this.editicon = this.wrap.createChild(
42898                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42899             if (this.addicon) {
42900                 this.editicon.setStyle('margin-left', '40px');
42901             }
42902             this.editicon.on('click', function(e) {
42903                 
42904                 // we fire even  if inothing is selected..
42905                 this.fireEvent('edit', this, this.lastData );
42906                 
42907             }, this);
42908         }
42909         
42910         
42911         
42912     },
42913
42914     // private
42915     initEvents : function(){
42916         Roo.form.ComboBox.superclass.initEvents.call(this);
42917
42918         this.keyNav = new Roo.KeyNav(this.el, {
42919             "up" : function(e){
42920                 this.inKeyMode = true;
42921                 this.selectPrev();
42922             },
42923
42924             "down" : function(e){
42925                 if(!this.isExpanded()){
42926                     this.onTriggerClick();
42927                 }else{
42928                     this.inKeyMode = true;
42929                     this.selectNext();
42930                 }
42931             },
42932
42933             "enter" : function(e){
42934                 this.onViewClick();
42935                 //return true;
42936             },
42937
42938             "esc" : function(e){
42939                 this.collapse();
42940             },
42941
42942             "tab" : function(e){
42943                 this.onViewClick(false);
42944                 this.fireEvent("specialkey", this, e);
42945                 return true;
42946             },
42947
42948             scope : this,
42949
42950             doRelay : function(foo, bar, hname){
42951                 if(hname == 'down' || this.scope.isExpanded()){
42952                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42953                 }
42954                 return true;
42955             },
42956
42957             forceKeyDown: true
42958         });
42959         this.queryDelay = Math.max(this.queryDelay || 10,
42960                 this.mode == 'local' ? 10 : 250);
42961         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42962         if(this.typeAhead){
42963             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42964         }
42965         if(this.editable !== false){
42966             this.el.on("keyup", this.onKeyUp, this);
42967         }
42968         if(this.forceSelection){
42969             this.on('blur', this.doForce, this);
42970         }
42971     },
42972
42973     onDestroy : function(){
42974         if(this.view){
42975             this.view.setStore(null);
42976             this.view.el.removeAllListeners();
42977             this.view.el.remove();
42978             this.view.purgeListeners();
42979         }
42980         if(this.list){
42981             this.list.destroy();
42982         }
42983         if(this.store){
42984             this.store.un('beforeload', this.onBeforeLoad, this);
42985             this.store.un('load', this.onLoad, this);
42986             this.store.un('loadexception', this.onLoadException, this);
42987         }
42988         Roo.form.ComboBox.superclass.onDestroy.call(this);
42989     },
42990
42991     // private
42992     fireKey : function(e){
42993         if(e.isNavKeyPress() && !this.list.isVisible()){
42994             this.fireEvent("specialkey", this, e);
42995         }
42996     },
42997
42998     // private
42999     onResize: function(w, h){
43000         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43001         
43002         if(typeof w != 'number'){
43003             // we do not handle it!?!?
43004             return;
43005         }
43006         var tw = this.trigger.getWidth();
43007         tw += this.addicon ? this.addicon.getWidth() : 0;
43008         tw += this.editicon ? this.editicon.getWidth() : 0;
43009         var x = w - tw;
43010         this.el.setWidth( this.adjustWidth('input', x));
43011             
43012         this.trigger.setStyle('left', x+'px');
43013         
43014         if(this.list && this.listWidth === undefined){
43015             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43016             this.list.setWidth(lw);
43017             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43018         }
43019         
43020     
43021         
43022     },
43023
43024     /**
43025      * Allow or prevent the user from directly editing the field text.  If false is passed,
43026      * the user will only be able to select from the items defined in the dropdown list.  This method
43027      * is the runtime equivalent of setting the 'editable' config option at config time.
43028      * @param {Boolean} value True to allow the user to directly edit the field text
43029      */
43030     setEditable : function(value){
43031         if(value == this.editable){
43032             return;
43033         }
43034         this.editable = value;
43035         if(!value){
43036             this.el.dom.setAttribute('readOnly', true);
43037             this.el.on('mousedown', this.onTriggerClick,  this);
43038             this.el.addClass('x-combo-noedit');
43039         }else{
43040             this.el.dom.setAttribute('readOnly', false);
43041             this.el.un('mousedown', this.onTriggerClick,  this);
43042             this.el.removeClass('x-combo-noedit');
43043         }
43044     },
43045
43046     // private
43047     onBeforeLoad : function(){
43048         if(!this.hasFocus){
43049             return;
43050         }
43051         this.innerList.update(this.loadingText ?
43052                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43053         this.restrictHeight();
43054         this.selectedIndex = -1;
43055     },
43056
43057     // private
43058     onLoad : function(){
43059         if(!this.hasFocus){
43060             return;
43061         }
43062         if(this.store.getCount() > 0){
43063             this.expand();
43064             this.restrictHeight();
43065             if(this.lastQuery == this.allQuery){
43066                 if(this.editable){
43067                     this.el.dom.select();
43068                 }
43069                 if(!this.selectByValue(this.value, true)){
43070                     this.select(0, true);
43071                 }
43072             }else{
43073                 this.selectNext();
43074                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43075                     this.taTask.delay(this.typeAheadDelay);
43076                 }
43077             }
43078         }else{
43079             this.onEmptyResults();
43080         }
43081         //this.el.focus();
43082     },
43083     // private
43084     onLoadException : function()
43085     {
43086         this.collapse();
43087         Roo.log(this.store.reader.jsonData);
43088         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43089             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43090         }
43091         
43092         
43093     },
43094     // private
43095     onTypeAhead : function(){
43096         if(this.store.getCount() > 0){
43097             var r = this.store.getAt(0);
43098             var newValue = r.data[this.displayField];
43099             var len = newValue.length;
43100             var selStart = this.getRawValue().length;
43101             if(selStart != len){
43102                 this.setRawValue(newValue);
43103                 this.selectText(selStart, newValue.length);
43104             }
43105         }
43106     },
43107
43108     // private
43109     onSelect : function(record, index){
43110         if(this.fireEvent('beforeselect', this, record, index) !== false){
43111             this.setFromData(index > -1 ? record.data : false);
43112             this.collapse();
43113             this.fireEvent('select', this, record, index);
43114         }
43115     },
43116
43117     /**
43118      * Returns the currently selected field value or empty string if no value is set.
43119      * @return {String} value The selected value
43120      */
43121     getValue : function(){
43122         if(this.valueField){
43123             return typeof this.value != 'undefined' ? this.value : '';
43124         }
43125         return Roo.form.ComboBox.superclass.getValue.call(this);
43126     },
43127
43128     /**
43129      * Clears any text/value currently set in the field
43130      */
43131     clearValue : function(){
43132         if(this.hiddenField){
43133             this.hiddenField.value = '';
43134         }
43135         this.value = '';
43136         this.setRawValue('');
43137         this.lastSelectionText = '';
43138         
43139     },
43140
43141     /**
43142      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43143      * will be displayed in the field.  If the value does not match the data value of an existing item,
43144      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43145      * Otherwise the field will be blank (although the value will still be set).
43146      * @param {String} value The value to match
43147      */
43148     setValue : function(v){
43149         var text = v;
43150         if(this.valueField){
43151             var r = this.findRecord(this.valueField, v);
43152             if(r){
43153                 text = r.data[this.displayField];
43154             }else if(this.valueNotFoundText !== undefined){
43155                 text = this.valueNotFoundText;
43156             }
43157         }
43158         this.lastSelectionText = text;
43159         if(this.hiddenField){
43160             this.hiddenField.value = v;
43161         }
43162         Roo.form.ComboBox.superclass.setValue.call(this, text);
43163         this.value = v;
43164     },
43165     /**
43166      * @property {Object} the last set data for the element
43167      */
43168     
43169     lastData : false,
43170     /**
43171      * Sets the value of the field based on a object which is related to the record format for the store.
43172      * @param {Object} value the value to set as. or false on reset?
43173      */
43174     setFromData : function(o){
43175         var dv = ''; // display value
43176         var vv = ''; // value value..
43177         this.lastData = o;
43178         if (this.displayField) {
43179             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43180         } else {
43181             // this is an error condition!!!
43182             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43183         }
43184         
43185         if(this.valueField){
43186             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43187         }
43188         if(this.hiddenField){
43189             this.hiddenField.value = vv;
43190             
43191             this.lastSelectionText = dv;
43192             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43193             this.value = vv;
43194             return;
43195         }
43196         // no hidden field.. - we store the value in 'value', but still display
43197         // display field!!!!
43198         this.lastSelectionText = dv;
43199         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43200         this.value = vv;
43201         
43202         
43203     },
43204     // private
43205     reset : function(){
43206         // overridden so that last data is reset..
43207         this.setValue(this.resetValue);
43208         this.originalValue = this.getValue();
43209         this.clearInvalid();
43210         this.lastData = false;
43211         if (this.view) {
43212             this.view.clearSelections();
43213         }
43214     },
43215     // private
43216     findRecord : function(prop, value){
43217         var record;
43218         if(this.store.getCount() > 0){
43219             this.store.each(function(r){
43220                 if(r.data[prop] == value){
43221                     record = r;
43222                     return false;
43223                 }
43224                 return true;
43225             });
43226         }
43227         return record;
43228     },
43229     
43230     getName: function()
43231     {
43232         // returns hidden if it's set..
43233         if (!this.rendered) {return ''};
43234         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43235         
43236     },
43237     // private
43238     onViewMove : function(e, t){
43239         this.inKeyMode = false;
43240     },
43241
43242     // private
43243     onViewOver : function(e, t){
43244         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43245             return;
43246         }
43247         var item = this.view.findItemFromChild(t);
43248         if(item){
43249             var index = this.view.indexOf(item);
43250             this.select(index, false);
43251         }
43252     },
43253
43254     // private
43255     onViewClick : function(doFocus)
43256     {
43257         var index = this.view.getSelectedIndexes()[0];
43258         var r = this.store.getAt(index);
43259         if(r){
43260             this.onSelect(r, index);
43261         }
43262         if(doFocus !== false && !this.blockFocus){
43263             this.el.focus();
43264         }
43265     },
43266
43267     // private
43268     restrictHeight : function(){
43269         this.innerList.dom.style.height = '';
43270         var inner = this.innerList.dom;
43271         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43272         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43273         this.list.beginUpdate();
43274         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43275         this.list.alignTo(this.el, this.listAlign);
43276         this.list.endUpdate();
43277     },
43278
43279     // private
43280     onEmptyResults : function(){
43281         this.collapse();
43282     },
43283
43284     /**
43285      * Returns true if the dropdown list is expanded, else false.
43286      */
43287     isExpanded : function(){
43288         return this.list.isVisible();
43289     },
43290
43291     /**
43292      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43293      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43294      * @param {String} value The data value of the item to select
43295      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43296      * selected item if it is not currently in view (defaults to true)
43297      * @return {Boolean} True if the value matched an item in the list, else false
43298      */
43299     selectByValue : function(v, scrollIntoView){
43300         if(v !== undefined && v !== null){
43301             var r = this.findRecord(this.valueField || this.displayField, v);
43302             if(r){
43303                 this.select(this.store.indexOf(r), scrollIntoView);
43304                 return true;
43305             }
43306         }
43307         return false;
43308     },
43309
43310     /**
43311      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43312      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43313      * @param {Number} index The zero-based index of the list item to select
43314      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43315      * selected item if it is not currently in view (defaults to true)
43316      */
43317     select : function(index, scrollIntoView){
43318         this.selectedIndex = index;
43319         this.view.select(index);
43320         if(scrollIntoView !== false){
43321             var el = this.view.getNode(index);
43322             if(el){
43323                 this.innerList.scrollChildIntoView(el, false);
43324             }
43325         }
43326     },
43327
43328     // private
43329     selectNext : function(){
43330         var ct = this.store.getCount();
43331         if(ct > 0){
43332             if(this.selectedIndex == -1){
43333                 this.select(0);
43334             }else if(this.selectedIndex < ct-1){
43335                 this.select(this.selectedIndex+1);
43336             }
43337         }
43338     },
43339
43340     // private
43341     selectPrev : function(){
43342         var ct = this.store.getCount();
43343         if(ct > 0){
43344             if(this.selectedIndex == -1){
43345                 this.select(0);
43346             }else if(this.selectedIndex != 0){
43347                 this.select(this.selectedIndex-1);
43348             }
43349         }
43350     },
43351
43352     // private
43353     onKeyUp : function(e){
43354         if(this.editable !== false && !e.isSpecialKey()){
43355             this.lastKey = e.getKey();
43356             this.dqTask.delay(this.queryDelay);
43357         }
43358     },
43359
43360     // private
43361     validateBlur : function(){
43362         return !this.list || !this.list.isVisible();   
43363     },
43364
43365     // private
43366     initQuery : function(){
43367         this.doQuery(this.getRawValue());
43368     },
43369
43370     // private
43371     doForce : function(){
43372         if(this.el.dom.value.length > 0){
43373             this.el.dom.value =
43374                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43375              
43376         }
43377     },
43378
43379     /**
43380      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43381      * query allowing the query action to be canceled if needed.
43382      * @param {String} query The SQL query to execute
43383      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43384      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43385      * saved in the current store (defaults to false)
43386      */
43387     doQuery : function(q, forceAll){
43388         if(q === undefined || q === null){
43389             q = '';
43390         }
43391         var qe = {
43392             query: q,
43393             forceAll: forceAll,
43394             combo: this,
43395             cancel:false
43396         };
43397         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43398             return false;
43399         }
43400         q = qe.query;
43401         forceAll = qe.forceAll;
43402         if(forceAll === true || (q.length >= this.minChars)){
43403             if(this.lastQuery != q || this.alwaysQuery){
43404                 this.lastQuery = q;
43405                 if(this.mode == 'local'){
43406                     this.selectedIndex = -1;
43407                     if(forceAll){
43408                         this.store.clearFilter();
43409                     }else{
43410                         this.store.filter(this.displayField, q);
43411                     }
43412                     this.onLoad();
43413                 }else{
43414                     this.store.baseParams[this.queryParam] = q;
43415                     this.store.load({
43416                         params: this.getParams(q)
43417                     });
43418                     this.expand();
43419                 }
43420             }else{
43421                 this.selectedIndex = -1;
43422                 this.onLoad();   
43423             }
43424         }
43425     },
43426
43427     // private
43428     getParams : function(q){
43429         var p = {};
43430         //p[this.queryParam] = q;
43431         if(this.pageSize){
43432             p.start = 0;
43433             p.limit = this.pageSize;
43434         }
43435         return p;
43436     },
43437
43438     /**
43439      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43440      */
43441     collapse : function(){
43442         if(!this.isExpanded()){
43443             return;
43444         }
43445         this.list.hide();
43446         Roo.get(document).un('mousedown', this.collapseIf, this);
43447         Roo.get(document).un('mousewheel', this.collapseIf, this);
43448         if (!this.editable) {
43449             Roo.get(document).un('keydown', this.listKeyPress, this);
43450         }
43451         this.fireEvent('collapse', this);
43452     },
43453
43454     // private
43455     collapseIf : function(e){
43456         if(!e.within(this.wrap) && !e.within(this.list)){
43457             this.collapse();
43458         }
43459     },
43460
43461     /**
43462      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43463      */
43464     expand : function(){
43465         if(this.isExpanded() || !this.hasFocus){
43466             return;
43467         }
43468         this.list.alignTo(this.el, this.listAlign);
43469         this.list.show();
43470         Roo.get(document).on('mousedown', this.collapseIf, this);
43471         Roo.get(document).on('mousewheel', this.collapseIf, this);
43472         if (!this.editable) {
43473             Roo.get(document).on('keydown', this.listKeyPress, this);
43474         }
43475         
43476         this.fireEvent('expand', this);
43477     },
43478
43479     // private
43480     // Implements the default empty TriggerField.onTriggerClick function
43481     onTriggerClick : function(){
43482         if(this.disabled){
43483             return;
43484         }
43485         if(this.isExpanded()){
43486             this.collapse();
43487             if (!this.blockFocus) {
43488                 this.el.focus();
43489             }
43490             
43491         }else {
43492             this.hasFocus = true;
43493             if(this.triggerAction == 'all') {
43494                 this.doQuery(this.allQuery, true);
43495             } else {
43496                 this.doQuery(this.getRawValue());
43497             }
43498             if (!this.blockFocus) {
43499                 this.el.focus();
43500             }
43501         }
43502     },
43503     listKeyPress : function(e)
43504     {
43505         //Roo.log('listkeypress');
43506         // scroll to first matching element based on key pres..
43507         if (e.isSpecialKey()) {
43508             return false;
43509         }
43510         var k = String.fromCharCode(e.getKey()).toUpperCase();
43511         //Roo.log(k);
43512         var match  = false;
43513         var csel = this.view.getSelectedNodes();
43514         var cselitem = false;
43515         if (csel.length) {
43516             var ix = this.view.indexOf(csel[0]);
43517             cselitem  = this.store.getAt(ix);
43518             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43519                 cselitem = false;
43520             }
43521             
43522         }
43523         
43524         this.store.each(function(v) { 
43525             if (cselitem) {
43526                 // start at existing selection.
43527                 if (cselitem.id == v.id) {
43528                     cselitem = false;
43529                 }
43530                 return;
43531             }
43532                 
43533             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43534                 match = this.store.indexOf(v);
43535                 return false;
43536             }
43537         }, this);
43538         
43539         if (match === false) {
43540             return true; // no more action?
43541         }
43542         // scroll to?
43543         this.view.select(match);
43544         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43545         sn.scrollIntoView(sn.dom.parentNode, false);
43546     } 
43547
43548     /** 
43549     * @cfg {Boolean} grow 
43550     * @hide 
43551     */
43552     /** 
43553     * @cfg {Number} growMin 
43554     * @hide 
43555     */
43556     /** 
43557     * @cfg {Number} growMax 
43558     * @hide 
43559     */
43560     /**
43561      * @hide
43562      * @method autoSize
43563      */
43564 });/*
43565  * Copyright(c) 2010-2012, Roo J Solutions Limited
43566  *
43567  * Licence LGPL
43568  *
43569  */
43570
43571 /**
43572  * @class Roo.form.ComboBoxArray
43573  * @extends Roo.form.TextField
43574  * A facebook style adder... for lists of email / people / countries  etc...
43575  * pick multiple items from a combo box, and shows each one.
43576  *
43577  *  Fred [x]  Brian [x]  [Pick another |v]
43578  *
43579  *
43580  *  For this to work: it needs various extra information
43581  *    - normal combo problay has
43582  *      name, hiddenName
43583  *    + displayField, valueField
43584  *
43585  *    For our purpose...
43586  *
43587  *
43588  *   If we change from 'extends' to wrapping...
43589  *   
43590  *  
43591  *
43592  
43593  
43594  * @constructor
43595  * Create a new ComboBoxArray.
43596  * @param {Object} config Configuration options
43597  */
43598  
43599
43600 Roo.form.ComboBoxArray = function(config)
43601 {
43602     this.addEvents({
43603         /**
43604          * @event beforeremove
43605          * Fires before remove the value from the list
43606              * @param {Roo.form.ComboBoxArray} _self This combo box array
43607              * @param {Roo.form.ComboBoxArray.Item} item removed item
43608              */
43609         'beforeremove' : true,
43610         /**
43611          * @event remove
43612          * Fires when remove the value from the list
43613              * @param {Roo.form.ComboBoxArray} _self This combo box array
43614              * @param {Roo.form.ComboBoxArray.Item} item removed item
43615              */
43616         'remove' : true
43617         
43618         
43619     });
43620     
43621     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43622     
43623     this.items = new Roo.util.MixedCollection(false);
43624     
43625     // construct the child combo...
43626     
43627     
43628     
43629     
43630    
43631     
43632 }
43633
43634  
43635 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43636
43637     /**
43638      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43639      */
43640     
43641     lastData : false,
43642     
43643     // behavies liek a hiddne field
43644     inputType:      'hidden',
43645     /**
43646      * @cfg {Number} width The width of the box that displays the selected element
43647      */ 
43648     width:          300,
43649
43650     
43651     
43652     /**
43653      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43654      */
43655     name : false,
43656     /**
43657      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43658      */
43659     hiddenName : false,
43660       /**
43661      * @cfg {String} seperator    The value seperator normally ',' 
43662      */
43663     seperator : ',',
43664     
43665     // private the array of items that are displayed..
43666     items  : false,
43667     // private - the hidden field el.
43668     hiddenEl : false,
43669     // private - the filed el..
43670     el : false,
43671     
43672     //validateValue : function() { return true; }, // all values are ok!
43673     //onAddClick: function() { },
43674     
43675     onRender : function(ct, position) 
43676     {
43677         
43678         // create the standard hidden element
43679         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43680         
43681         
43682         // give fake names to child combo;
43683         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43684         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43685         
43686         this.combo = Roo.factory(this.combo, Roo.form);
43687         this.combo.onRender(ct, position);
43688         if (typeof(this.combo.width) != 'undefined') {
43689             this.combo.onResize(this.combo.width,0);
43690         }
43691         
43692         this.combo.initEvents();
43693         
43694         // assigned so form know we need to do this..
43695         this.store          = this.combo.store;
43696         this.valueField     = this.combo.valueField;
43697         this.displayField   = this.combo.displayField ;
43698         
43699         
43700         this.combo.wrap.addClass('x-cbarray-grp');
43701         
43702         var cbwrap = this.combo.wrap.createChild(
43703             {tag: 'div', cls: 'x-cbarray-cb'},
43704             this.combo.el.dom
43705         );
43706         
43707              
43708         this.hiddenEl = this.combo.wrap.createChild({
43709             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43710         });
43711         this.el = this.combo.wrap.createChild({
43712             tag: 'input',  type:'hidden' , name: this.name, value : ''
43713         });
43714          //   this.el.dom.removeAttribute("name");
43715         
43716         
43717         this.outerWrap = this.combo.wrap;
43718         this.wrap = cbwrap;
43719         
43720         this.outerWrap.setWidth(this.width);
43721         this.outerWrap.dom.removeChild(this.el.dom);
43722         
43723         this.wrap.dom.appendChild(this.el.dom);
43724         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43725         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43726         
43727         this.combo.trigger.setStyle('position','relative');
43728         this.combo.trigger.setStyle('left', '0px');
43729         this.combo.trigger.setStyle('top', '2px');
43730         
43731         this.combo.el.setStyle('vertical-align', 'text-bottom');
43732         
43733         //this.trigger.setStyle('vertical-align', 'top');
43734         
43735         // this should use the code from combo really... on('add' ....)
43736         if (this.adder) {
43737             
43738         
43739             this.adder = this.outerWrap.createChild(
43740                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43741             var _t = this;
43742             this.adder.on('click', function(e) {
43743                 _t.fireEvent('adderclick', this, e);
43744             }, _t);
43745         }
43746         //var _t = this;
43747         //this.adder.on('click', this.onAddClick, _t);
43748         
43749         
43750         this.combo.on('select', function(cb, rec, ix) {
43751             this.addItem(rec.data);
43752             
43753             cb.setValue('');
43754             cb.el.dom.value = '';
43755             //cb.lastData = rec.data;
43756             // add to list
43757             
43758         }, this);
43759         
43760         
43761     },
43762     
43763     
43764     getName: function()
43765     {
43766         // returns hidden if it's set..
43767         if (!this.rendered) {return ''};
43768         return  this.hiddenName ? this.hiddenName : this.name;
43769         
43770     },
43771     
43772     
43773     onResize: function(w, h){
43774         
43775         return;
43776         // not sure if this is needed..
43777         //this.combo.onResize(w,h);
43778         
43779         if(typeof w != 'number'){
43780             // we do not handle it!?!?
43781             return;
43782         }
43783         var tw = this.combo.trigger.getWidth();
43784         tw += this.addicon ? this.addicon.getWidth() : 0;
43785         tw += this.editicon ? this.editicon.getWidth() : 0;
43786         var x = w - tw;
43787         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43788             
43789         this.combo.trigger.setStyle('left', '0px');
43790         
43791         if(this.list && this.listWidth === undefined){
43792             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43793             this.list.setWidth(lw);
43794             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43795         }
43796         
43797     
43798         
43799     },
43800     
43801     addItem: function(rec)
43802     {
43803         var valueField = this.combo.valueField;
43804         var displayField = this.combo.displayField;
43805         
43806         if (this.items.indexOfKey(rec[valueField]) > -1) {
43807             //console.log("GOT " + rec.data.id);
43808             return;
43809         }
43810         
43811         var x = new Roo.form.ComboBoxArray.Item({
43812             //id : rec[this.idField],
43813             data : rec,
43814             displayField : displayField ,
43815             tipField : displayField ,
43816             cb : this
43817         });
43818         // use the 
43819         this.items.add(rec[valueField],x);
43820         // add it before the element..
43821         this.updateHiddenEl();
43822         x.render(this.outerWrap, this.wrap.dom);
43823         // add the image handler..
43824     },
43825     
43826     updateHiddenEl : function()
43827     {
43828         this.validate();
43829         if (!this.hiddenEl) {
43830             return;
43831         }
43832         var ar = [];
43833         var idField = this.combo.valueField;
43834         
43835         this.items.each(function(f) {
43836             ar.push(f.data[idField]);
43837         });
43838         this.hiddenEl.dom.value = ar.join(this.seperator);
43839         this.validate();
43840     },
43841     
43842     reset : function()
43843     {
43844         this.items.clear();
43845         
43846         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43847            el.remove();
43848         });
43849         
43850         this.el.dom.value = '';
43851         if (this.hiddenEl) {
43852             this.hiddenEl.dom.value = '';
43853         }
43854         
43855     },
43856     getValue: function()
43857     {
43858         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43859     },
43860     setValue: function(v) // not a valid action - must use addItems..
43861     {
43862         
43863         this.reset();
43864          
43865         if (this.store.isLocal && (typeof(v) == 'string')) {
43866             // then we can use the store to find the values..
43867             // comma seperated at present.. this needs to allow JSON based encoding..
43868             this.hiddenEl.value  = v;
43869             var v_ar = [];
43870             Roo.each(v.split(this.seperator), function(k) {
43871                 Roo.log("CHECK " + this.valueField + ',' + k);
43872                 var li = this.store.query(this.valueField, k);
43873                 if (!li.length) {
43874                     return;
43875                 }
43876                 var add = {};
43877                 add[this.valueField] = k;
43878                 add[this.displayField] = li.item(0).data[this.displayField];
43879                 
43880                 this.addItem(add);
43881             }, this) 
43882              
43883         }
43884         if (typeof(v) == 'object' ) {
43885             // then let's assume it's an array of objects..
43886             Roo.each(v, function(l) {
43887                 var add = l;
43888                 if (typeof(l) == 'string') {
43889                     add = {};
43890                     add[this.valueField] = l;
43891                     add[this.displayField] = l
43892                 }
43893                 this.addItem(add);
43894             }, this);
43895              
43896         }
43897         
43898         
43899     },
43900     setFromData: function(v)
43901     {
43902         // this recieves an object, if setValues is called.
43903         this.reset();
43904         this.el.dom.value = v[this.displayField];
43905         this.hiddenEl.dom.value = v[this.valueField];
43906         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43907             return;
43908         }
43909         var kv = v[this.valueField];
43910         var dv = v[this.displayField];
43911         kv = typeof(kv) != 'string' ? '' : kv;
43912         dv = typeof(dv) != 'string' ? '' : dv;
43913         
43914         
43915         var keys = kv.split(this.seperator);
43916         var display = dv.split(this.seperator);
43917         for (var i = 0 ; i < keys.length; i++) {
43918             add = {};
43919             add[this.valueField] = keys[i];
43920             add[this.displayField] = display[i];
43921             this.addItem(add);
43922         }
43923       
43924         
43925     },
43926     
43927     /**
43928      * Validates the combox array value
43929      * @return {Boolean} True if the value is valid, else false
43930      */
43931     validate : function(){
43932         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43933             this.clearInvalid();
43934             return true;
43935         }
43936         return false;
43937     },
43938     
43939     validateValue : function(value){
43940         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43941         
43942     },
43943     
43944     /*@
43945      * overide
43946      * 
43947      */
43948     isDirty : function() {
43949         if(this.disabled) {
43950             return false;
43951         }
43952         
43953         try {
43954             var d = Roo.decode(String(this.originalValue));
43955         } catch (e) {
43956             return String(this.getValue()) !== String(this.originalValue);
43957         }
43958         
43959         var originalValue = [];
43960         
43961         for (var i = 0; i < d.length; i++){
43962             originalValue.push(d[i][this.valueField]);
43963         }
43964         
43965         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43966         
43967     }
43968     
43969 });
43970
43971
43972
43973 /**
43974  * @class Roo.form.ComboBoxArray.Item
43975  * @extends Roo.BoxComponent
43976  * A selected item in the list
43977  *  Fred [x]  Brian [x]  [Pick another |v]
43978  * 
43979  * @constructor
43980  * Create a new item.
43981  * @param {Object} config Configuration options
43982  */
43983  
43984 Roo.form.ComboBoxArray.Item = function(config) {
43985     config.id = Roo.id();
43986     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43987 }
43988
43989 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43990     data : {},
43991     cb: false,
43992     displayField : false,
43993     tipField : false,
43994     
43995     
43996     defaultAutoCreate : {
43997         tag: 'div',
43998         cls: 'x-cbarray-item',
43999         cn : [ 
44000             { tag: 'div' },
44001             {
44002                 tag: 'img',
44003                 width:16,
44004                 height : 16,
44005                 src : Roo.BLANK_IMAGE_URL ,
44006                 align: 'center'
44007             }
44008         ]
44009         
44010     },
44011     
44012  
44013     onRender : function(ct, position)
44014     {
44015         Roo.form.Field.superclass.onRender.call(this, ct, position);
44016         
44017         if(!this.el){
44018             var cfg = this.getAutoCreate();
44019             this.el = ct.createChild(cfg, position);
44020         }
44021         
44022         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44023         
44024         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44025             this.cb.renderer(this.data) :
44026             String.format('{0}',this.data[this.displayField]);
44027         
44028             
44029         this.el.child('div').dom.setAttribute('qtip',
44030                         String.format('{0}',this.data[this.tipField])
44031         );
44032         
44033         this.el.child('img').on('click', this.remove, this);
44034         
44035     },
44036    
44037     remove : function()
44038     {
44039         if(this.cb.disabled){
44040             return;
44041         }
44042         
44043         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44044             this.cb.items.remove(this);
44045             this.el.child('img').un('click', this.remove, this);
44046             this.el.remove();
44047             this.cb.updateHiddenEl();
44048
44049             this.cb.fireEvent('remove', this.cb, this);
44050         }
44051         
44052     }
44053 });/*
44054  * RooJS Library 1.1.1
44055  * Copyright(c) 2008-2011  Alan Knowles
44056  *
44057  * License - LGPL
44058  */
44059  
44060
44061 /**
44062  * @class Roo.form.ComboNested
44063  * @extends Roo.form.ComboBox
44064  * A combobox for that allows selection of nested items in a list,
44065  * eg.
44066  *
44067  *  Book
44068  *    -> red
44069  *    -> green
44070  *  Table
44071  *    -> square
44072  *      ->red
44073  *      ->green
44074  *    -> rectangle
44075  *      ->green
44076  *      
44077  * 
44078  * @constructor
44079  * Create a new ComboNested
44080  * @param {Object} config Configuration options
44081  */
44082 Roo.form.ComboNested = function(config){
44083     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44084     // should verify some data...
44085     // like
44086     // hiddenName = required..
44087     // displayField = required
44088     // valudField == required
44089     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44090     var _t = this;
44091     Roo.each(req, function(e) {
44092         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44093             throw "Roo.form.ComboNested : missing value for: " + e;
44094         }
44095     });
44096      
44097     
44098 };
44099
44100 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44101    
44102     /*
44103      * @config {Number} max Number of columns to show
44104      */
44105     
44106     maxColumns : 3,
44107    
44108     list : null, // the outermost div..
44109     innerLists : null, // the
44110     views : null,
44111     stores : null,
44112     // private
44113     loadingChildren : false,
44114     
44115     onRender : function(ct, position)
44116     {
44117         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44118         
44119         if(this.hiddenName){
44120             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44121                     'before', true);
44122             this.hiddenField.value =
44123                 this.hiddenValue !== undefined ? this.hiddenValue :
44124                 this.value !== undefined ? this.value : '';
44125
44126             // prevent input submission
44127             this.el.dom.removeAttribute('name');
44128              
44129              
44130         }
44131         
44132         if(Roo.isGecko){
44133             this.el.dom.setAttribute('autocomplete', 'off');
44134         }
44135
44136         var cls = 'x-combo-list';
44137
44138         this.list = new Roo.Layer({
44139             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44140         });
44141
44142         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44143         this.list.setWidth(lw);
44144         this.list.swallowEvent('mousewheel');
44145         this.assetHeight = 0;
44146
44147         if(this.title){
44148             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44149             this.assetHeight += this.header.getHeight();
44150         }
44151         this.innerLists = [];
44152         this.views = [];
44153         this.stores = [];
44154         for (var i =0 ; i < this.maxColumns; i++) {
44155             this.onRenderList( cls, i);
44156         }
44157         
44158         // always needs footer, as we are going to have an 'OK' button.
44159         this.footer = this.list.createChild({cls:cls+'-ft'});
44160         this.pageTb = new Roo.Toolbar(this.footer);  
44161         var _this = this;
44162         this.pageTb.add(  {
44163             
44164             text: 'Done',
44165             handler: function()
44166             {
44167                 _this.collapse();
44168             }
44169         });
44170         
44171         if ( this.allowBlank && !this.disableClear) {
44172             
44173             this.pageTb.add(new Roo.Toolbar.Fill(), {
44174                 cls: 'x-btn-icon x-btn-clear',
44175                 text: '&#160;',
44176                 handler: function()
44177                 {
44178                     _this.collapse();
44179                     _this.clearValue();
44180                     _this.onSelect(false, -1);
44181                 }
44182             });
44183         }
44184         if (this.footer) {
44185             this.assetHeight += this.footer.getHeight();
44186         }
44187         
44188     },
44189     onRenderList : function (  cls, i)
44190     {
44191         
44192         var lw = Math.floor(
44193                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44194         );
44195         
44196         this.list.setWidth(lw); // default to '1'
44197
44198         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44199         //il.on('mouseover', this.onViewOver, this, { list:  i });
44200         //il.on('mousemove', this.onViewMove, this, { list:  i });
44201         il.setWidth(lw);
44202         il.setStyle({ 'overflow-x' : 'hidden'});
44203
44204         if(!this.tpl){
44205             this.tpl = new Roo.Template({
44206                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44207                 isEmpty: function (value, allValues) {
44208                     //Roo.log(value);
44209                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44210                     return dl ? 'has-children' : 'no-children'
44211                 }
44212             });
44213         }
44214         
44215         var store  = this.store;
44216         if (i > 0) {
44217             store  = new Roo.data.SimpleStore({
44218                 //fields : this.store.reader.meta.fields,
44219                 reader : this.store.reader,
44220                 data : [ ]
44221             });
44222         }
44223         this.stores[i]  = store;
44224                   
44225         var view = this.views[i] = new Roo.View(
44226             il,
44227             this.tpl,
44228             {
44229                 singleSelect:true,
44230                 store: store,
44231                 selectedClass: this.selectedClass
44232             }
44233         );
44234         view.getEl().setWidth(lw);
44235         view.getEl().setStyle({
44236             position: i < 1 ? 'relative' : 'absolute',
44237             top: 0,
44238             left: (i * lw ) + 'px',
44239             display : i > 0 ? 'none' : 'block'
44240         });
44241         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44242         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44243         //view.on('click', this.onViewClick, this, { list : i });
44244
44245         store.on('beforeload', this.onBeforeLoad, this);
44246         store.on('load',  this.onLoad, this, { list  : i});
44247         store.on('loadexception', this.onLoadException, this);
44248
44249         // hide the other vies..
44250         
44251         
44252         
44253     },
44254       
44255     restrictHeight : function()
44256     {
44257         var mh = 0;
44258         Roo.each(this.innerLists, function(il,i) {
44259             var el = this.views[i].getEl();
44260             el.dom.style.height = '';
44261             var inner = el.dom;
44262             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44263             // only adjust heights on other ones..
44264             mh = Math.max(h, mh);
44265             if (i < 1) {
44266                 
44267                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44268                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44269                
44270             }
44271             
44272             
44273         }, this);
44274         
44275         this.list.beginUpdate();
44276         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44277         this.list.alignTo(this.el, this.listAlign);
44278         this.list.endUpdate();
44279         
44280     },
44281      
44282     
44283     // -- store handlers..
44284     // private
44285     onBeforeLoad : function()
44286     {
44287         if(!this.hasFocus){
44288             return;
44289         }
44290         this.innerLists[0].update(this.loadingText ?
44291                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44292         this.restrictHeight();
44293         this.selectedIndex = -1;
44294     },
44295     // private
44296     onLoad : function(a,b,c,d)
44297     {
44298         if (!this.loadingChildren) {
44299             // then we are loading the top level. - hide the children
44300             for (var i = 1;i < this.views.length; i++) {
44301                 this.views[i].getEl().setStyle({ display : 'none' });
44302             }
44303             var lw = Math.floor(
44304                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44305             );
44306         
44307              this.list.setWidth(lw); // default to '1'
44308
44309             
44310         }
44311         if(!this.hasFocus){
44312             return;
44313         }
44314         
44315         if(this.store.getCount() > 0) {
44316             this.expand();
44317             this.restrictHeight();   
44318         } else {
44319             this.onEmptyResults();
44320         }
44321         
44322         if (!this.loadingChildren) {
44323             this.selectActive();
44324         }
44325         /*
44326         this.stores[1].loadData([]);
44327         this.stores[2].loadData([]);
44328         this.views
44329         */    
44330     
44331         //this.el.focus();
44332     },
44333     
44334     
44335     // private
44336     onLoadException : function()
44337     {
44338         this.collapse();
44339         Roo.log(this.store.reader.jsonData);
44340         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44341             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44342         }
44343         
44344         
44345     },
44346     // no cleaning of leading spaces on blur here.
44347     cleanLeadingSpace : function(e) { },
44348     
44349
44350     onSelectChange : function (view, sels, opts )
44351     {
44352         var ix = view.getSelectedIndexes();
44353          
44354         if (opts.list > this.maxColumns - 2) {
44355             if (view.store.getCount()<  1) {
44356                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44357
44358             } else  {
44359                 if (ix.length) {
44360                     // used to clear ?? but if we are loading unselected 
44361                     this.setFromData(view.store.getAt(ix[0]).data);
44362                 }
44363                 
44364             }
44365             
44366             return;
44367         }
44368         
44369         if (!ix.length) {
44370             // this get's fired when trigger opens..
44371            // this.setFromData({});
44372             var str = this.stores[opts.list+1];
44373             str.data.clear(); // removeall wihtout the fire events..
44374             return;
44375         }
44376         
44377         var rec = view.store.getAt(ix[0]);
44378          
44379         this.setFromData(rec.data);
44380         this.fireEvent('select', this, rec, ix[0]);
44381         
44382         var lw = Math.floor(
44383              (
44384                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44385              ) / this.maxColumns
44386         );
44387         this.loadingChildren = true;
44388         this.stores[opts.list+1].loadDataFromChildren( rec );
44389         this.loadingChildren = false;
44390         var dl = this.stores[opts.list+1]. getTotalCount();
44391         
44392         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44393         
44394         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44395         for (var i = opts.list+2; i < this.views.length;i++) {
44396             this.views[i].getEl().setStyle({ display : 'none' });
44397         }
44398         
44399         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44400         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44401         
44402         if (this.isLoading) {
44403            // this.selectActive(opts.list);
44404         }
44405          
44406     },
44407     
44408     
44409     
44410     
44411     onDoubleClick : function()
44412     {
44413         this.collapse(); //??
44414     },
44415     
44416      
44417     
44418     
44419     
44420     // private
44421     recordToStack : function(store, prop, value, stack)
44422     {
44423         var cstore = new Roo.data.SimpleStore({
44424             //fields : this.store.reader.meta.fields, // we need array reader.. for
44425             reader : this.store.reader,
44426             data : [ ]
44427         });
44428         var _this = this;
44429         var record  = false;
44430         var srec = false;
44431         if(store.getCount() < 1){
44432             return false;
44433         }
44434         store.each(function(r){
44435             if(r.data[prop] == value){
44436                 record = r;
44437             srec = r;
44438                 return false;
44439             }
44440             if (r.data.cn && r.data.cn.length) {
44441                 cstore.loadDataFromChildren( r);
44442                 var cret = _this.recordToStack(cstore, prop, value, stack);
44443                 if (cret !== false) {
44444                     record = cret;
44445                     srec = r;
44446                     return false;
44447                 }
44448             }
44449              
44450             return true;
44451         });
44452         if (record == false) {
44453             return false
44454         }
44455         stack.unshift(srec);
44456         return record;
44457     },
44458     
44459     /*
44460      * find the stack of stores that match our value.
44461      *
44462      * 
44463      */
44464     
44465     selectActive : function ()
44466     {
44467         // if store is not loaded, then we will need to wait for that to happen first.
44468         var stack = [];
44469         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44470         for (var i = 0; i < stack.length; i++ ) {
44471             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44472         }
44473         
44474     }
44475         
44476          
44477     
44478     
44479     
44480     
44481 });/*
44482  * Based on:
44483  * Ext JS Library 1.1.1
44484  * Copyright(c) 2006-2007, Ext JS, LLC.
44485  *
44486  * Originally Released Under LGPL - original licence link has changed is not relivant.
44487  *
44488  * Fork - LGPL
44489  * <script type="text/javascript">
44490  */
44491 /**
44492  * @class Roo.form.Checkbox
44493  * @extends Roo.form.Field
44494  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44495  * @constructor
44496  * Creates a new Checkbox
44497  * @param {Object} config Configuration options
44498  */
44499 Roo.form.Checkbox = function(config){
44500     Roo.form.Checkbox.superclass.constructor.call(this, config);
44501     this.addEvents({
44502         /**
44503          * @event check
44504          * Fires when the checkbox is checked or unchecked.
44505              * @param {Roo.form.Checkbox} this This checkbox
44506              * @param {Boolean} checked The new checked value
44507              */
44508         check : true
44509     });
44510 };
44511
44512 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44513     /**
44514      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44515      */
44516     focusClass : undefined,
44517     /**
44518      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44519      */
44520     fieldClass: "x-form-field",
44521     /**
44522      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44523      */
44524     checked: false,
44525     /**
44526      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44527      * {tag: "input", type: "checkbox", autocomplete: "off"})
44528      */
44529     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44530     /**
44531      * @cfg {String} boxLabel The text that appears beside the checkbox
44532      */
44533     boxLabel : "",
44534     /**
44535      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44536      */  
44537     inputValue : '1',
44538     /**
44539      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44540      */
44541      valueOff: '0', // value when not checked..
44542
44543     actionMode : 'viewEl', 
44544     //
44545     // private
44546     itemCls : 'x-menu-check-item x-form-item',
44547     groupClass : 'x-menu-group-item',
44548     inputType : 'hidden',
44549     
44550     
44551     inSetChecked: false, // check that we are not calling self...
44552     
44553     inputElement: false, // real input element?
44554     basedOn: false, // ????
44555     
44556     isFormField: true, // not sure where this is needed!!!!
44557
44558     onResize : function(){
44559         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44560         if(!this.boxLabel){
44561             this.el.alignTo(this.wrap, 'c-c');
44562         }
44563     },
44564
44565     initEvents : function(){
44566         Roo.form.Checkbox.superclass.initEvents.call(this);
44567         this.el.on("click", this.onClick,  this);
44568         this.el.on("change", this.onClick,  this);
44569     },
44570
44571
44572     getResizeEl : function(){
44573         return this.wrap;
44574     },
44575
44576     getPositionEl : function(){
44577         return this.wrap;
44578     },
44579
44580     // private
44581     onRender : function(ct, position){
44582         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44583         /*
44584         if(this.inputValue !== undefined){
44585             this.el.dom.value = this.inputValue;
44586         }
44587         */
44588         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44589         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44590         var viewEl = this.wrap.createChild({ 
44591             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44592         this.viewEl = viewEl;   
44593         this.wrap.on('click', this.onClick,  this); 
44594         
44595         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44596         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44597         
44598         
44599         
44600         if(this.boxLabel){
44601             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44602         //    viewEl.on('click', this.onClick,  this); 
44603         }
44604         //if(this.checked){
44605             this.setChecked(this.checked);
44606         //}else{
44607             //this.checked = this.el.dom;
44608         //}
44609
44610     },
44611
44612     // private
44613     initValue : Roo.emptyFn,
44614
44615     /**
44616      * Returns the checked state of the checkbox.
44617      * @return {Boolean} True if checked, else false
44618      */
44619     getValue : function(){
44620         if(this.el){
44621             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44622         }
44623         return this.valueOff;
44624         
44625     },
44626
44627         // private
44628     onClick : function(){ 
44629         if (this.disabled) {
44630             return;
44631         }
44632         this.setChecked(!this.checked);
44633
44634         //if(this.el.dom.checked != this.checked){
44635         //    this.setValue(this.el.dom.checked);
44636        // }
44637     },
44638
44639     /**
44640      * Sets the checked state of the checkbox.
44641      * On is always based on a string comparison between inputValue and the param.
44642      * @param {Boolean/String} value - the value to set 
44643      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44644      */
44645     setValue : function(v,suppressEvent){
44646         
44647         
44648         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44649         //if(this.el && this.el.dom){
44650         //    this.el.dom.checked = this.checked;
44651         //    this.el.dom.defaultChecked = this.checked;
44652         //}
44653         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44654         //this.fireEvent("check", this, this.checked);
44655     },
44656     // private..
44657     setChecked : function(state,suppressEvent)
44658     {
44659         if (this.inSetChecked) {
44660             this.checked = state;
44661             return;
44662         }
44663         
44664     
44665         if(this.wrap){
44666             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44667         }
44668         this.checked = state;
44669         if(suppressEvent !== true){
44670             this.fireEvent('check', this, state);
44671         }
44672         this.inSetChecked = true;
44673         this.el.dom.value = state ? this.inputValue : this.valueOff;
44674         this.inSetChecked = false;
44675         
44676     },
44677     // handle setting of hidden value by some other method!!?!?
44678     setFromHidden: function()
44679     {
44680         if(!this.el){
44681             return;
44682         }
44683         //console.log("SET FROM HIDDEN");
44684         //alert('setFrom hidden');
44685         this.setValue(this.el.dom.value);
44686     },
44687     
44688     onDestroy : function()
44689     {
44690         if(this.viewEl){
44691             Roo.get(this.viewEl).remove();
44692         }
44693          
44694         Roo.form.Checkbox.superclass.onDestroy.call(this);
44695     },
44696     
44697     setBoxLabel : function(str)
44698     {
44699         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44700     }
44701
44702 });/*
44703  * Based on:
44704  * Ext JS Library 1.1.1
44705  * Copyright(c) 2006-2007, Ext JS, LLC.
44706  *
44707  * Originally Released Under LGPL - original licence link has changed is not relivant.
44708  *
44709  * Fork - LGPL
44710  * <script type="text/javascript">
44711  */
44712  
44713 /**
44714  * @class Roo.form.Radio
44715  * @extends Roo.form.Checkbox
44716  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44717  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44718  * @constructor
44719  * Creates a new Radio
44720  * @param {Object} config Configuration options
44721  */
44722 Roo.form.Radio = function(){
44723     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44724 };
44725 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44726     inputType: 'radio',
44727
44728     /**
44729      * If this radio is part of a group, it will return the selected value
44730      * @return {String}
44731      */
44732     getGroupValue : function(){
44733         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44734     },
44735     
44736     
44737     onRender : function(ct, position){
44738         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44739         
44740         if(this.inputValue !== undefined){
44741             this.el.dom.value = this.inputValue;
44742         }
44743          
44744         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44745         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44746         //var viewEl = this.wrap.createChild({ 
44747         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44748         //this.viewEl = viewEl;   
44749         //this.wrap.on('click', this.onClick,  this); 
44750         
44751         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44752         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44753         
44754         
44755         
44756         if(this.boxLabel){
44757             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44758         //    viewEl.on('click', this.onClick,  this); 
44759         }
44760          if(this.checked){
44761             this.el.dom.checked =   'checked' ;
44762         }
44763          
44764     } 
44765     
44766     
44767 });Roo.rtf = {}; // namespace
44768 Roo.rtf.Hex = function(hex)
44769 {
44770     this.hexstr = hex;
44771 };
44772 Roo.rtf.Paragraph = function(opts)
44773 {
44774     this.content = []; ///??? is that used?
44775 };Roo.rtf.Span = function(opts)
44776 {
44777     this.value = opts.value;
44778 };
44779
44780 Roo.rtf.Group = function(parent)
44781 {
44782     // we dont want to acutally store parent - it will make debug a nightmare..
44783     this.content = [];
44784     this.cn  = [];
44785      
44786        
44787     
44788 };
44789
44790 Roo.rtf.Group.prototype = {
44791     ignorable : false,
44792     content: false,
44793     cn: false,
44794     addContent : function(node) {
44795         // could set styles...
44796         this.content.push(node);
44797     },
44798     addChild : function(cn)
44799     {
44800         this.cn.push(cn);
44801     },
44802     // only for images really...
44803     toDataURL : function()
44804     {
44805         var mimetype = false;
44806         switch(true) {
44807             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44808                 mimetype = "image/png";
44809                 break;
44810              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44811                 mimetype = "image/jpeg";
44812                 break;
44813             default :
44814                 return 'about:blank'; // ?? error?
44815         }
44816         
44817         
44818         var hexstring = this.content[this.content.length-1].value;
44819         
44820         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44821             return String.fromCharCode(parseInt(a, 16));
44822         }).join(""));
44823     }
44824     
44825 };
44826 // this looks like it's normally the {rtf{ .... }}
44827 Roo.rtf.Document = function()
44828 {
44829     // we dont want to acutally store parent - it will make debug a nightmare..
44830     this.rtlch  = [];
44831     this.content = [];
44832     this.cn = [];
44833     
44834 };
44835 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44836     addChild : function(cn)
44837     {
44838         this.cn.push(cn);
44839         switch(cn.type) {
44840             case 'rtlch': // most content seems to be inside this??
44841             case 'listtext':
44842             case 'shpinst':
44843                 this.rtlch.push(cn);
44844                 return;
44845             default:
44846                 this[cn.type] = cn;
44847         }
44848         
44849     },
44850     
44851     getElementsByType : function(type)
44852     {
44853         var ret =  [];
44854         this._getElementsByType(type, ret, this.cn, 'rtf');
44855         return ret;
44856     },
44857     _getElementsByType : function (type, ret, search_array, path)
44858     {
44859         search_array.forEach(function(n,i) {
44860             if (n.type == type) {
44861                 n.path = path + '/' + n.type + ':' + i;
44862                 ret.push(n);
44863             }
44864             if (n.cn.length > 0) {
44865                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44866             }
44867         },this);
44868     }
44869     
44870 });
44871  
44872 Roo.rtf.Ctrl = function(opts)
44873 {
44874     this.value = opts.value;
44875     this.param = opts.param;
44876 };
44877 /**
44878  *
44879  *
44880  * based on this https://github.com/iarna/rtf-parser
44881  * it's really only designed to extract pict from pasted RTF 
44882  *
44883  * usage:
44884  *
44885  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44886  *  
44887  *
44888  */
44889
44890  
44891
44892
44893
44894 Roo.rtf.Parser = function(text) {
44895     //super({objectMode: true})
44896     this.text = '';
44897     this.parserState = this.parseText;
44898     
44899     // these are for interpeter...
44900     this.doc = {};
44901     ///this.parserState = this.parseTop
44902     this.groupStack = [];
44903     this.hexStore = [];
44904     this.doc = false;
44905     
44906     this.groups = []; // where we put the return.
44907     
44908     for (var ii = 0; ii < text.length; ++ii) {
44909         ++this.cpos;
44910         
44911         if (text[ii] === '\n') {
44912             ++this.row;
44913             this.col = 1;
44914         } else {
44915             ++this.col;
44916         }
44917         this.parserState(text[ii]);
44918     }
44919     
44920     
44921     
44922 };
44923 Roo.rtf.Parser.prototype = {
44924     text : '', // string being parsed..
44925     controlWord : '',
44926     controlWordParam :  '',
44927     hexChar : '',
44928     doc : false,
44929     group: false,
44930     groupStack : false,
44931     hexStore : false,
44932     
44933     
44934     cpos : 0, 
44935     row : 1, // reportin?
44936     col : 1, //
44937
44938      
44939     push : function (el)
44940     {
44941         var m = 'cmd'+ el.type;
44942         if (typeof(this[m]) == 'undefined') {
44943             Roo.log('invalid cmd:' + el.type);
44944             return;
44945         }
44946         this[m](el);
44947         //Roo.log(el);
44948     },
44949     flushHexStore : function()
44950     {
44951         if (this.hexStore.length < 1) {
44952             return;
44953         }
44954         var hexstr = this.hexStore.map(
44955             function(cmd) {
44956                 return cmd.value;
44957         }).join('');
44958         
44959         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44960               
44961             
44962         this.hexStore.splice(0)
44963         
44964     },
44965     
44966     cmdgroupstart : function()
44967     {
44968         this.flushHexStore();
44969         if (this.group) {
44970             this.groupStack.push(this.group);
44971         }
44972          // parent..
44973         if (this.doc === false) {
44974             this.group = this.doc = new Roo.rtf.Document();
44975             return;
44976             
44977         }
44978         this.group = new Roo.rtf.Group(this.group);
44979     },
44980     cmdignorable : function()
44981     {
44982         this.flushHexStore();
44983         this.group.ignorable = true;
44984     },
44985     cmdendparagraph : function()
44986     {
44987         this.flushHexStore();
44988         this.group.addContent(new Roo.rtf.Paragraph());
44989     },
44990     cmdgroupend : function ()
44991     {
44992         this.flushHexStore();
44993         var endingGroup = this.group;
44994         
44995         
44996         this.group = this.groupStack.pop();
44997         if (this.group) {
44998             this.group.addChild(endingGroup);
44999         }
45000         
45001         
45002         
45003         var doc = this.group || this.doc;
45004         //if (endingGroup instanceof FontTable) {
45005         //  doc.fonts = endingGroup.table
45006         //} else if (endingGroup instanceof ColorTable) {
45007         //  doc.colors = endingGroup.table
45008         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45009         if (endingGroup.ignorable === false) {
45010             //code
45011             this.groups.push(endingGroup);
45012            // Roo.log( endingGroup );
45013         }
45014             //Roo.each(endingGroup.content, function(item)) {
45015             //    doc.addContent(item);
45016             //}
45017             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45018         //}
45019     },
45020     cmdtext : function (cmd)
45021     {
45022         this.flushHexStore();
45023         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45024             //this.group = this.doc
45025         }
45026         this.group.addContent(new Roo.rtf.Span(cmd));
45027     },
45028     cmdcontrolword : function (cmd)
45029     {
45030         this.flushHexStore();
45031         if (!this.group.type) {
45032             this.group.type = cmd.value;
45033             return;
45034         }
45035         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45036         // we actually don't care about ctrl words...
45037         return ;
45038         /*
45039         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45040         if (this[method]) {
45041             this[method](cmd.param)
45042         } else {
45043             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45044         }
45045         */
45046     },
45047     cmdhexchar : function(cmd) {
45048         this.hexStore.push(cmd);
45049     },
45050     cmderror : function(cmd) {
45051         throw new Exception (cmd.value);
45052     },
45053     
45054     /*
45055       _flush (done) {
45056         if (this.text !== '\u0000') this.emitText()
45057         done()
45058       }
45059       */
45060       
45061       
45062     parseText : function(c)
45063     {
45064         if (c === '\\') {
45065             this.parserState = this.parseEscapes;
45066         } else if (c === '{') {
45067             this.emitStartGroup();
45068         } else if (c === '}') {
45069             this.emitEndGroup();
45070         } else if (c === '\x0A' || c === '\x0D') {
45071             // cr/lf are noise chars
45072         } else {
45073             this.text += c;
45074         }
45075     },
45076     
45077     parseEscapes: function (c)
45078     {
45079         if (c === '\\' || c === '{' || c === '}') {
45080             this.text += c;
45081             this.parserState = this.parseText;
45082         } else {
45083             this.parserState = this.parseControlSymbol;
45084             this.parseControlSymbol(c);
45085         }
45086     },
45087     parseControlSymbol: function(c)
45088     {
45089         if (c === '~') {
45090             this.text += '\u00a0'; // nbsp
45091             this.parserState = this.parseText
45092         } else if (c === '-') {
45093              this.text += '\u00ad'; // soft hyphen
45094         } else if (c === '_') {
45095             this.text += '\u2011'; // non-breaking hyphen
45096         } else if (c === '*') {
45097             this.emitIgnorable();
45098             this.parserState = this.parseText;
45099         } else if (c === "'") {
45100             this.parserState = this.parseHexChar;
45101         } else if (c === '|') { // formula cacter
45102             this.emitFormula();
45103             this.parserState = this.parseText;
45104         } else if (c === ':') { // subentry in an index entry
45105             this.emitIndexSubEntry();
45106             this.parserState = this.parseText;
45107         } else if (c === '\x0a') {
45108             this.emitEndParagraph();
45109             this.parserState = this.parseText;
45110         } else if (c === '\x0d') {
45111             this.emitEndParagraph();
45112             this.parserState = this.parseText;
45113         } else {
45114             this.parserState = this.parseControlWord;
45115             this.parseControlWord(c);
45116         }
45117     },
45118     parseHexChar: function (c)
45119     {
45120         if (/^[A-Fa-f0-9]$/.test(c)) {
45121             this.hexChar += c;
45122             if (this.hexChar.length >= 2) {
45123               this.emitHexChar();
45124               this.parserState = this.parseText;
45125             }
45126             return;
45127         }
45128         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45129         this.parserState = this.parseText;
45130         
45131     },
45132     parseControlWord : function(c)
45133     {
45134         if (c === ' ') {
45135             this.emitControlWord();
45136             this.parserState = this.parseText;
45137         } else if (/^[-\d]$/.test(c)) {
45138             this.parserState = this.parseControlWordParam;
45139             this.controlWordParam += c;
45140         } else if (/^[A-Za-z]$/.test(c)) {
45141           this.controlWord += c;
45142         } else {
45143           this.emitControlWord();
45144           this.parserState = this.parseText;
45145           this.parseText(c);
45146         }
45147     },
45148     parseControlWordParam : function (c) {
45149         if (/^\d$/.test(c)) {
45150           this.controlWordParam += c;
45151         } else if (c === ' ') {
45152           this.emitControlWord();
45153           this.parserState = this.parseText;
45154         } else {
45155           this.emitControlWord();
45156           this.parserState = this.parseText;
45157           this.parseText(c);
45158         }
45159     },
45160     
45161     
45162     
45163     
45164     emitText : function () {
45165         if (this.text === '') {
45166             return;
45167         }
45168         this.push({
45169             type: 'text',
45170             value: this.text,
45171             pos: this.cpos,
45172             row: this.row,
45173             col: this.col
45174         });
45175         this.text = ''
45176     },
45177     emitControlWord : function ()
45178     {
45179         this.emitText();
45180         if (this.controlWord === '') {
45181             this.emitError('empty control word');
45182         } else {
45183             this.push({
45184                   type: 'controlword',
45185                   value: this.controlWord,
45186                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45187                   pos: this.cpos,
45188                   row: this.row,
45189                   col: this.col
45190             });
45191         }
45192         this.controlWord = '';
45193         this.controlWordParam = '';
45194     },
45195     emitStartGroup : function ()
45196     {
45197         this.emitText();
45198         this.push({
45199             type: 'groupstart',
45200             pos: this.cpos,
45201             row: this.row,
45202             col: this.col
45203         });
45204     },
45205     emitEndGroup : function ()
45206     {
45207         this.emitText();
45208         this.push({
45209             type: 'groupend',
45210             pos: this.cpos,
45211             row: this.row,
45212             col: this.col
45213         });
45214     },
45215     emitIgnorable : function ()
45216     {
45217         this.emitText();
45218         this.push({
45219             type: 'ignorable',
45220             pos: this.cpos,
45221             row: this.row,
45222             col: this.col
45223         });
45224     },
45225     emitHexChar : function ()
45226     {
45227         this.emitText();
45228         this.push({
45229             type: 'hexchar',
45230             value: this.hexChar,
45231             pos: this.cpos,
45232             row: this.row,
45233             col: this.col
45234         });
45235         this.hexChar = ''
45236     },
45237     emitError : function (message)
45238     {
45239       this.emitText();
45240       this.push({
45241             type: 'error',
45242             value: message,
45243             row: this.row,
45244             col: this.col,
45245             char: this.cpos //,
45246             //stack: new Error().stack
45247         });
45248     },
45249     emitEndParagraph : function () {
45250         this.emitText();
45251         this.push({
45252             type: 'endparagraph',
45253             pos: this.cpos,
45254             row: this.row,
45255             col: this.col
45256         });
45257     }
45258      
45259 } ;
45260 Roo.htmleditor = {};
45261  
45262 /**
45263  * @class Roo.htmleditor.Filter
45264  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45265  * @cfg {DomElement} node The node to iterate and filter
45266  * @cfg {boolean|String|Array} tag Tags to replace 
45267  * @constructor
45268  * Create a new Filter.
45269  * @param {Object} config Configuration options
45270  */
45271
45272
45273
45274 Roo.htmleditor.Filter = function(cfg) {
45275     Roo.apply(this.cfg);
45276     // this does not actually call walk as it's really just a abstract class
45277 }
45278
45279
45280 Roo.htmleditor.Filter.prototype = {
45281     
45282     node: false,
45283     
45284     tag: false,
45285
45286     // overrride to do replace comments.
45287     replaceComment : false,
45288     
45289     // overrride to do replace or do stuff with tags..
45290     replaceTag : false,
45291     
45292     walk : function(dom)
45293     {
45294         Roo.each( Array.from(dom.childNodes), function( e ) {
45295             switch(true) {
45296                 
45297                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45298                     this.replaceComment(e);
45299                     return;
45300                 
45301                 case e.nodeType != 1: //not a node.
45302                     return;
45303                 
45304                 case this.tag === true: // everything
45305                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45306                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45307                     if (this.replaceTag && false === this.replaceTag(e)) {
45308                         return;
45309                     }
45310                     if (e.hasChildNodes()) {
45311                         this.walk(e);
45312                     }
45313                     return;
45314                 
45315                 default:    // tags .. that do not match.
45316                     if (e.hasChildNodes()) {
45317                         this.walk(e);
45318                     }
45319             }
45320             
45321         }, this);
45322         
45323     }
45324 }; 
45325
45326 /**
45327  * @class Roo.htmleditor.FilterAttributes
45328  * clean attributes and  styles including http:// etc.. in attribute
45329  * @constructor
45330 * Run a new Attribute Filter
45331 * @param {Object} config Configuration options
45332  */
45333 Roo.htmleditor.FilterAttributes = function(cfg)
45334 {
45335     Roo.apply(this, cfg);
45336     this.attrib_black = this.attrib_black || [];
45337     this.attrib_white = this.attrib_white || [];
45338
45339     this.attrib_clean = this.attrib_clean || [];
45340     this.style_white = this.style_white || [];
45341     this.style_black = this.style_black || [];
45342     this.walk(cfg.node);
45343 }
45344
45345 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45346 {
45347     tag: true, // all tags
45348     
45349     attrib_black : false, // array
45350     attrib_clean : false,
45351     attrib_white : false,
45352
45353     style_white : false,
45354     style_black : false,
45355      
45356      
45357     replaceTag : function(node)
45358     {
45359         if (!node.attributes || !node.attributes.length) {
45360             return true;
45361         }
45362         
45363         for (var i = node.attributes.length-1; i > -1 ; i--) {
45364             var a = node.attributes[i];
45365             //console.log(a);
45366             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45367                 node.removeAttribute(a.name);
45368                 continue;
45369             }
45370             
45371             
45372             
45373             if (a.name.toLowerCase().substr(0,2)=='on')  {
45374                 node.removeAttribute(a.name);
45375                 continue;
45376             }
45377             
45378             
45379             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45380                 node.removeAttribute(a.name);
45381                 continue;
45382             }
45383             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45384                 this.cleanAttr(node,a.name,a.value); // fixme..
45385                 continue;
45386             }
45387             if (a.name == 'style') {
45388                 this.cleanStyle(node,a.name,a.value);
45389                 continue;
45390             }
45391             /// clean up MS crap..
45392             // tecnically this should be a list of valid class'es..
45393             
45394             
45395             if (a.name == 'class') {
45396                 if (a.value.match(/^Mso/)) {
45397                     node.removeAttribute('class');
45398                 }
45399                 
45400                 if (a.value.match(/^body$/)) {
45401                     node.removeAttribute('class');
45402                 }
45403                 continue;
45404             }
45405             
45406             
45407             // style cleanup!?
45408             // class cleanup?
45409             
45410         }
45411         return true; // clean children
45412     },
45413         
45414     cleanAttr: function(node, n,v)
45415     {
45416         
45417         if (v.match(/^\./) || v.match(/^\//)) {
45418             return;
45419         }
45420         if (v.match(/^(http|https):\/\//)
45421             || v.match(/^mailto:/) 
45422             || v.match(/^ftp:/)
45423             || v.match(/^data:/)
45424             ) {
45425             return;
45426         }
45427         if (v.match(/^#/)) {
45428             return;
45429         }
45430         if (v.match(/^\{/)) { // allow template editing.
45431             return;
45432         }
45433 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45434         node.removeAttribute(n);
45435         
45436     },
45437     cleanStyle : function(node,  n,v)
45438     {
45439         if (v.match(/expression/)) { //XSS?? should we even bother..
45440             node.removeAttribute(n);
45441             return;
45442         }
45443         
45444         var parts = v.split(/;/);
45445         var clean = [];
45446         
45447         Roo.each(parts, function(p) {
45448             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45449             if (!p.length) {
45450                 return true;
45451             }
45452             var l = p.split(':').shift().replace(/\s+/g,'');
45453             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45454             
45455             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45456                 return true;
45457             }
45458             //Roo.log()
45459             // only allow 'c whitelisted system attributes'
45460             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45461                 return true;
45462             }
45463             
45464             
45465             clean.push(p);
45466             return true;
45467         },this);
45468         if (clean.length) { 
45469             node.setAttribute(n, clean.join(';'));
45470         } else {
45471             node.removeAttribute(n);
45472         }
45473         
45474     }
45475         
45476         
45477         
45478     
45479 });/**
45480  * @class Roo.htmleditor.FilterBlack
45481  * remove blacklisted elements.
45482  * @constructor
45483  * Run a new Blacklisted Filter
45484  * @param {Object} config Configuration options
45485  */
45486
45487 Roo.htmleditor.FilterBlack = function(cfg)
45488 {
45489     Roo.apply(this, cfg);
45490     this.walk(cfg.node);
45491 }
45492
45493 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45494 {
45495     tag : true, // all elements.
45496    
45497     replace : function(n)
45498     {
45499         n.parentNode.removeChild(n);
45500     }
45501 });
45502 /**
45503  * @class Roo.htmleditor.FilterComment
45504  * remove comments.
45505  * @constructor
45506 * Run a new Comments Filter
45507 * @param {Object} config Configuration options
45508  */
45509 Roo.htmleditor.FilterComment = function(cfg)
45510 {
45511     this.walk(cfg.node);
45512 }
45513
45514 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45515 {
45516   
45517     replaceComment : function(n)
45518     {
45519         n.parentNode.removeChild(n);
45520     }
45521 });/**
45522  * @class Roo.htmleditor.FilterKeepChildren
45523  * remove tags but keep children
45524  * @constructor
45525  * Run a new Keep Children Filter
45526  * @param {Object} config Configuration options
45527  */
45528
45529 Roo.htmleditor.FilterKeepChildren = function(cfg)
45530 {
45531     Roo.apply(this, cfg);
45532     if (this.tag === false) {
45533         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45534     }
45535     this.walk(cfg.node);
45536 }
45537
45538 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45539 {
45540     
45541   
45542     replaceTag : function(node)
45543     {
45544         // walk children...
45545         //Roo.log(node);
45546         var ar = Array.from(node.childNodes);
45547         //remove first..
45548         for (var i = 0; i < ar.length; i++) {
45549             if (ar[i].nodeType == 1) {
45550                 if (
45551                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45552                     || // array and it matches
45553                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45554                 ) {
45555                     this.replaceTag(ar[i]); // child is blacklisted as well...
45556                     continue;
45557                 }
45558             }
45559         }  
45560         ar = Array.from(node.childNodes);
45561         for (var i = 0; i < ar.length; i++) {
45562          
45563             node.removeChild(ar[i]);
45564             // what if we need to walk these???
45565             node.parentNode.insertBefore(ar[i], node);
45566             if (this.tag !== false) {
45567                 this.walk(ar[i]);
45568                 
45569             }
45570         }
45571         node.parentNode.removeChild(node);
45572         return false; // don't walk children
45573         
45574         
45575     }
45576 });/**
45577  * @class Roo.htmleditor.FilterParagraph
45578  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45579  * like on 'push' to remove the <p> tags and replace them with line breaks.
45580  * @constructor
45581  * Run a new Paragraph Filter
45582  * @param {Object} config Configuration options
45583  */
45584
45585 Roo.htmleditor.FilterParagraph = function(cfg)
45586 {
45587     // no need to apply config.
45588     this.walk(cfg.node);
45589 }
45590
45591 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45592 {
45593     
45594      
45595     tag : 'P',
45596     
45597      
45598     replaceTag : function(node)
45599     {
45600         
45601         if (node.childNodes.length == 1 &&
45602             node.childNodes[0].nodeType == 3 &&
45603             node.childNodes[0].textContent.trim().length < 1
45604             ) {
45605             // remove and replace with '<BR>';
45606             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45607             return false; // no need to walk..
45608         }
45609         var ar = Array.from(node.childNodes);
45610         for (var i = 0; i < ar.length; i++) {
45611             node.removeChild(ar[i]);
45612             // what if we need to walk these???
45613             node.parentNode.insertBefore(ar[i], node);
45614         }
45615         // now what about this?
45616         // <p> &nbsp; </p>
45617         
45618         // double BR.
45619         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45620         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45621         node.parentNode.removeChild(node);
45622         
45623         return false;
45624
45625     }
45626     
45627 });/**
45628  * @class Roo.htmleditor.FilterSpan
45629  * filter span's with no attributes out..
45630  * @constructor
45631  * Run a new Span Filter
45632  * @param {Object} config Configuration options
45633  */
45634
45635 Roo.htmleditor.FilterSpan = function(cfg)
45636 {
45637     // no need to apply config.
45638     this.walk(cfg.node);
45639 }
45640
45641 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45642 {
45643      
45644     tag : 'SPAN',
45645      
45646  
45647     replaceTag : function(node)
45648     {
45649         if (node.attributes && node.attributes.length > 0) {
45650             return true; // walk if there are any.
45651         }
45652         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45653         return false;
45654      
45655     }
45656     
45657 });/**
45658  * @class Roo.htmleditor.FilterTableWidth
45659   try and remove table width data - as that frequently messes up other stuff.
45660  * 
45661  *      was cleanTableWidths.
45662  *
45663  * Quite often pasting from word etc.. results in tables with column and widths.
45664  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45665  *
45666  * @constructor
45667  * Run a new Table Filter
45668  * @param {Object} config Configuration options
45669  */
45670
45671 Roo.htmleditor.FilterTableWidth = function(cfg)
45672 {
45673     // no need to apply config.
45674     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45675     this.walk(cfg.node);
45676 }
45677
45678 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45679 {
45680      
45681      
45682     
45683     replaceTag: function(node) {
45684         
45685         
45686       
45687         if (node.hasAttribute('width')) {
45688             node.removeAttribute('width');
45689         }
45690         
45691          
45692         if (node.hasAttribute("style")) {
45693             // pretty basic...
45694             
45695             var styles = node.getAttribute("style").split(";");
45696             var nstyle = [];
45697             Roo.each(styles, function(s) {
45698                 if (!s.match(/:/)) {
45699                     return;
45700                 }
45701                 var kv = s.split(":");
45702                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45703                     return;
45704                 }
45705                 // what ever is left... we allow.
45706                 nstyle.push(s);
45707             });
45708             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45709             if (!nstyle.length) {
45710                 node.removeAttribute('style');
45711             }
45712         }
45713         
45714         return true; // continue doing children..
45715     }
45716 });/**
45717  * @class Roo.htmleditor.FilterWord
45718  * try and clean up all the mess that Word generates.
45719  * 
45720  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45721  
45722  * @constructor
45723  * Run a new Span Filter
45724  * @param {Object} config Configuration options
45725  */
45726
45727 Roo.htmleditor.FilterWord = function(cfg)
45728 {
45729     // no need to apply config.
45730     this.walk(cfg.node);
45731 }
45732
45733 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45734 {
45735     tag: true,
45736      
45737     
45738     /**
45739      * Clean up MS wordisms...
45740      */
45741     replaceTag : function(node)
45742     {
45743          
45744         // no idea what this does - span with text, replaceds with just text.
45745         if(
45746                 node.nodeName == 'SPAN' &&
45747                 !node.hasAttributes() &&
45748                 node.childNodes.length == 1 &&
45749                 node.firstChild.nodeName == "#text"  
45750         ) {
45751             var textNode = node.firstChild;
45752             node.removeChild(textNode);
45753             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45754                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45755             }
45756             node.parentNode.insertBefore(textNode, node);
45757             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45758                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45759             }
45760             
45761             node.parentNode.removeChild(node);
45762             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45763         }
45764         
45765    
45766         
45767         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45768             node.parentNode.removeChild(node);
45769             return false; // dont do chidlren
45770         }
45771         //Roo.log(node.tagName);
45772         // remove - but keep children..
45773         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45774             //Roo.log('-- removed');
45775             while (node.childNodes.length) {
45776                 var cn = node.childNodes[0];
45777                 node.removeChild(cn);
45778                 node.parentNode.insertBefore(cn, node);
45779                 // move node to parent - and clean it..
45780                 this.replaceTag(cn);
45781             }
45782             node.parentNode.removeChild(node);
45783             /// no need to iterate chidlren = it's got none..
45784             //this.iterateChildren(node, this.cleanWord);
45785             return false; // no need to iterate children.
45786         }
45787         // clean styles
45788         if (node.className.length) {
45789             
45790             var cn = node.className.split(/\W+/);
45791             var cna = [];
45792             Roo.each(cn, function(cls) {
45793                 if (cls.match(/Mso[a-zA-Z]+/)) {
45794                     return;
45795                 }
45796                 cna.push(cls);
45797             });
45798             node.className = cna.length ? cna.join(' ') : '';
45799             if (!cna.length) {
45800                 node.removeAttribute("class");
45801             }
45802         }
45803         
45804         if (node.hasAttribute("lang")) {
45805             node.removeAttribute("lang");
45806         }
45807         
45808         if (node.hasAttribute("style")) {
45809             
45810             var styles = node.getAttribute("style").split(";");
45811             var nstyle = [];
45812             Roo.each(styles, function(s) {
45813                 if (!s.match(/:/)) {
45814                     return;
45815                 }
45816                 var kv = s.split(":");
45817                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45818                     return;
45819                 }
45820                 // what ever is left... we allow.
45821                 nstyle.push(s);
45822             });
45823             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45824             if (!nstyle.length) {
45825                 node.removeAttribute('style');
45826             }
45827         }
45828         return true; // do children
45829         
45830         
45831         
45832     }
45833 });
45834 /**
45835  * @class Roo.htmleditor.FilterStyleToTag
45836  * part of the word stuff... - certain 'styles' should be converted to tags.
45837  * eg.
45838  *   font-weight: bold -> bold
45839  *   ?? super / subscrit etc..
45840  * 
45841  * @constructor
45842 * Run a new style to tag filter.
45843 * @param {Object} config Configuration options
45844  */
45845 Roo.htmleditor.FilterStyleToTag = function(cfg)
45846 {
45847     
45848     this.tags = {
45849         B  : [ 'fontWeight' , 'bold'],
45850         I :  [ 'fontStyle' , 'italic'],
45851         //pre :  [ 'font-style' , 'italic'],
45852         // h1.. h6 ?? font-size?
45853         SUP : [ 'verticalAlign' , 'super' ],
45854         SUB : [ 'verticalAlign' , 'sub' ]
45855         
45856         
45857     };
45858     
45859     Roo.apply(this, cfg);
45860      
45861     
45862     this.walk(cfg.node);
45863     
45864     
45865     
45866 }
45867
45868
45869 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45870 {
45871     tag: true, // all tags
45872     
45873     tags : false,
45874     
45875     
45876     replaceTag : function(node)
45877     {
45878         
45879         
45880         if (node.getAttribute("style") === null) {
45881             return true;
45882         }
45883         var inject = [];
45884         for (var k in this.tags) {
45885             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45886                 inject.push(k);
45887                 node.style.removeProperty(this.tags[k][0]);
45888             }
45889         }
45890         if (!inject.length) {
45891             return true; 
45892         }
45893         var cn = Array.from(node.childNodes);
45894         var nn = node;
45895         Roo.each(inject, function(t) {
45896             var nc = node.ownerDocument.createelement(t);
45897             nn.appendChild(nc);
45898             nn = nc;
45899         });
45900         for(var i = 0;i < cn.length;cn++) {
45901             node.removeChild(cn[i]);
45902             nn.appendChild(cn[i]);
45903         }
45904         return true /// iterate thru
45905     }
45906     
45907 })/**
45908  * @class Roo.htmleditor.FilterLongBr
45909  * BR/BR/BR - keep a maximum of 2...
45910  * @constructor
45911  * Run a new Long BR Filter
45912  * @param {Object} config Configuration options
45913  */
45914
45915 Roo.htmleditor.FilterLongBr = function(cfg)
45916 {
45917     // no need to apply config.
45918     this.walk(cfg.node);
45919 }
45920
45921 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45922 {
45923     
45924      
45925     tag : 'BR',
45926     
45927      
45928     replaceTag : function(node)
45929     {
45930         
45931         var ps = node.nextSibling;
45932         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45933             ps = ps.nextSibling;
45934         }
45935         
45936         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45937             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45938             return false;
45939         }
45940         
45941         if (!ps || ps.nodeType != 1) {
45942             return false;
45943         }
45944         
45945         if (!ps || ps.tagName != 'BR') {
45946            
45947             return false;
45948         }
45949         
45950         
45951         
45952         
45953         
45954         if (!node.previousSibling) {
45955             return false;
45956         }
45957         var ps = node.previousSibling;
45958         
45959         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45960             ps = ps.previousSibling;
45961         }
45962         if (!ps || ps.nodeType != 1) {
45963             return false;
45964         }
45965         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45966         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45967             return false;
45968         }
45969         
45970         node.parentNode.removeChild(node); // remove me...
45971         
45972         return false; // no need to do children
45973
45974     }
45975     
45976 });
45977 /**
45978  * @class Roo.htmleditor.Tidy
45979  * Tidy HTML 
45980  * @cfg {Roo.HtmlEditorCore} core the editor.
45981  * @constructor
45982  * Create a new Filter.
45983  * @param {Object} config Configuration options
45984  */
45985
45986
45987 Roo.htmleditor.Tidy = function(cfg) {
45988     Roo.apply(this, cfg);
45989     
45990     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45991      
45992 }
45993
45994 Roo.htmleditor.Tidy.toString = function(node)
45995 {
45996     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45997 }
45998
45999 Roo.htmleditor.Tidy.prototype = {
46000     
46001     
46002     wrap : function(s) {
46003         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46004     },
46005
46006     
46007     tidy : function(node, indent) {
46008      
46009         if  (node.nodeType == 3) {
46010             // text.
46011             
46012             
46013             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46014                 
46015             
46016         }
46017         
46018         if  (node.nodeType != 1) {
46019             return '';
46020         }
46021         
46022         
46023         
46024         if (node.tagName == 'BODY') {
46025             
46026             return this.cn(node, '');
46027         }
46028              
46029              // Prints the node tagName, such as <A>, <IMG>, etc
46030         var ret = "<" + node.tagName +  this.attr(node) ;
46031         
46032         // elements with no children..
46033         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46034                 return ret + '/>';
46035         }
46036         ret += '>';
46037         
46038         
46039         var cindent = indent === false ? '' : (indent + '  ');
46040         // tags where we will not pad the children.. (inline text tags etc..)
46041         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46042             cindent = false;
46043             
46044             
46045         }
46046         
46047         var cn = this.cn(node, cindent );
46048         
46049         return ret + cn  + '</' + node.tagName + '>';
46050         
46051     },
46052     cn: function(node, indent)
46053     {
46054         var ret = [];
46055         
46056         var ar = Array.from(node.childNodes);
46057         for (var i = 0 ; i < ar.length ; i++) {
46058             
46059             
46060             
46061             if (indent !== false   // indent==false preservies everything
46062                 && i > 0
46063                 && ar[i].nodeType == 3 
46064                 && ar[i].nodeValue.length > 0
46065                 && ar[i].nodeValue.match(/^\s+/)
46066             ) {
46067                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46068                     ret.pop(); // remove line break from last?
46069                 }
46070                 
46071                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46072             }
46073             if (indent !== false
46074                 && ar[i].nodeType == 1 // element - and indent is not set... 
46075             ) {
46076                 ret.push("\n" + indent); 
46077             }
46078             
46079             ret.push(this.tidy(ar[i], indent));
46080             // text + trailing indent 
46081             if (indent !== false
46082                 && ar[i].nodeType == 3
46083                 && ar[i].nodeValue.length > 0
46084                 && ar[i].nodeValue.match(/\s+$/)
46085             ){
46086                 ret.push("\n" + indent); 
46087             }
46088             
46089             
46090             
46091             
46092         }
46093         // what if all text?
46094         
46095         
46096         return ret.join('');
46097     },
46098     
46099          
46100         
46101     attr : function(node)
46102     {
46103         var attr = [];
46104         for(i = 0; i < node.attributes.length;i++) {
46105             
46106             // skip empty values?
46107             if (!node.attributes.item(i).value.length) {
46108                 continue;
46109             }
46110             attr.push(  node.attributes.item(i).name + '="' +
46111                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46112             );
46113         }
46114         return attr.length ? (' ' + attr.join(' ') ) : '';
46115         
46116     }
46117     
46118     
46119     
46120 }
46121 /**
46122  * @class Roo.htmleditor.KeyEnter
46123  * Handle Enter press..
46124  * @cfg {Roo.HtmlEditorCore} core the editor.
46125  * @constructor
46126  * Create a new Filter.
46127  * @param {Object} config Configuration options
46128  */
46129
46130
46131
46132 Roo.htmleditor.KeyEnter = function(cfg) {
46133     Roo.apply(this, cfg);
46134     // this does not actually call walk as it's really just a abstract class
46135  
46136     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46137 }
46138
46139
46140 Roo.htmleditor.KeyEnter.prototype = {
46141     
46142     core : false,
46143     
46144     keypress : function(e) {
46145         if (e.charCode != 13) {
46146             return true;
46147         }
46148         e.preventDefault();
46149         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46150         var doc = this.core.doc;
46151         
46152         var docFragment = doc.createDocumentFragment();
46153     
46154         //add a new line
46155         var newEle = doc.createTextNode('\n');
46156         docFragment.appendChild(newEle);
46157     
46158     
46159         var range = this.core.win.getSelection().getRangeAt(0);
46160         var n = range.commonAncestorContainer ;
46161         while (n && n.nodeType != 1) {
46162             n  = n.parentNode;
46163         }
46164         var li = false;
46165         if (n && n.tagName == 'UL') {
46166             li = doc.createElement('LI');
46167             n.appendChild(li);
46168             
46169         }
46170         if (n && n.tagName == 'LI') {
46171             li = doc.createElement('LI');
46172             if (n.nextSibling) {
46173                 n.parentNode.insertBefore(li, n.firstSibling);
46174                 
46175             } else {
46176                 n.parentNode.appendChild(li);
46177             }
46178         }
46179         if (li) {   
46180             range = doc.createRange();
46181             range.setStartAfter(li);
46182             range.collapse(true);
46183         
46184             //make the cursor there
46185             var sel = this.core.win.getSelection();
46186             sel.removeAllRanges();
46187             sel.addRange(range);
46188             return false;
46189             
46190             
46191         }
46192         //add the br, or p, or something else
46193         newEle = doc.createElement('br');
46194         docFragment.appendChild(newEle);
46195     
46196         //make the br replace selection
46197         
46198         range.deleteContents();
46199         
46200         range.insertNode(docFragment);
46201     
46202         //create a new range
46203         range = doc.createRange();
46204         range.setStartAfter(newEle);
46205         range.collapse(true);
46206     
46207         //make the cursor there
46208         var sel = this.core.win.getSelection();
46209         sel.removeAllRanges();
46210         sel.addRange(range);
46211     
46212         return false;
46213          
46214     }
46215 };
46216      
46217 /**
46218  * @class Roo.htmleditor.Block
46219  * Base class for html editor blocks - do not use it directly .. extend it..
46220  * @cfg {DomElement} node The node to apply stuff to.
46221  * @cfg {String} friendly_name the name that appears in the context bar about this block
46222  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46223  
46224  * @constructor
46225  * Create a new Filter.
46226  * @param {Object} config Configuration options
46227  */
46228
46229 Roo.htmleditor.Block  = function(cfg)
46230 {
46231     // do nothing .. should not be called really.
46232 }
46233
46234 Roo.htmleditor.Block.factory = function(node)
46235 {
46236     
46237     var id = Roo.get(node).id;
46238     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46239         Roo.htmleditor.Block.cache[id].readElement();
46240         return Roo.htmleditor.Block.cache[id];
46241     }
46242     
46243     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46244     if (typeof(cls) == 'undefined') {
46245         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46246         return false;
46247     }
46248     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46249     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46250 };
46251 // question goes here... do we need to clear out this cache sometimes?
46252 // or show we make it relivant to the htmleditor.
46253 Roo.htmleditor.Block.cache = {};
46254
46255 Roo.htmleditor.Block.prototype = {
46256     
46257     node : false,
46258     
46259      // used by context menu
46260     friendly_name : 'Image with caption',
46261     
46262     context : false,
46263     /**
46264      * Update a node with values from this object
46265      * @param {DomElement} node
46266      */
46267     updateElement : function(node)
46268     {
46269         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46270     },
46271      /**
46272      * convert to plain HTML for calling insertAtCursor..
46273      */
46274     toHTML : function()
46275     {
46276         return Roo.DomHelper.markup(this.toObject());
46277     },
46278     /**
46279      * used by readEleemnt to extract data from a node
46280      * may need improving as it's pretty basic
46281      
46282      * @param {DomElement} node
46283      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46284      * @param {String} attribute (use html - for contents, or style for using next param as style)
46285      * @param {String} style the style property - eg. text-align
46286      */
46287     getVal : function(node, tag, attr, style)
46288     {
46289         var n = node;
46290         if (tag !== true && n.tagName != tag.toUpperCase()) {
46291             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46292             // but kiss for now.
46293             n = node.getElementsByTagName(tag).item(0);
46294         }
46295         if (attr == 'html') {
46296             return n.innerHTML;
46297         }
46298         if (attr == 'style') {
46299             return Roo.get(n).getStyle(style);
46300         }
46301         
46302         return Roo.get(n).attr(attr);
46303             
46304     },
46305     /**
46306      * create a DomHelper friendly object - for use with 
46307      * Roo.DomHelper.markup / overwrite / etc..
46308      * (override this)
46309      */
46310     toObject : function()
46311     {
46312         return {};
46313     },
46314       /**
46315      * Read a node that has a 'data-block' property - and extract the values from it.
46316      * @param {DomElement} node - the node
46317      */
46318     readElement : function(node)
46319     {
46320         
46321     } 
46322     
46323     
46324 };
46325
46326  
46327
46328 /**
46329  * @class Roo.htmleditor.BlockFigure
46330  * Block that has an image and a figcaption
46331  * @cfg {String} image_src the url for the image
46332  * @cfg {String} align (left|right) alignment for the block default left
46333  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46334  * @cfg {String} caption the text to appear below  (and in the alt tag)
46335  * @cfg {String|number} image_width the width of the image number or %?
46336  * @cfg {String|number} image_height the height of the image number or %?
46337  * 
46338  * @constructor
46339  * Create a new Filter.
46340  * @param {Object} config Configuration options
46341  */
46342
46343 Roo.htmleditor.BlockFigure = function(cfg)
46344 {
46345     if (cfg.node) {
46346         this.readElement(cfg.node);
46347         this.updateElement(cfg.node);
46348     }
46349     Roo.apply(this, cfg);
46350 }
46351 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46352  
46353     
46354     // setable values.
46355     image_src: '',
46356     
46357     align: 'left',
46358     caption : '',
46359     text_align: 'left',
46360     
46361     width : '46%',
46362     margin: '2%',
46363     
46364     // used by context menu
46365     friendly_name : 'Image with caption',
46366     
46367     context : { // ?? static really
46368         width : {
46369             title: "Width",
46370             width: 40
46371             // ?? number
46372         },
46373         margin : {
46374             title: "Margin",
46375             width: 40
46376             // ?? number
46377         },
46378         align: {
46379             title: "Align",
46380             opts : [[ "left"],[ "right"]],
46381             width : 80
46382             
46383         },
46384         text_align: {
46385             title: "Caption Align",
46386             opts : [ [ "left"],[ "right"],[ "center"]],
46387             width : 80
46388         },
46389         
46390        
46391         image_src : {
46392             title: "Src",
46393             width: 220
46394         }
46395     },
46396     /**
46397      * create a DomHelper friendly object - for use with
46398      * Roo.DomHelper.markup / overwrite / etc..
46399      */
46400     toObject : function()
46401     {
46402         var d = document.createElement('div');
46403         d.innerHTML = this.caption;
46404         
46405         return {
46406             tag: 'figure',
46407             'data-block' : 'Figure',
46408             contenteditable : 'false',
46409             style : {
46410                 display: 'table',
46411                 float :  this.align ,
46412                 width :  this.width,
46413                 margin:  this.margin
46414             },
46415             cn : [
46416                 {
46417                     tag : 'img',
46418                     src : this.image_src,
46419                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46420                     style: {
46421                         width: '100%'
46422                     }
46423                 },
46424                 {
46425                     tag: 'figcaption',
46426                     contenteditable : true,
46427                     style : {
46428                         'text-align': this.text_align
46429                     },
46430                     html : this.caption
46431                     
46432                 }
46433             ]
46434         };
46435     },
46436     
46437     readElement : function(node)
46438     {
46439         this.image_src = this.getVal(node, 'img', 'src');
46440         this.align = this.getVal(node, 'figure', 'style', 'float');
46441         this.caption = this.getVal(node, 'figcaption', 'html');
46442         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46443         this.width = this.getVal(node, 'figure', 'style', 'width');
46444         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46445         
46446     } 
46447     
46448   
46449    
46450      
46451     
46452     
46453     
46454     
46455 })
46456
46457 //<script type="text/javascript">
46458
46459 /*
46460  * Based  Ext JS Library 1.1.1
46461  * Copyright(c) 2006-2007, Ext JS, LLC.
46462  * LGPL
46463  *
46464  */
46465  
46466 /**
46467  * @class Roo.HtmlEditorCore
46468  * @extends Roo.Component
46469  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46470  *
46471  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46472  */
46473
46474 Roo.HtmlEditorCore = function(config){
46475     
46476     
46477     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46478     
46479     
46480     this.addEvents({
46481         /**
46482          * @event initialize
46483          * Fires when the editor is fully initialized (including the iframe)
46484          * @param {Roo.HtmlEditorCore} this
46485          */
46486         initialize: true,
46487         /**
46488          * @event activate
46489          * Fires when the editor is first receives the focus. Any insertion must wait
46490          * until after this event.
46491          * @param {Roo.HtmlEditorCore} this
46492          */
46493         activate: true,
46494          /**
46495          * @event beforesync
46496          * Fires before the textarea is updated with content from the editor iframe. Return false
46497          * to cancel the sync.
46498          * @param {Roo.HtmlEditorCore} this
46499          * @param {String} html
46500          */
46501         beforesync: true,
46502          /**
46503          * @event beforepush
46504          * Fires before the iframe editor is updated with content from the textarea. Return false
46505          * to cancel the push.
46506          * @param {Roo.HtmlEditorCore} this
46507          * @param {String} html
46508          */
46509         beforepush: true,
46510          /**
46511          * @event sync
46512          * Fires when the textarea is updated with content from the editor iframe.
46513          * @param {Roo.HtmlEditorCore} this
46514          * @param {String} html
46515          */
46516         sync: true,
46517          /**
46518          * @event push
46519          * Fires when the iframe editor is updated with content from the textarea.
46520          * @param {Roo.HtmlEditorCore} this
46521          * @param {String} html
46522          */
46523         push: true,
46524         
46525         /**
46526          * @event editorevent
46527          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46528          * @param {Roo.HtmlEditorCore} this
46529          */
46530         editorevent: true
46531         
46532     });
46533     
46534     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46535     
46536     // defaults : white / black...
46537     this.applyBlacklists();
46538     
46539     
46540     
46541 };
46542
46543
46544 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46545
46546
46547      /**
46548      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46549      */
46550     
46551     owner : false,
46552     
46553      /**
46554      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46555      *                        Roo.resizable.
46556      */
46557     resizable : false,
46558      /**
46559      * @cfg {Number} height (in pixels)
46560      */   
46561     height: 300,
46562    /**
46563      * @cfg {Number} width (in pixels)
46564      */   
46565     width: 500,
46566     
46567     /**
46568      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46569      * 
46570      */
46571     stylesheets: false,
46572     
46573     /**
46574      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46575      */
46576     allowComments: false,
46577     // id of frame..
46578     frameId: false,
46579     
46580     // private properties
46581     validationEvent : false,
46582     deferHeight: true,
46583     initialized : false,
46584     activated : false,
46585     sourceEditMode : false,
46586     onFocus : Roo.emptyFn,
46587     iframePad:3,
46588     hideMode:'offsets',
46589     
46590     clearUp: true,
46591     
46592     // blacklist + whitelisted elements..
46593     black: false,
46594     white: false,
46595      
46596     bodyCls : '',
46597
46598     /**
46599      * Protected method that will not generally be called directly. It
46600      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46601      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46602      */
46603     getDocMarkup : function(){
46604         // body styles..
46605         var st = '';
46606         
46607         // inherit styels from page...?? 
46608         if (this.stylesheets === false) {
46609             
46610             Roo.get(document.head).select('style').each(function(node) {
46611                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46612             });
46613             
46614             Roo.get(document.head).select('link').each(function(node) { 
46615                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46616             });
46617             
46618         } else if (!this.stylesheets.length) {
46619                 // simple..
46620                 st = '<style type="text/css">' +
46621                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46622                    '</style>';
46623         } else {
46624             for (var i in this.stylesheets) {
46625                 if (typeof(this.stylesheets[i]) != 'string') {
46626                     continue;
46627                 }
46628                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46629             }
46630             
46631         }
46632         
46633         st +=  '<style type="text/css">' +
46634             'IMG { cursor: pointer } ' +
46635         '</style>';
46636
46637         var cls = 'roo-htmleditor-body';
46638         
46639         if(this.bodyCls.length){
46640             cls += ' ' + this.bodyCls;
46641         }
46642         
46643         return '<html><head>' + st  +
46644             //<style type="text/css">' +
46645             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46646             //'</style>' +
46647             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46648     },
46649
46650     // private
46651     onRender : function(ct, position)
46652     {
46653         var _t = this;
46654         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46655         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46656         
46657         
46658         this.el.dom.style.border = '0 none';
46659         this.el.dom.setAttribute('tabIndex', -1);
46660         this.el.addClass('x-hidden hide');
46661         
46662         
46663         
46664         if(Roo.isIE){ // fix IE 1px bogus margin
46665             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46666         }
46667        
46668         
46669         this.frameId = Roo.id();
46670         
46671          
46672         
46673         var iframe = this.owner.wrap.createChild({
46674             tag: 'iframe',
46675             cls: 'form-control', // bootstrap..
46676             id: this.frameId,
46677             name: this.frameId,
46678             frameBorder : 'no',
46679             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46680         }, this.el
46681         );
46682         
46683         
46684         this.iframe = iframe.dom;
46685
46686         this.assignDocWin();
46687         
46688         this.doc.designMode = 'on';
46689        
46690         this.doc.open();
46691         this.doc.write(this.getDocMarkup());
46692         this.doc.close();
46693
46694         
46695         var task = { // must defer to wait for browser to be ready
46696             run : function(){
46697                 //console.log("run task?" + this.doc.readyState);
46698                 this.assignDocWin();
46699                 if(this.doc.body || this.doc.readyState == 'complete'){
46700                     try {
46701                         this.doc.designMode="on";
46702                     } catch (e) {
46703                         return;
46704                     }
46705                     Roo.TaskMgr.stop(task);
46706                     this.initEditor.defer(10, this);
46707                 }
46708             },
46709             interval : 10,
46710             duration: 10000,
46711             scope: this
46712         };
46713         Roo.TaskMgr.start(task);
46714
46715     },
46716
46717     // private
46718     onResize : function(w, h)
46719     {
46720          Roo.log('resize: ' +w + ',' + h );
46721         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46722         if(!this.iframe){
46723             return;
46724         }
46725         if(typeof w == 'number'){
46726             
46727             this.iframe.style.width = w + 'px';
46728         }
46729         if(typeof h == 'number'){
46730             
46731             this.iframe.style.height = h + 'px';
46732             if(this.doc){
46733                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46734             }
46735         }
46736         
46737     },
46738
46739     /**
46740      * Toggles the editor between standard and source edit mode.
46741      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46742      */
46743     toggleSourceEdit : function(sourceEditMode){
46744         
46745         this.sourceEditMode = sourceEditMode === true;
46746         
46747         if(this.sourceEditMode){
46748  
46749             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46750             
46751         }else{
46752             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46753             //this.iframe.className = '';
46754             this.deferFocus();
46755         }
46756         //this.setSize(this.owner.wrap.getSize());
46757         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46758     },
46759
46760     
46761   
46762
46763     /**
46764      * Protected method that will not generally be called directly. If you need/want
46765      * custom HTML cleanup, this is the method you should override.
46766      * @param {String} html The HTML to be cleaned
46767      * return {String} The cleaned HTML
46768      */
46769     cleanHtml : function(html){
46770         html = String(html);
46771         if(html.length > 5){
46772             if(Roo.isSafari){ // strip safari nonsense
46773                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46774             }
46775         }
46776         if(html == '&nbsp;'){
46777             html = '';
46778         }
46779         return html;
46780     },
46781
46782     /**
46783      * HTML Editor -> Textarea
46784      * Protected method that will not generally be called directly. Syncs the contents
46785      * of the editor iframe with the textarea.
46786      */
46787     syncValue : function()
46788     {
46789         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46790         if(this.initialized){
46791             var bd = (this.doc.body || this.doc.documentElement);
46792             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46793             
46794             // not sure if this is really the place for this
46795             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46796             // this has to update attributes that get duped.. like alt and caption..
46797             
46798             
46799             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46800             //     Roo.htmleditor.Block.factory(e);
46801             //},this);
46802             
46803             
46804             var div = document.createElement('div');
46805             div.innerHTML = bd.innerHTML;
46806             // remove content editable. (blocks)
46807             
46808            
46809             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46810             //?? tidy?
46811             var html = div.innerHTML;
46812             if(Roo.isSafari){
46813                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46814                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46815                 if(m && m[1]){
46816                     html = '<div style="'+m[0]+'">' + html + '</div>';
46817                 }
46818             }
46819             html = this.cleanHtml(html);
46820             // fix up the special chars.. normaly like back quotes in word...
46821             // however we do not want to do this with chinese..
46822             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46823                 
46824                 var cc = match.charCodeAt();
46825
46826                 // Get the character value, handling surrogate pairs
46827                 if (match.length == 2) {
46828                     // It's a surrogate pair, calculate the Unicode code point
46829                     var high = match.charCodeAt(0) - 0xD800;
46830                     var low  = match.charCodeAt(1) - 0xDC00;
46831                     cc = (high * 0x400) + low + 0x10000;
46832                 }  else if (
46833                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46834                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46835                     (cc >= 0xf900 && cc < 0xfb00 )
46836                 ) {
46837                         return match;
46838                 }  
46839          
46840                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46841                 return "&#" + cc + ";";
46842                 
46843                 
46844             });
46845             
46846             
46847              
46848             if(this.owner.fireEvent('beforesync', this, html) !== false){
46849                 this.el.dom.value = html;
46850                 this.owner.fireEvent('sync', this, html);
46851             }
46852         }
46853     },
46854
46855     /**
46856      * TEXTAREA -> EDITABLE
46857      * Protected method that will not generally be called directly. Pushes the value of the textarea
46858      * into the iframe editor.
46859      */
46860     pushValue : function()
46861     {
46862         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46863         if(this.initialized){
46864             var v = this.el.dom.value.trim();
46865             
46866             
46867             if(this.owner.fireEvent('beforepush', this, v) !== false){
46868                 var d = (this.doc.body || this.doc.documentElement);
46869                 d.innerHTML = v;
46870                  
46871                 this.el.dom.value = d.innerHTML;
46872                 this.owner.fireEvent('push', this, v);
46873             }
46874             
46875             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46876                 
46877                 Roo.htmleditor.Block.factory(e);
46878                 
46879             },this);
46880             var lc = this.doc.body.lastChild;
46881             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46882                 // add an extra line at the end.
46883                 this.doc.body.appendChild(this.doc.createElement('br'));
46884             }
46885             
46886             
46887         }
46888     },
46889
46890     // private
46891     deferFocus : function(){
46892         this.focus.defer(10, this);
46893     },
46894
46895     // doc'ed in Field
46896     focus : function(){
46897         if(this.win && !this.sourceEditMode){
46898             this.win.focus();
46899         }else{
46900             this.el.focus();
46901         }
46902     },
46903     
46904     assignDocWin: function()
46905     {
46906         var iframe = this.iframe;
46907         
46908          if(Roo.isIE){
46909             this.doc = iframe.contentWindow.document;
46910             this.win = iframe.contentWindow;
46911         } else {
46912 //            if (!Roo.get(this.frameId)) {
46913 //                return;
46914 //            }
46915 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46916 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46917             
46918             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46919                 return;
46920             }
46921             
46922             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46923             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46924         }
46925     },
46926     
46927     // private
46928     initEditor : function(){
46929         //console.log("INIT EDITOR");
46930         this.assignDocWin();
46931         
46932         
46933         
46934         this.doc.designMode="on";
46935         this.doc.open();
46936         this.doc.write(this.getDocMarkup());
46937         this.doc.close();
46938         
46939         var dbody = (this.doc.body || this.doc.documentElement);
46940         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46941         // this copies styles from the containing element into thsi one..
46942         // not sure why we need all of this..
46943         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46944         
46945         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46946         //ss['background-attachment'] = 'fixed'; // w3c
46947         dbody.bgProperties = 'fixed'; // ie
46948         //Roo.DomHelper.applyStyles(dbody, ss);
46949         Roo.EventManager.on(this.doc, {
46950             //'mousedown': this.onEditorEvent,
46951             'mouseup': this.onEditorEvent,
46952             'dblclick': this.onEditorEvent,
46953             'click': this.onEditorEvent,
46954             'keyup': this.onEditorEvent,
46955             
46956             buffer:100,
46957             scope: this
46958         });
46959         Roo.EventManager.on(this.doc, {
46960             'paste': this.onPasteEvent,
46961             scope : this
46962         });
46963         if(Roo.isGecko){
46964             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46965         }
46966         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46967             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46968         }
46969         this.initialized = true;
46970
46971         
46972         // initialize special key events - enter
46973         new Roo.htmleditor.KeyEnter({core : this});
46974         
46975          
46976         
46977         this.owner.fireEvent('initialize', this);
46978         this.pushValue();
46979     },
46980     
46981     onPasteEvent : function(e,v)
46982     {
46983         // I think we better assume paste is going to be a dirty load of rubish from word..
46984         
46985         // even pasting into a 'email version' of this widget will have to clean up that mess.
46986         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46987         
46988         // check what type of paste - if it's an image, then handle it differently.
46989         if (cd.files.length > 0) {
46990             // pasting images?
46991             var urlAPI = (window.createObjectURL && window) || 
46992                 (window.URL && URL.revokeObjectURL && URL) || 
46993                 (window.webkitURL && webkitURL);
46994     
46995             var url = urlAPI.createObjectURL( cd.files[0]);
46996             this.insertAtCursor('<img src=" + url + ">');
46997             return false;
46998         }
46999         
47000         var html = cd.getData('text/html'); // clipboard event
47001         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47002         var images = parser.doc.getElementsByType('pict');
47003         Roo.log(images);
47004         //Roo.log(imgs);
47005         // fixme..
47006         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47007                        .map(function(g) { return g.toDataURL(); });
47008         
47009         
47010         html = this.cleanWordChars(html);
47011         
47012         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47013         
47014         if (images.length > 0) {
47015             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47016                 img.setAttribute('src', images[i]);
47017             });
47018         }
47019         
47020       
47021         new Roo.htmleditor.FilterStyleToTag({ node : d });
47022         new Roo.htmleditor.FilterAttributes({
47023             node : d,
47024             attrib_white : ['href', 'src', 'name', 'align'],
47025             attrib_clean : ['href', 'src' ] 
47026         });
47027         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47028         // should be fonts..
47029         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47030         new Roo.htmleditor.FilterParagraph({ node : d });
47031         new Roo.htmleditor.FilterSpan({ node : d });
47032         new Roo.htmleditor.FilterLongBr({ node : d });
47033         
47034         
47035         
47036         this.insertAtCursor(d.innerHTML);
47037         
47038         e.preventDefault();
47039         return false;
47040         // default behaveiour should be our local cleanup paste? (optional?)
47041         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47042         //this.owner.fireEvent('paste', e, v);
47043     },
47044     // private
47045     onDestroy : function(){
47046         
47047         
47048         
47049         if(this.rendered){
47050             
47051             //for (var i =0; i < this.toolbars.length;i++) {
47052             //    // fixme - ask toolbars for heights?
47053             //    this.toolbars[i].onDestroy();
47054            // }
47055             
47056             //this.wrap.dom.innerHTML = '';
47057             //this.wrap.remove();
47058         }
47059     },
47060
47061     // private
47062     onFirstFocus : function(){
47063         
47064         this.assignDocWin();
47065         
47066         
47067         this.activated = true;
47068          
47069     
47070         if(Roo.isGecko){ // prevent silly gecko errors
47071             this.win.focus();
47072             var s = this.win.getSelection();
47073             if(!s.focusNode || s.focusNode.nodeType != 3){
47074                 var r = s.getRangeAt(0);
47075                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47076                 r.collapse(true);
47077                 this.deferFocus();
47078             }
47079             try{
47080                 this.execCmd('useCSS', true);
47081                 this.execCmd('styleWithCSS', false);
47082             }catch(e){}
47083         }
47084         this.owner.fireEvent('activate', this);
47085     },
47086
47087     // private
47088     adjustFont: function(btn){
47089         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47090         //if(Roo.isSafari){ // safari
47091         //    adjust *= 2;
47092        // }
47093         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47094         if(Roo.isSafari){ // safari
47095             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47096             v =  (v < 10) ? 10 : v;
47097             v =  (v > 48) ? 48 : v;
47098             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47099             
47100         }
47101         
47102         
47103         v = Math.max(1, v+adjust);
47104         
47105         this.execCmd('FontSize', v  );
47106     },
47107
47108     onEditorEvent : function(e)
47109     {
47110         this.owner.fireEvent('editorevent', this, e);
47111       //  this.updateToolbar();
47112         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47113     },
47114
47115     insertTag : function(tg)
47116     {
47117         // could be a bit smarter... -> wrap the current selected tRoo..
47118         if (tg.toLowerCase() == 'span' ||
47119             tg.toLowerCase() == 'code' ||
47120             tg.toLowerCase() == 'sup' ||
47121             tg.toLowerCase() == 'sub' 
47122             ) {
47123             
47124             range = this.createRange(this.getSelection());
47125             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47126             wrappingNode.appendChild(range.extractContents());
47127             range.insertNode(wrappingNode);
47128
47129             return;
47130             
47131             
47132             
47133         }
47134         this.execCmd("formatblock",   tg);
47135         
47136     },
47137     
47138     insertText : function(txt)
47139     {
47140         
47141         
47142         var range = this.createRange();
47143         range.deleteContents();
47144                //alert(Sender.getAttribute('label'));
47145                
47146         range.insertNode(this.doc.createTextNode(txt));
47147     } ,
47148     
47149      
47150
47151     /**
47152      * Executes a Midas editor command on the editor document and performs necessary focus and
47153      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47154      * @param {String} cmd The Midas command
47155      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47156      */
47157     relayCmd : function(cmd, value){
47158         this.win.focus();
47159         this.execCmd(cmd, value);
47160         this.owner.fireEvent('editorevent', this);
47161         //this.updateToolbar();
47162         this.owner.deferFocus();
47163     },
47164
47165     /**
47166      * Executes a Midas editor command directly on the editor document.
47167      * For visual commands, you should use {@link #relayCmd} instead.
47168      * <b>This should only be called after the editor is initialized.</b>
47169      * @param {String} cmd The Midas command
47170      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47171      */
47172     execCmd : function(cmd, value){
47173         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47174         this.syncValue();
47175     },
47176  
47177  
47178    
47179     /**
47180      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47181      * to insert tRoo.
47182      * @param {String} text | dom node.. 
47183      */
47184     insertAtCursor : function(text)
47185     {
47186         
47187         if(!this.activated){
47188             return;
47189         }
47190          
47191         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47192             this.win.focus();
47193             
47194             
47195             // from jquery ui (MIT licenced)
47196             var range, node;
47197             var win = this.win;
47198             
47199             if (win.getSelection && win.getSelection().getRangeAt) {
47200                 
47201                 // delete the existing?
47202                 
47203                 this.createRange(this.getSelection()).deleteContents();
47204                 range = win.getSelection().getRangeAt(0);
47205                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47206                 range.insertNode(node);
47207             } else if (win.document.selection && win.document.selection.createRange) {
47208                 // no firefox support
47209                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47210                 win.document.selection.createRange().pasteHTML(txt);
47211             } else {
47212                 // no firefox support
47213                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47214                 this.execCmd('InsertHTML', txt);
47215             } 
47216             
47217             this.syncValue();
47218             
47219             this.deferFocus();
47220         }
47221     },
47222  // private
47223     mozKeyPress : function(e){
47224         if(e.ctrlKey){
47225             var c = e.getCharCode(), cmd;
47226           
47227             if(c > 0){
47228                 c = String.fromCharCode(c).toLowerCase();
47229                 switch(c){
47230                     case 'b':
47231                         cmd = 'bold';
47232                         break;
47233                     case 'i':
47234                         cmd = 'italic';
47235                         break;
47236                     
47237                     case 'u':
47238                         cmd = 'underline';
47239                         break;
47240                     
47241                     //case 'v':
47242                       //  this.cleanUpPaste.defer(100, this);
47243                       //  return;
47244                         
47245                 }
47246                 if(cmd){
47247                     this.win.focus();
47248                     this.execCmd(cmd);
47249                     this.deferFocus();
47250                     e.preventDefault();
47251                 }
47252                 
47253             }
47254         }
47255     },
47256
47257     // private
47258     fixKeys : function(){ // load time branching for fastest keydown performance
47259         if(Roo.isIE){
47260             return function(e){
47261                 var k = e.getKey(), r;
47262                 if(k == e.TAB){
47263                     e.stopEvent();
47264                     r = this.doc.selection.createRange();
47265                     if(r){
47266                         r.collapse(true);
47267                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47268                         this.deferFocus();
47269                     }
47270                     return;
47271                 }
47272                 
47273                 if(k == e.ENTER){
47274                     r = this.doc.selection.createRange();
47275                     if(r){
47276                         var target = r.parentElement();
47277                         if(!target || target.tagName.toLowerCase() != 'li'){
47278                             e.stopEvent();
47279                             r.pasteHTML('<br/>');
47280                             r.collapse(false);
47281                             r.select();
47282                         }
47283                     }
47284                 }
47285                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47286                 //    this.cleanUpPaste.defer(100, this);
47287                 //    return;
47288                 //}
47289                 
47290                 
47291             };
47292         }else if(Roo.isOpera){
47293             return function(e){
47294                 var k = e.getKey();
47295                 if(k == e.TAB){
47296                     e.stopEvent();
47297                     this.win.focus();
47298                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47299                     this.deferFocus();
47300                 }
47301                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47302                 //    this.cleanUpPaste.defer(100, this);
47303                  //   return;
47304                 //}
47305                 
47306             };
47307         }else if(Roo.isSafari){
47308             return function(e){
47309                 var k = e.getKey();
47310                 
47311                 if(k == e.TAB){
47312                     e.stopEvent();
47313                     this.execCmd('InsertText','\t');
47314                     this.deferFocus();
47315                     return;
47316                 }
47317                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47318                  //   this.cleanUpPaste.defer(100, this);
47319                  //   return;
47320                // }
47321                 
47322              };
47323         }
47324     }(),
47325     
47326     getAllAncestors: function()
47327     {
47328         var p = this.getSelectedNode();
47329         var a = [];
47330         if (!p) {
47331             a.push(p); // push blank onto stack..
47332             p = this.getParentElement();
47333         }
47334         
47335         
47336         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47337             a.push(p);
47338             p = p.parentNode;
47339         }
47340         a.push(this.doc.body);
47341         return a;
47342     },
47343     lastSel : false,
47344     lastSelNode : false,
47345     
47346     
47347     getSelection : function() 
47348     {
47349         this.assignDocWin();
47350         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47351     },
47352     /**
47353      * Select a dom node
47354      * @param {DomElement} node the node to select
47355      */
47356     selectNode : function(node)
47357     {
47358         
47359             var nodeRange = node.ownerDocument.createRange();
47360             try {
47361                 nodeRange.selectNode(node);
47362             } catch (e) {
47363                 nodeRange.selectNodeContents(node);
47364             }
47365             //nodeRange.collapse(true);
47366             var s = this.win.getSelection();
47367             s.removeAllRanges();
47368             s.addRange(nodeRange);
47369     },
47370     
47371     getSelectedNode: function() 
47372     {
47373         // this may only work on Gecko!!!
47374         
47375         // should we cache this!!!!
47376         
47377         
47378         
47379          
47380         var range = this.createRange(this.getSelection()).cloneRange();
47381         
47382         if (Roo.isIE) {
47383             var parent = range.parentElement();
47384             while (true) {
47385                 var testRange = range.duplicate();
47386                 testRange.moveToElementText(parent);
47387                 if (testRange.inRange(range)) {
47388                     break;
47389                 }
47390                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47391                     break;
47392                 }
47393                 parent = parent.parentElement;
47394             }
47395             return parent;
47396         }
47397         
47398         // is ancestor a text element.
47399         var ac =  range.commonAncestorContainer;
47400         if (ac.nodeType == 3) {
47401             ac = ac.parentNode;
47402         }
47403         
47404         var ar = ac.childNodes;
47405          
47406         var nodes = [];
47407         var other_nodes = [];
47408         var has_other_nodes = false;
47409         for (var i=0;i<ar.length;i++) {
47410             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47411                 continue;
47412             }
47413             // fullly contained node.
47414             
47415             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47416                 nodes.push(ar[i]);
47417                 continue;
47418             }
47419             
47420             // probably selected..
47421             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47422                 other_nodes.push(ar[i]);
47423                 continue;
47424             }
47425             // outer..
47426             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47427                 continue;
47428             }
47429             
47430             
47431             has_other_nodes = true;
47432         }
47433         if (!nodes.length && other_nodes.length) {
47434             nodes= other_nodes;
47435         }
47436         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47437             return false;
47438         }
47439         
47440         return nodes[0];
47441     },
47442     createRange: function(sel)
47443     {
47444         // this has strange effects when using with 
47445         // top toolbar - not sure if it's a great idea.
47446         //this.editor.contentWindow.focus();
47447         if (typeof sel != "undefined") {
47448             try {
47449                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47450             } catch(e) {
47451                 return this.doc.createRange();
47452             }
47453         } else {
47454             return this.doc.createRange();
47455         }
47456     },
47457     getParentElement: function()
47458     {
47459         
47460         this.assignDocWin();
47461         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47462         
47463         var range = this.createRange(sel);
47464          
47465         try {
47466             var p = range.commonAncestorContainer;
47467             while (p.nodeType == 3) { // text node
47468                 p = p.parentNode;
47469             }
47470             return p;
47471         } catch (e) {
47472             return null;
47473         }
47474     
47475     },
47476     /***
47477      *
47478      * Range intersection.. the hard stuff...
47479      *  '-1' = before
47480      *  '0' = hits..
47481      *  '1' = after.
47482      *         [ -- selected range --- ]
47483      *   [fail]                        [fail]
47484      *
47485      *    basically..
47486      *      if end is before start or  hits it. fail.
47487      *      if start is after end or hits it fail.
47488      *
47489      *   if either hits (but other is outside. - then it's not 
47490      *   
47491      *    
47492      **/
47493     
47494     
47495     // @see http://www.thismuchiknow.co.uk/?p=64.
47496     rangeIntersectsNode : function(range, node)
47497     {
47498         var nodeRange = node.ownerDocument.createRange();
47499         try {
47500             nodeRange.selectNode(node);
47501         } catch (e) {
47502             nodeRange.selectNodeContents(node);
47503         }
47504     
47505         var rangeStartRange = range.cloneRange();
47506         rangeStartRange.collapse(true);
47507     
47508         var rangeEndRange = range.cloneRange();
47509         rangeEndRange.collapse(false);
47510     
47511         var nodeStartRange = nodeRange.cloneRange();
47512         nodeStartRange.collapse(true);
47513     
47514         var nodeEndRange = nodeRange.cloneRange();
47515         nodeEndRange.collapse(false);
47516     
47517         return rangeStartRange.compareBoundaryPoints(
47518                  Range.START_TO_START, nodeEndRange) == -1 &&
47519                rangeEndRange.compareBoundaryPoints(
47520                  Range.START_TO_START, nodeStartRange) == 1;
47521         
47522          
47523     },
47524     rangeCompareNode : function(range, node)
47525     {
47526         var nodeRange = node.ownerDocument.createRange();
47527         try {
47528             nodeRange.selectNode(node);
47529         } catch (e) {
47530             nodeRange.selectNodeContents(node);
47531         }
47532         
47533         
47534         range.collapse(true);
47535     
47536         nodeRange.collapse(true);
47537      
47538         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47539         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47540          
47541         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47542         
47543         var nodeIsBefore   =  ss == 1;
47544         var nodeIsAfter    = ee == -1;
47545         
47546         if (nodeIsBefore && nodeIsAfter) {
47547             return 0; // outer
47548         }
47549         if (!nodeIsBefore && nodeIsAfter) {
47550             return 1; //right trailed.
47551         }
47552         
47553         if (nodeIsBefore && !nodeIsAfter) {
47554             return 2;  // left trailed.
47555         }
47556         // fully contined.
47557         return 3;
47558     },
47559  
47560     cleanWordChars : function(input) {// change the chars to hex code
47561         
47562        var swapCodes  = [ 
47563             [    8211, "&#8211;" ], 
47564             [    8212, "&#8212;" ], 
47565             [    8216,  "'" ],  
47566             [    8217, "'" ],  
47567             [    8220, '"' ],  
47568             [    8221, '"' ],  
47569             [    8226, "*" ],  
47570             [    8230, "..." ]
47571         ]; 
47572         var output = input;
47573         Roo.each(swapCodes, function(sw) { 
47574             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47575             
47576             output = output.replace(swapper, sw[1]);
47577         });
47578         
47579         return output;
47580     },
47581     
47582      
47583     
47584         
47585     
47586     cleanUpChild : function (node)
47587     {
47588         
47589         new Roo.htmleditor.FilterComment({node : node});
47590         new Roo.htmleditor.FilterAttributes({
47591                 node : node,
47592                 attrib_black : this.ablack,
47593                 attrib_clean : this.aclean,
47594                 style_white : this.cwhite,
47595                 style_black : this.cblack
47596         });
47597         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47598         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47599          
47600         
47601     },
47602     
47603     /**
47604      * Clean up MS wordisms...
47605      * @deprecated - use filter directly
47606      */
47607     cleanWord : function(node)
47608     {
47609         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47610         
47611     },
47612    
47613     
47614     /**
47615
47616      * @deprecated - use filters
47617      */
47618     cleanTableWidths : function(node)
47619     {
47620         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47621         
47622  
47623     },
47624     
47625      
47626         
47627     applyBlacklists : function()
47628     {
47629         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47630         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47631         
47632         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47633         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47634         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47635         
47636         this.white = [];
47637         this.black = [];
47638         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47639             if (b.indexOf(tag) > -1) {
47640                 return;
47641             }
47642             this.white.push(tag);
47643             
47644         }, this);
47645         
47646         Roo.each(w, function(tag) {
47647             if (b.indexOf(tag) > -1) {
47648                 return;
47649             }
47650             if (this.white.indexOf(tag) > -1) {
47651                 return;
47652             }
47653             this.white.push(tag);
47654             
47655         }, this);
47656         
47657         
47658         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47659             if (w.indexOf(tag) > -1) {
47660                 return;
47661             }
47662             this.black.push(tag);
47663             
47664         }, this);
47665         
47666         Roo.each(b, function(tag) {
47667             if (w.indexOf(tag) > -1) {
47668                 return;
47669             }
47670             if (this.black.indexOf(tag) > -1) {
47671                 return;
47672             }
47673             this.black.push(tag);
47674             
47675         }, this);
47676         
47677         
47678         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47679         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47680         
47681         this.cwhite = [];
47682         this.cblack = [];
47683         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47684             if (b.indexOf(tag) > -1) {
47685                 return;
47686             }
47687             this.cwhite.push(tag);
47688             
47689         }, this);
47690         
47691         Roo.each(w, function(tag) {
47692             if (b.indexOf(tag) > -1) {
47693                 return;
47694             }
47695             if (this.cwhite.indexOf(tag) > -1) {
47696                 return;
47697             }
47698             this.cwhite.push(tag);
47699             
47700         }, this);
47701         
47702         
47703         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47704             if (w.indexOf(tag) > -1) {
47705                 return;
47706             }
47707             this.cblack.push(tag);
47708             
47709         }, this);
47710         
47711         Roo.each(b, function(tag) {
47712             if (w.indexOf(tag) > -1) {
47713                 return;
47714             }
47715             if (this.cblack.indexOf(tag) > -1) {
47716                 return;
47717             }
47718             this.cblack.push(tag);
47719             
47720         }, this);
47721     },
47722     
47723     setStylesheets : function(stylesheets)
47724     {
47725         if(typeof(stylesheets) == 'string'){
47726             Roo.get(this.iframe.contentDocument.head).createChild({
47727                 tag : 'link',
47728                 rel : 'stylesheet',
47729                 type : 'text/css',
47730                 href : stylesheets
47731             });
47732             
47733             return;
47734         }
47735         var _this = this;
47736      
47737         Roo.each(stylesheets, function(s) {
47738             if(!s.length){
47739                 return;
47740             }
47741             
47742             Roo.get(_this.iframe.contentDocument.head).createChild({
47743                 tag : 'link',
47744                 rel : 'stylesheet',
47745                 type : 'text/css',
47746                 href : s
47747             });
47748         });
47749
47750         
47751     },
47752     
47753     removeStylesheets : function()
47754     {
47755         var _this = this;
47756         
47757         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47758             s.remove();
47759         });
47760     },
47761     
47762     setStyle : function(style)
47763     {
47764         Roo.get(this.iframe.contentDocument.head).createChild({
47765             tag : 'style',
47766             type : 'text/css',
47767             html : style
47768         });
47769
47770         return;
47771     }
47772     
47773     // hide stuff that is not compatible
47774     /**
47775      * @event blur
47776      * @hide
47777      */
47778     /**
47779      * @event change
47780      * @hide
47781      */
47782     /**
47783      * @event focus
47784      * @hide
47785      */
47786     /**
47787      * @event specialkey
47788      * @hide
47789      */
47790     /**
47791      * @cfg {String} fieldClass @hide
47792      */
47793     /**
47794      * @cfg {String} focusClass @hide
47795      */
47796     /**
47797      * @cfg {String} autoCreate @hide
47798      */
47799     /**
47800      * @cfg {String} inputType @hide
47801      */
47802     /**
47803      * @cfg {String} invalidClass @hide
47804      */
47805     /**
47806      * @cfg {String} invalidText @hide
47807      */
47808     /**
47809      * @cfg {String} msgFx @hide
47810      */
47811     /**
47812      * @cfg {String} validateOnBlur @hide
47813      */
47814 });
47815
47816 Roo.HtmlEditorCore.white = [
47817         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47818         
47819        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47820        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47821        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47822        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47823        'TABLE',   'UL',         'XMP', 
47824        
47825        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47826       'THEAD',   'TR', 
47827      
47828       'DIR', 'MENU', 'OL', 'UL', 'DL',
47829        
47830       'EMBED',  'OBJECT'
47831 ];
47832
47833
47834 Roo.HtmlEditorCore.black = [
47835     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47836         'APPLET', // 
47837         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47838         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47839         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47840         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47841         //'FONT' // CLEAN LATER..
47842         'COLGROUP', 'COL'  // messy tables.
47843         
47844 ];
47845 Roo.HtmlEditorCore.clean = [ // ?? needed???
47846      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47847 ];
47848 Roo.HtmlEditorCore.tag_remove = [
47849     'FONT', 'TBODY'  
47850 ];
47851 // attributes..
47852
47853 Roo.HtmlEditorCore.ablack = [
47854     'on'
47855 ];
47856     
47857 Roo.HtmlEditorCore.aclean = [ 
47858     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47859 ];
47860
47861 // protocols..
47862 Roo.HtmlEditorCore.pwhite= [
47863         'http',  'https',  'mailto'
47864 ];
47865
47866 // white listed style attributes.
47867 Roo.HtmlEditorCore.cwhite= [
47868       //  'text-align', /// default is to allow most things..
47869       
47870          
47871 //        'font-size'//??
47872 ];
47873
47874 // black listed style attributes.
47875 Roo.HtmlEditorCore.cblack= [
47876       //  'font-size' -- this can be set by the project 
47877 ];
47878
47879
47880
47881
47882     //<script type="text/javascript">
47883
47884 /*
47885  * Ext JS Library 1.1.1
47886  * Copyright(c) 2006-2007, Ext JS, LLC.
47887  * Licence LGPL
47888  * 
47889  */
47890  
47891  
47892 Roo.form.HtmlEditor = function(config){
47893     
47894     
47895     
47896     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47897     
47898     if (!this.toolbars) {
47899         this.toolbars = [];
47900     }
47901     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47902     
47903     
47904 };
47905
47906 /**
47907  * @class Roo.form.HtmlEditor
47908  * @extends Roo.form.Field
47909  * Provides a lightweight HTML Editor component.
47910  *
47911  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47912  * 
47913  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47914  * supported by this editor.</b><br/><br/>
47915  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47916  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47917  */
47918 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47919     /**
47920      * @cfg {Boolean} clearUp
47921      */
47922     clearUp : true,
47923       /**
47924      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47925      */
47926     toolbars : false,
47927    
47928      /**
47929      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47930      *                        Roo.resizable.
47931      */
47932     resizable : false,
47933      /**
47934      * @cfg {Number} height (in pixels)
47935      */   
47936     height: 300,
47937    /**
47938      * @cfg {Number} width (in pixels)
47939      */   
47940     width: 500,
47941     
47942     /**
47943      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47944      * 
47945      */
47946     stylesheets: false,
47947     
47948     
47949      /**
47950      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47951      * 
47952      */
47953     cblack: false,
47954     /**
47955      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47956      * 
47957      */
47958     cwhite: false,
47959     
47960      /**
47961      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47962      * 
47963      */
47964     black: false,
47965     /**
47966      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47967      * 
47968      */
47969     white: false,
47970     /**
47971      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47972      */
47973     allowComments: false,
47974     /**
47975      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47976      */
47977     
47978     
47979      bodyCls : '',
47980     
47981     // id of frame..
47982     frameId: false,
47983     
47984     // private properties
47985     validationEvent : false,
47986     deferHeight: true,
47987     initialized : false,
47988     activated : false,
47989     
47990     onFocus : Roo.emptyFn,
47991     iframePad:3,
47992     hideMode:'offsets',
47993     
47994     actionMode : 'container', // defaults to hiding it...
47995     
47996     defaultAutoCreate : { // modified by initCompnoent..
47997         tag: "textarea",
47998         style:"width:500px;height:300px;",
47999         autocomplete: "new-password"
48000     },
48001
48002     // private
48003     initComponent : function(){
48004         this.addEvents({
48005             /**
48006              * @event initialize
48007              * Fires when the editor is fully initialized (including the iframe)
48008              * @param {HtmlEditor} this
48009              */
48010             initialize: true,
48011             /**
48012              * @event activate
48013              * Fires when the editor is first receives the focus. Any insertion must wait
48014              * until after this event.
48015              * @param {HtmlEditor} this
48016              */
48017             activate: true,
48018              /**
48019              * @event beforesync
48020              * Fires before the textarea is updated with content from the editor iframe. Return false
48021              * to cancel the sync.
48022              * @param {HtmlEditor} this
48023              * @param {String} html
48024              */
48025             beforesync: true,
48026              /**
48027              * @event beforepush
48028              * Fires before the iframe editor is updated with content from the textarea. Return false
48029              * to cancel the push.
48030              * @param {HtmlEditor} this
48031              * @param {String} html
48032              */
48033             beforepush: true,
48034              /**
48035              * @event sync
48036              * Fires when the textarea is updated with content from the editor iframe.
48037              * @param {HtmlEditor} this
48038              * @param {String} html
48039              */
48040             sync: true,
48041              /**
48042              * @event push
48043              * Fires when the iframe editor is updated with content from the textarea.
48044              * @param {HtmlEditor} this
48045              * @param {String} html
48046              */
48047             push: true,
48048              /**
48049              * @event editmodechange
48050              * Fires when the editor switches edit modes
48051              * @param {HtmlEditor} this
48052              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48053              */
48054             editmodechange: true,
48055             /**
48056              * @event editorevent
48057              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48058              * @param {HtmlEditor} this
48059              */
48060             editorevent: true,
48061             /**
48062              * @event firstfocus
48063              * Fires when on first focus - needed by toolbars..
48064              * @param {HtmlEditor} this
48065              */
48066             firstfocus: true,
48067             /**
48068              * @event autosave
48069              * Auto save the htmlEditor value as a file into Events
48070              * @param {HtmlEditor} this
48071              */
48072             autosave: true,
48073             /**
48074              * @event savedpreview
48075              * preview the saved version of htmlEditor
48076              * @param {HtmlEditor} this
48077              */
48078             savedpreview: true,
48079             
48080             /**
48081             * @event stylesheetsclick
48082             * Fires when press the Sytlesheets button
48083             * @param {Roo.HtmlEditorCore} this
48084             */
48085             stylesheetsclick: true,
48086             /**
48087             * @event paste
48088             * Fires when press user pastes into the editor
48089             * @param {Roo.HtmlEditorCore} this
48090             */
48091             paste: true 
48092         });
48093         this.defaultAutoCreate =  {
48094             tag: "textarea",
48095             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48096             autocomplete: "new-password"
48097         };
48098     },
48099
48100     /**
48101      * Protected method that will not generally be called directly. It
48102      * is called when the editor creates its toolbar. Override this method if you need to
48103      * add custom toolbar buttons.
48104      * @param {HtmlEditor} editor
48105      */
48106     createToolbar : function(editor){
48107         Roo.log("create toolbars");
48108         if (!editor.toolbars || !editor.toolbars.length) {
48109             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48110         }
48111         
48112         for (var i =0 ; i < editor.toolbars.length;i++) {
48113             editor.toolbars[i] = Roo.factory(
48114                     typeof(editor.toolbars[i]) == 'string' ?
48115                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48116                 Roo.form.HtmlEditor);
48117             editor.toolbars[i].init(editor);
48118         }
48119          
48120         
48121     },
48122
48123      
48124     // private
48125     onRender : function(ct, position)
48126     {
48127         var _t = this;
48128         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48129         
48130         this.wrap = this.el.wrap({
48131             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48132         });
48133         
48134         this.editorcore.onRender(ct, position);
48135          
48136         if (this.resizable) {
48137             this.resizeEl = new Roo.Resizable(this.wrap, {
48138                 pinned : true,
48139                 wrap: true,
48140                 dynamic : true,
48141                 minHeight : this.height,
48142                 height: this.height,
48143                 handles : this.resizable,
48144                 width: this.width,
48145                 listeners : {
48146                     resize : function(r, w, h) {
48147                         _t.onResize(w,h); // -something
48148                     }
48149                 }
48150             });
48151             
48152         }
48153         this.createToolbar(this);
48154        
48155         
48156         if(!this.width){
48157             this.setSize(this.wrap.getSize());
48158         }
48159         if (this.resizeEl) {
48160             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48161             // should trigger onReize..
48162         }
48163         
48164         this.keyNav = new Roo.KeyNav(this.el, {
48165             
48166             "tab" : function(e){
48167                 e.preventDefault();
48168                 
48169                 var value = this.getValue();
48170                 
48171                 var start = this.el.dom.selectionStart;
48172                 var end = this.el.dom.selectionEnd;
48173                 
48174                 if(!e.shiftKey){
48175                     
48176                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48177                     this.el.dom.setSelectionRange(end + 1, end + 1);
48178                     return;
48179                 }
48180                 
48181                 var f = value.substring(0, start).split("\t");
48182                 
48183                 if(f.pop().length != 0){
48184                     return;
48185                 }
48186                 
48187                 this.setValue(f.join("\t") + value.substring(end));
48188                 this.el.dom.setSelectionRange(start - 1, start - 1);
48189                 
48190             },
48191             
48192             "home" : function(e){
48193                 e.preventDefault();
48194                 
48195                 var curr = this.el.dom.selectionStart;
48196                 var lines = this.getValue().split("\n");
48197                 
48198                 if(!lines.length){
48199                     return;
48200                 }
48201                 
48202                 if(e.ctrlKey){
48203                     this.el.dom.setSelectionRange(0, 0);
48204                     return;
48205                 }
48206                 
48207                 var pos = 0;
48208                 
48209                 for (var i = 0; i < lines.length;i++) {
48210                     pos += lines[i].length;
48211                     
48212                     if(i != 0){
48213                         pos += 1;
48214                     }
48215                     
48216                     if(pos < curr){
48217                         continue;
48218                     }
48219                     
48220                     pos -= lines[i].length;
48221                     
48222                     break;
48223                 }
48224                 
48225                 if(!e.shiftKey){
48226                     this.el.dom.setSelectionRange(pos, pos);
48227                     return;
48228                 }
48229                 
48230                 this.el.dom.selectionStart = pos;
48231                 this.el.dom.selectionEnd = curr;
48232             },
48233             
48234             "end" : function(e){
48235                 e.preventDefault();
48236                 
48237                 var curr = this.el.dom.selectionStart;
48238                 var lines = this.getValue().split("\n");
48239                 
48240                 if(!lines.length){
48241                     return;
48242                 }
48243                 
48244                 if(e.ctrlKey){
48245                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48246                     return;
48247                 }
48248                 
48249                 var pos = 0;
48250                 
48251                 for (var i = 0; i < lines.length;i++) {
48252                     
48253                     pos += lines[i].length;
48254                     
48255                     if(i != 0){
48256                         pos += 1;
48257                     }
48258                     
48259                     if(pos < curr){
48260                         continue;
48261                     }
48262                     
48263                     break;
48264                 }
48265                 
48266                 if(!e.shiftKey){
48267                     this.el.dom.setSelectionRange(pos, pos);
48268                     return;
48269                 }
48270                 
48271                 this.el.dom.selectionStart = curr;
48272                 this.el.dom.selectionEnd = pos;
48273             },
48274
48275             scope : this,
48276
48277             doRelay : function(foo, bar, hname){
48278                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48279             },
48280
48281             forceKeyDown: true
48282         });
48283         
48284 //        if(this.autosave && this.w){
48285 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48286 //        }
48287     },
48288
48289     // private
48290     onResize : function(w, h)
48291     {
48292         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48293         var ew = false;
48294         var eh = false;
48295         
48296         if(this.el ){
48297             if(typeof w == 'number'){
48298                 var aw = w - this.wrap.getFrameWidth('lr');
48299                 this.el.setWidth(this.adjustWidth('textarea', aw));
48300                 ew = aw;
48301             }
48302             if(typeof h == 'number'){
48303                 var tbh = 0;
48304                 for (var i =0; i < this.toolbars.length;i++) {
48305                     // fixme - ask toolbars for heights?
48306                     tbh += this.toolbars[i].tb.el.getHeight();
48307                     if (this.toolbars[i].footer) {
48308                         tbh += this.toolbars[i].footer.el.getHeight();
48309                     }
48310                 }
48311                 
48312                 
48313                 
48314                 
48315                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48316                 ah -= 5; // knock a few pixes off for look..
48317 //                Roo.log(ah);
48318                 this.el.setHeight(this.adjustWidth('textarea', ah));
48319                 var eh = ah;
48320             }
48321         }
48322         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48323         this.editorcore.onResize(ew,eh);
48324         
48325     },
48326
48327     /**
48328      * Toggles the editor between standard and source edit mode.
48329      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48330      */
48331     toggleSourceEdit : function(sourceEditMode)
48332     {
48333         this.editorcore.toggleSourceEdit(sourceEditMode);
48334         
48335         if(this.editorcore.sourceEditMode){
48336             Roo.log('editor - showing textarea');
48337             
48338 //            Roo.log('in');
48339 //            Roo.log(this.syncValue());
48340             this.editorcore.syncValue();
48341             this.el.removeClass('x-hidden');
48342             this.el.dom.removeAttribute('tabIndex');
48343             this.el.focus();
48344             this.el.dom.scrollTop = 0;
48345             
48346             
48347             for (var i = 0; i < this.toolbars.length; i++) {
48348                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48349                     this.toolbars[i].tb.hide();
48350                     this.toolbars[i].footer.hide();
48351                 }
48352             }
48353             
48354         }else{
48355             Roo.log('editor - hiding textarea');
48356 //            Roo.log('out')
48357 //            Roo.log(this.pushValue()); 
48358             this.editorcore.pushValue();
48359             
48360             this.el.addClass('x-hidden');
48361             this.el.dom.setAttribute('tabIndex', -1);
48362             
48363             for (var i = 0; i < this.toolbars.length; i++) {
48364                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48365                     this.toolbars[i].tb.show();
48366                     this.toolbars[i].footer.show();
48367                 }
48368             }
48369             
48370             //this.deferFocus();
48371         }
48372         
48373         this.setSize(this.wrap.getSize());
48374         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48375         
48376         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48377     },
48378  
48379     // private (for BoxComponent)
48380     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48381
48382     // private (for BoxComponent)
48383     getResizeEl : function(){
48384         return this.wrap;
48385     },
48386
48387     // private (for BoxComponent)
48388     getPositionEl : function(){
48389         return this.wrap;
48390     },
48391
48392     // private
48393     initEvents : function(){
48394         this.originalValue = this.getValue();
48395     },
48396
48397     /**
48398      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48399      * @method
48400      */
48401     markInvalid : Roo.emptyFn,
48402     /**
48403      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48404      * @method
48405      */
48406     clearInvalid : Roo.emptyFn,
48407
48408     setValue : function(v){
48409         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48410         this.editorcore.pushValue();
48411     },
48412
48413      
48414     // private
48415     deferFocus : function(){
48416         this.focus.defer(10, this);
48417     },
48418
48419     // doc'ed in Field
48420     focus : function(){
48421         this.editorcore.focus();
48422         
48423     },
48424       
48425
48426     // private
48427     onDestroy : function(){
48428         
48429         
48430         
48431         if(this.rendered){
48432             
48433             for (var i =0; i < this.toolbars.length;i++) {
48434                 // fixme - ask toolbars for heights?
48435                 this.toolbars[i].onDestroy();
48436             }
48437             
48438             this.wrap.dom.innerHTML = '';
48439             this.wrap.remove();
48440         }
48441     },
48442
48443     // private
48444     onFirstFocus : function(){
48445         //Roo.log("onFirstFocus");
48446         this.editorcore.onFirstFocus();
48447          for (var i =0; i < this.toolbars.length;i++) {
48448             this.toolbars[i].onFirstFocus();
48449         }
48450         
48451     },
48452     
48453     // private
48454     syncValue : function()
48455     {
48456         this.editorcore.syncValue();
48457     },
48458     
48459     pushValue : function()
48460     {
48461         this.editorcore.pushValue();
48462     },
48463     
48464     setStylesheets : function(stylesheets)
48465     {
48466         this.editorcore.setStylesheets(stylesheets);
48467     },
48468     
48469     removeStylesheets : function()
48470     {
48471         this.editorcore.removeStylesheets();
48472     }
48473      
48474     
48475     // hide stuff that is not compatible
48476     /**
48477      * @event blur
48478      * @hide
48479      */
48480     /**
48481      * @event change
48482      * @hide
48483      */
48484     /**
48485      * @event focus
48486      * @hide
48487      */
48488     /**
48489      * @event specialkey
48490      * @hide
48491      */
48492     /**
48493      * @cfg {String} fieldClass @hide
48494      */
48495     /**
48496      * @cfg {String} focusClass @hide
48497      */
48498     /**
48499      * @cfg {String} autoCreate @hide
48500      */
48501     /**
48502      * @cfg {String} inputType @hide
48503      */
48504     /**
48505      * @cfg {String} invalidClass @hide
48506      */
48507     /**
48508      * @cfg {String} invalidText @hide
48509      */
48510     /**
48511      * @cfg {String} msgFx @hide
48512      */
48513     /**
48514      * @cfg {String} validateOnBlur @hide
48515      */
48516 });
48517  
48518     // <script type="text/javascript">
48519 /*
48520  * Based on
48521  * Ext JS Library 1.1.1
48522  * Copyright(c) 2006-2007, Ext JS, LLC.
48523  *  
48524  
48525  */
48526
48527 /**
48528  * @class Roo.form.HtmlEditorToolbar1
48529  * Basic Toolbar
48530  * 
48531  * Usage:
48532  *
48533  new Roo.form.HtmlEditor({
48534     ....
48535     toolbars : [
48536         new Roo.form.HtmlEditorToolbar1({
48537             disable : { fonts: 1 , format: 1, ..., ... , ...],
48538             btns : [ .... ]
48539         })
48540     }
48541      
48542  * 
48543  * @cfg {Object} disable List of elements to disable..
48544  * @cfg {Array} btns List of additional buttons.
48545  * 
48546  * 
48547  * NEEDS Extra CSS? 
48548  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48549  */
48550  
48551 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48552 {
48553     
48554     Roo.apply(this, config);
48555     
48556     // default disabled, based on 'good practice'..
48557     this.disable = this.disable || {};
48558     Roo.applyIf(this.disable, {
48559         fontSize : true,
48560         colors : true,
48561         specialElements : true
48562     });
48563     
48564     
48565     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48566     // dont call parent... till later.
48567 }
48568
48569 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48570     
48571     tb: false,
48572     
48573     rendered: false,
48574     
48575     editor : false,
48576     editorcore : false,
48577     /**
48578      * @cfg {Object} disable  List of toolbar elements to disable
48579          
48580      */
48581     disable : false,
48582     
48583     
48584      /**
48585      * @cfg {String} createLinkText The default text for the create link prompt
48586      */
48587     createLinkText : 'Please enter the URL for the link:',
48588     /**
48589      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48590      */
48591     defaultLinkValue : 'http:/'+'/',
48592    
48593     
48594       /**
48595      * @cfg {Array} fontFamilies An array of available font families
48596      */
48597     fontFamilies : [
48598         'Arial',
48599         'Courier New',
48600         'Tahoma',
48601         'Times New Roman',
48602         'Verdana'
48603     ],
48604     
48605     specialChars : [
48606            "&#169;",
48607           "&#174;",     
48608           "&#8482;",    
48609           "&#163;" ,    
48610          // "&#8212;",    
48611           "&#8230;",    
48612           "&#247;" ,    
48613         //  "&#225;" ,     ?? a acute?
48614            "&#8364;"    , //Euro
48615        //   "&#8220;"    ,
48616         //  "&#8221;"    ,
48617         //  "&#8226;"    ,
48618           "&#176;"  //   , // degrees
48619
48620          // "&#233;"     , // e ecute
48621          // "&#250;"     , // u ecute?
48622     ],
48623     
48624     specialElements : [
48625         {
48626             text: "Insert Table",
48627             xtype: 'MenuItem',
48628             xns : Roo.Menu,
48629             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48630                 
48631         },
48632         {    
48633             text: "Insert Image",
48634             xtype: 'MenuItem',
48635             xns : Roo.Menu,
48636             ihtml : '<img src="about:blank"/>'
48637             
48638         }
48639         
48640          
48641     ],
48642     
48643     
48644     inputElements : [ 
48645             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48646             "input:submit", "input:button", "select", "textarea", "label" ],
48647     formats : [
48648         ["p"] ,  
48649         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48650         ["pre"],[ "code"], 
48651         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48652         ['div'],['span'],
48653         ['sup'],['sub']
48654     ],
48655     
48656     cleanStyles : [
48657         "font-size"
48658     ],
48659      /**
48660      * @cfg {String} defaultFont default font to use.
48661      */
48662     defaultFont: 'tahoma',
48663    
48664     fontSelect : false,
48665     
48666     
48667     formatCombo : false,
48668     
48669     init : function(editor)
48670     {
48671         this.editor = editor;
48672         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48673         var editorcore = this.editorcore;
48674         
48675         var _t = this;
48676         
48677         var fid = editorcore.frameId;
48678         var etb = this;
48679         function btn(id, toggle, handler){
48680             var xid = fid + '-'+ id ;
48681             return {
48682                 id : xid,
48683                 cmd : id,
48684                 cls : 'x-btn-icon x-edit-'+id,
48685                 enableToggle:toggle !== false,
48686                 scope: _t, // was editor...
48687                 handler:handler||_t.relayBtnCmd,
48688                 clickEvent:'mousedown',
48689                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48690                 tabIndex:-1
48691             };
48692         }
48693         
48694         
48695         
48696         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48697         this.tb = tb;
48698          // stop form submits
48699         tb.el.on('click', function(e){
48700             e.preventDefault(); // what does this do?
48701         });
48702
48703         if(!this.disable.font) { // && !Roo.isSafari){
48704             /* why no safari for fonts 
48705             editor.fontSelect = tb.el.createChild({
48706                 tag:'select',
48707                 tabIndex: -1,
48708                 cls:'x-font-select',
48709                 html: this.createFontOptions()
48710             });
48711             
48712             editor.fontSelect.on('change', function(){
48713                 var font = editor.fontSelect.dom.value;
48714                 editor.relayCmd('fontname', font);
48715                 editor.deferFocus();
48716             }, editor);
48717             
48718             tb.add(
48719                 editor.fontSelect.dom,
48720                 '-'
48721             );
48722             */
48723             
48724         };
48725         if(!this.disable.formats){
48726             this.formatCombo = new Roo.form.ComboBox({
48727                 store: new Roo.data.SimpleStore({
48728                     id : 'tag',
48729                     fields: ['tag'],
48730                     data : this.formats // from states.js
48731                 }),
48732                 blockFocus : true,
48733                 name : '',
48734                 //autoCreate : {tag: "div",  size: "20"},
48735                 displayField:'tag',
48736                 typeAhead: false,
48737                 mode: 'local',
48738                 editable : false,
48739                 triggerAction: 'all',
48740                 emptyText:'Add tag',
48741                 selectOnFocus:true,
48742                 width:135,
48743                 listeners : {
48744                     'select': function(c, r, i) {
48745                         editorcore.insertTag(r.get('tag'));
48746                         editor.focus();
48747                     }
48748                 }
48749
48750             });
48751             tb.addField(this.formatCombo);
48752             
48753         }
48754         
48755         if(!this.disable.format){
48756             tb.add(
48757                 btn('bold'),
48758                 btn('italic'),
48759                 btn('underline'),
48760                 btn('strikethrough')
48761             );
48762         };
48763         if(!this.disable.fontSize){
48764             tb.add(
48765                 '-',
48766                 
48767                 
48768                 btn('increasefontsize', false, editorcore.adjustFont),
48769                 btn('decreasefontsize', false, editorcore.adjustFont)
48770             );
48771         };
48772         
48773         
48774         if(!this.disable.colors){
48775             tb.add(
48776                 '-', {
48777                     id:editorcore.frameId +'-forecolor',
48778                     cls:'x-btn-icon x-edit-forecolor',
48779                     clickEvent:'mousedown',
48780                     tooltip: this.buttonTips['forecolor'] || undefined,
48781                     tabIndex:-1,
48782                     menu : new Roo.menu.ColorMenu({
48783                         allowReselect: true,
48784                         focus: Roo.emptyFn,
48785                         value:'000000',
48786                         plain:true,
48787                         selectHandler: function(cp, color){
48788                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48789                             editor.deferFocus();
48790                         },
48791                         scope: editorcore,
48792                         clickEvent:'mousedown'
48793                     })
48794                 }, {
48795                     id:editorcore.frameId +'backcolor',
48796                     cls:'x-btn-icon x-edit-backcolor',
48797                     clickEvent:'mousedown',
48798                     tooltip: this.buttonTips['backcolor'] || undefined,
48799                     tabIndex:-1,
48800                     menu : new Roo.menu.ColorMenu({
48801                         focus: Roo.emptyFn,
48802                         value:'FFFFFF',
48803                         plain:true,
48804                         allowReselect: true,
48805                         selectHandler: function(cp, color){
48806                             if(Roo.isGecko){
48807                                 editorcore.execCmd('useCSS', false);
48808                                 editorcore.execCmd('hilitecolor', color);
48809                                 editorcore.execCmd('useCSS', true);
48810                                 editor.deferFocus();
48811                             }else{
48812                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48813                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48814                                 editor.deferFocus();
48815                             }
48816                         },
48817                         scope:editorcore,
48818                         clickEvent:'mousedown'
48819                     })
48820                 }
48821             );
48822         };
48823         // now add all the items...
48824         
48825
48826         if(!this.disable.alignments){
48827             tb.add(
48828                 '-',
48829                 btn('justifyleft'),
48830                 btn('justifycenter'),
48831                 btn('justifyright')
48832             );
48833         };
48834
48835         //if(!Roo.isSafari){
48836             if(!this.disable.links){
48837                 tb.add(
48838                     '-',
48839                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48840                 );
48841             };
48842
48843             if(!this.disable.lists){
48844                 tb.add(
48845                     '-',
48846                     btn('insertorderedlist'),
48847                     btn('insertunorderedlist')
48848                 );
48849             }
48850             if(!this.disable.sourceEdit){
48851                 tb.add(
48852                     '-',
48853                     btn('sourceedit', true, function(btn){
48854                         this.toggleSourceEdit(btn.pressed);
48855                     })
48856                 );
48857             }
48858         //}
48859         
48860         var smenu = { };
48861         // special menu.. - needs to be tidied up..
48862         if (!this.disable.special) {
48863             smenu = {
48864                 text: "&#169;",
48865                 cls: 'x-edit-none',
48866                 
48867                 menu : {
48868                     items : []
48869                 }
48870             };
48871             for (var i =0; i < this.specialChars.length; i++) {
48872                 smenu.menu.items.push({
48873                     
48874                     html: this.specialChars[i],
48875                     handler: function(a,b) {
48876                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48877                         //editor.insertAtCursor(a.html);
48878                         
48879                     },
48880                     tabIndex:-1
48881                 });
48882             }
48883             
48884             
48885             tb.add(smenu);
48886             
48887             
48888         }
48889         
48890         var cmenu = { };
48891         if (!this.disable.cleanStyles) {
48892             cmenu = {
48893                 cls: 'x-btn-icon x-btn-clear',
48894                 
48895                 menu : {
48896                     items : []
48897                 }
48898             };
48899             for (var i =0; i < this.cleanStyles.length; i++) {
48900                 cmenu.menu.items.push({
48901                     actiontype : this.cleanStyles[i],
48902                     html: 'Remove ' + this.cleanStyles[i],
48903                     handler: function(a,b) {
48904 //                        Roo.log(a);
48905 //                        Roo.log(b);
48906                         var c = Roo.get(editorcore.doc.body);
48907                         c.select('[style]').each(function(s) {
48908                             s.dom.style.removeProperty(a.actiontype);
48909                         });
48910                         editorcore.syncValue();
48911                     },
48912                     tabIndex:-1
48913                 });
48914             }
48915             cmenu.menu.items.push({
48916                 actiontype : 'tablewidths',
48917                 html: 'Remove Table Widths',
48918                 handler: function(a,b) {
48919                     editorcore.cleanTableWidths();
48920                     editorcore.syncValue();
48921                 },
48922                 tabIndex:-1
48923             });
48924             cmenu.menu.items.push({
48925                 actiontype : 'word',
48926                 html: 'Remove MS Word Formating',
48927                 handler: function(a,b) {
48928                     editorcore.cleanWord();
48929                     editorcore.syncValue();
48930                 },
48931                 tabIndex:-1
48932             });
48933             
48934             cmenu.menu.items.push({
48935                 actiontype : 'all',
48936                 html: 'Remove All Styles',
48937                 handler: function(a,b) {
48938                     
48939                     var c = Roo.get(editorcore.doc.body);
48940                     c.select('[style]').each(function(s) {
48941                         s.dom.removeAttribute('style');
48942                     });
48943                     editorcore.syncValue();
48944                 },
48945                 tabIndex:-1
48946             });
48947             
48948             cmenu.menu.items.push({
48949                 actiontype : 'all',
48950                 html: 'Remove All CSS Classes',
48951                 handler: function(a,b) {
48952                     
48953                     var c = Roo.get(editorcore.doc.body);
48954                     c.select('[class]').each(function(s) {
48955                         s.dom.removeAttribute('class');
48956                     });
48957                     editorcore.cleanWord();
48958                     editorcore.syncValue();
48959                 },
48960                 tabIndex:-1
48961             });
48962             
48963              cmenu.menu.items.push({
48964                 actiontype : 'tidy',
48965                 html: 'Tidy HTML Source',
48966                 handler: function(a,b) {
48967                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48968                     editorcore.syncValue();
48969                 },
48970                 tabIndex:-1
48971             });
48972             
48973             
48974             tb.add(cmenu);
48975         }
48976          
48977         if (!this.disable.specialElements) {
48978             var semenu = {
48979                 text: "Other;",
48980                 cls: 'x-edit-none',
48981                 menu : {
48982                     items : []
48983                 }
48984             };
48985             for (var i =0; i < this.specialElements.length; i++) {
48986                 semenu.menu.items.push(
48987                     Roo.apply({ 
48988                         handler: function(a,b) {
48989                             editor.insertAtCursor(this.ihtml);
48990                         }
48991                     }, this.specialElements[i])
48992                 );
48993                     
48994             }
48995             
48996             tb.add(semenu);
48997             
48998             
48999         }
49000          
49001         
49002         if (this.btns) {
49003             for(var i =0; i< this.btns.length;i++) {
49004                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49005                 b.cls =  'x-edit-none';
49006                 
49007                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49008                     b.cls += ' x-init-enable';
49009                 }
49010                 
49011                 b.scope = editorcore;
49012                 tb.add(b);
49013             }
49014         
49015         }
49016         
49017         
49018         
49019         // disable everything...
49020         
49021         this.tb.items.each(function(item){
49022             
49023            if(
49024                 item.id != editorcore.frameId+ '-sourceedit' && 
49025                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49026             ){
49027                 
49028                 item.disable();
49029             }
49030         });
49031         this.rendered = true;
49032         
49033         // the all the btns;
49034         editor.on('editorevent', this.updateToolbar, this);
49035         // other toolbars need to implement this..
49036         //editor.on('editmodechange', this.updateToolbar, this);
49037     },
49038     
49039     
49040     relayBtnCmd : function(btn) {
49041         this.editorcore.relayCmd(btn.cmd);
49042     },
49043     // private used internally
49044     createLink : function(){
49045         Roo.log("create link?");
49046         var url = prompt(this.createLinkText, this.defaultLinkValue);
49047         if(url && url != 'http:/'+'/'){
49048             this.editorcore.relayCmd('createlink', url);
49049         }
49050     },
49051
49052     
49053     /**
49054      * Protected method that will not generally be called directly. It triggers
49055      * a toolbar update by reading the markup state of the current selection in the editor.
49056      */
49057     updateToolbar: function(){
49058
49059         if(!this.editorcore.activated){
49060             this.editor.onFirstFocus();
49061             return;
49062         }
49063
49064         var btns = this.tb.items.map, 
49065             doc = this.editorcore.doc,
49066             frameId = this.editorcore.frameId;
49067
49068         if(!this.disable.font && !Roo.isSafari){
49069             /*
49070             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49071             if(name != this.fontSelect.dom.value){
49072                 this.fontSelect.dom.value = name;
49073             }
49074             */
49075         }
49076         if(!this.disable.format){
49077             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49078             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49079             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49080             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49081         }
49082         if(!this.disable.alignments){
49083             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49084             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49085             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49086         }
49087         if(!Roo.isSafari && !this.disable.lists){
49088             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49089             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49090         }
49091         
49092         var ans = this.editorcore.getAllAncestors();
49093         if (this.formatCombo) {
49094             
49095             
49096             var store = this.formatCombo.store;
49097             this.formatCombo.setValue("");
49098             for (var i =0; i < ans.length;i++) {
49099                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49100                     // select it..
49101                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49102                     break;
49103                 }
49104             }
49105         }
49106         
49107         
49108         
49109         // hides menus... - so this cant be on a menu...
49110         Roo.menu.MenuMgr.hideAll();
49111
49112         //this.editorsyncValue();
49113     },
49114    
49115     
49116     createFontOptions : function(){
49117         var buf = [], fs = this.fontFamilies, ff, lc;
49118         
49119         
49120         
49121         for(var i = 0, len = fs.length; i< len; i++){
49122             ff = fs[i];
49123             lc = ff.toLowerCase();
49124             buf.push(
49125                 '<option value="',lc,'" style="font-family:',ff,';"',
49126                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49127                     ff,
49128                 '</option>'
49129             );
49130         }
49131         return buf.join('');
49132     },
49133     
49134     toggleSourceEdit : function(sourceEditMode){
49135         
49136         Roo.log("toolbar toogle");
49137         if(sourceEditMode === undefined){
49138             sourceEditMode = !this.sourceEditMode;
49139         }
49140         this.sourceEditMode = sourceEditMode === true;
49141         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49142         // just toggle the button?
49143         if(btn.pressed !== this.sourceEditMode){
49144             btn.toggle(this.sourceEditMode);
49145             return;
49146         }
49147         
49148         if(sourceEditMode){
49149             Roo.log("disabling buttons");
49150             this.tb.items.each(function(item){
49151                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49152                     item.disable();
49153                 }
49154             });
49155           
49156         }else{
49157             Roo.log("enabling buttons");
49158             if(this.editorcore.initialized){
49159                 this.tb.items.each(function(item){
49160                     item.enable();
49161                 });
49162             }
49163             
49164         }
49165         Roo.log("calling toggole on editor");
49166         // tell the editor that it's been pressed..
49167         this.editor.toggleSourceEdit(sourceEditMode);
49168        
49169     },
49170      /**
49171      * Object collection of toolbar tooltips for the buttons in the editor. The key
49172      * is the command id associated with that button and the value is a valid QuickTips object.
49173      * For example:
49174 <pre><code>
49175 {
49176     bold : {
49177         title: 'Bold (Ctrl+B)',
49178         text: 'Make the selected text bold.',
49179         cls: 'x-html-editor-tip'
49180     },
49181     italic : {
49182         title: 'Italic (Ctrl+I)',
49183         text: 'Make the selected text italic.',
49184         cls: 'x-html-editor-tip'
49185     },
49186     ...
49187 </code></pre>
49188     * @type Object
49189      */
49190     buttonTips : {
49191         bold : {
49192             title: 'Bold (Ctrl+B)',
49193             text: 'Make the selected text bold.',
49194             cls: 'x-html-editor-tip'
49195         },
49196         italic : {
49197             title: 'Italic (Ctrl+I)',
49198             text: 'Make the selected text italic.',
49199             cls: 'x-html-editor-tip'
49200         },
49201         underline : {
49202             title: 'Underline (Ctrl+U)',
49203             text: 'Underline the selected text.',
49204             cls: 'x-html-editor-tip'
49205         },
49206         strikethrough : {
49207             title: 'Strikethrough',
49208             text: 'Strikethrough the selected text.',
49209             cls: 'x-html-editor-tip'
49210         },
49211         increasefontsize : {
49212             title: 'Grow Text',
49213             text: 'Increase the font size.',
49214             cls: 'x-html-editor-tip'
49215         },
49216         decreasefontsize : {
49217             title: 'Shrink Text',
49218             text: 'Decrease the font size.',
49219             cls: 'x-html-editor-tip'
49220         },
49221         backcolor : {
49222             title: 'Text Highlight Color',
49223             text: 'Change the background color of the selected text.',
49224             cls: 'x-html-editor-tip'
49225         },
49226         forecolor : {
49227             title: 'Font Color',
49228             text: 'Change the color of the selected text.',
49229             cls: 'x-html-editor-tip'
49230         },
49231         justifyleft : {
49232             title: 'Align Text Left',
49233             text: 'Align text to the left.',
49234             cls: 'x-html-editor-tip'
49235         },
49236         justifycenter : {
49237             title: 'Center Text',
49238             text: 'Center text in the editor.',
49239             cls: 'x-html-editor-tip'
49240         },
49241         justifyright : {
49242             title: 'Align Text Right',
49243             text: 'Align text to the right.',
49244             cls: 'x-html-editor-tip'
49245         },
49246         insertunorderedlist : {
49247             title: 'Bullet List',
49248             text: 'Start a bulleted list.',
49249             cls: 'x-html-editor-tip'
49250         },
49251         insertorderedlist : {
49252             title: 'Numbered List',
49253             text: 'Start a numbered list.',
49254             cls: 'x-html-editor-tip'
49255         },
49256         createlink : {
49257             title: 'Hyperlink',
49258             text: 'Make the selected text a hyperlink.',
49259             cls: 'x-html-editor-tip'
49260         },
49261         sourceedit : {
49262             title: 'Source Edit',
49263             text: 'Switch to source editing mode.',
49264             cls: 'x-html-editor-tip'
49265         }
49266     },
49267     // private
49268     onDestroy : function(){
49269         if(this.rendered){
49270             
49271             this.tb.items.each(function(item){
49272                 if(item.menu){
49273                     item.menu.removeAll();
49274                     if(item.menu.el){
49275                         item.menu.el.destroy();
49276                     }
49277                 }
49278                 item.destroy();
49279             });
49280              
49281         }
49282     },
49283     onFirstFocus: function() {
49284         this.tb.items.each(function(item){
49285            item.enable();
49286         });
49287     }
49288 });
49289
49290
49291
49292
49293 // <script type="text/javascript">
49294 /*
49295  * Based on
49296  * Ext JS Library 1.1.1
49297  * Copyright(c) 2006-2007, Ext JS, LLC.
49298  *  
49299  
49300  */
49301
49302  
49303 /**
49304  * @class Roo.form.HtmlEditor.ToolbarContext
49305  * Context Toolbar
49306  * 
49307  * Usage:
49308  *
49309  new Roo.form.HtmlEditor({
49310     ....
49311     toolbars : [
49312         { xtype: 'ToolbarStandard', styles : {} }
49313         { xtype: 'ToolbarContext', disable : {} }
49314     ]
49315 })
49316
49317      
49318  * 
49319  * @config : {Object} disable List of elements to disable.. (not done yet.)
49320  * @config : {Object} styles  Map of styles available.
49321  * 
49322  */
49323
49324 Roo.form.HtmlEditor.ToolbarContext = function(config)
49325 {
49326     
49327     Roo.apply(this, config);
49328     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49329     // dont call parent... till later.
49330     this.styles = this.styles || {};
49331 }
49332
49333  
49334
49335 Roo.form.HtmlEditor.ToolbarContext.types = {
49336     'IMG' : [
49337         {
49338             name : 'width',
49339             title: "Width",
49340             width: 40
49341         },
49342         {
49343             name : 'height',
49344             title: "Height",
49345             width: 40
49346         },
49347         {
49348             name : 'align',
49349             title: "Align",
49350             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49351             width : 80
49352             
49353         },
49354         {
49355             name : 'border',
49356             title: "Border",
49357             width: 40
49358         },
49359         {
49360             name : 'alt',
49361             title: "Alt",
49362             width: 120
49363         },
49364         {
49365             name : 'src',
49366             title: "Src",
49367             width: 220
49368         }
49369         
49370     ],
49371     
49372     'FIGURE' : [
49373         {
49374             name : 'align',
49375             title: "Align",
49376             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49377             width : 80  
49378         }
49379     ],
49380     'A' : [
49381         {
49382             name : 'name',
49383             title: "Name",
49384             width: 50
49385         },
49386         {
49387             name : 'target',
49388             title: "Target",
49389             width: 120
49390         },
49391         {
49392             name : 'href',
49393             title: "Href",
49394             width: 220
49395         } // border?
49396         
49397     ],
49398     
49399     'INPUT' : [
49400         {
49401             name : 'name',
49402             title: "name",
49403             width: 120
49404         },
49405         {
49406             name : 'value',
49407             title: "Value",
49408             width: 120
49409         },
49410         {
49411             name : 'width',
49412             title: "Width",
49413             width: 40
49414         }
49415     ],
49416     'LABEL' : [
49417          {
49418             name : 'for',
49419             title: "For",
49420             width: 120
49421         }
49422     ],
49423     'TEXTAREA' : [
49424         {
49425             name : 'name',
49426             title: "name",
49427             width: 120
49428         },
49429         {
49430             name : 'rows',
49431             title: "Rows",
49432             width: 20
49433         },
49434         {
49435             name : 'cols',
49436             title: "Cols",
49437             width: 20
49438         }
49439     ],
49440     'SELECT' : [
49441         {
49442             name : 'name',
49443             title: "name",
49444             width: 120
49445         },
49446         {
49447             name : 'selectoptions',
49448             title: "Options",
49449             width: 200
49450         }
49451     ],
49452     
49453     // should we really allow this??
49454     // should this just be 
49455     'BODY' : [
49456         
49457         {
49458             name : 'title',
49459             title: "Title",
49460             width: 200,
49461             disabled : true
49462         }
49463     ],
49464  
49465     '*' : [
49466         // empty.
49467     ]
49468
49469 };
49470
49471 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49472 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49473
49474 Roo.form.HtmlEditor.ToolbarContext.options = {
49475         'font-family'  : [ 
49476                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49477                 [ 'Courier New', 'Courier New'],
49478                 [ 'Tahoma', 'Tahoma'],
49479                 [ 'Times New Roman,serif', 'Times'],
49480                 [ 'Verdana','Verdana' ]
49481         ]
49482 };
49483
49484 // fixme - these need to be configurable..
49485  
49486
49487 //Roo.form.HtmlEditor.ToolbarContext.types
49488
49489
49490 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49491     
49492     tb: false,
49493     
49494     rendered: false,
49495     
49496     editor : false,
49497     editorcore : false,
49498     /**
49499      * @cfg {Object} disable  List of toolbar elements to disable
49500          
49501      */
49502     disable : false,
49503     /**
49504      * @cfg {Object} styles List of styles 
49505      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49506      *
49507      * These must be defined in the page, so they get rendered correctly..
49508      * .headline { }
49509      * TD.underline { }
49510      * 
49511      */
49512     styles : false,
49513     
49514     options: false,
49515     
49516     toolbars : false,
49517     
49518     init : function(editor)
49519     {
49520         this.editor = editor;
49521         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49522         var editorcore = this.editorcore;
49523         
49524         var fid = editorcore.frameId;
49525         var etb = this;
49526         function btn(id, toggle, handler){
49527             var xid = fid + '-'+ id ;
49528             return {
49529                 id : xid,
49530                 cmd : id,
49531                 cls : 'x-btn-icon x-edit-'+id,
49532                 enableToggle:toggle !== false,
49533                 scope: editorcore, // was editor...
49534                 handler:handler||editorcore.relayBtnCmd,
49535                 clickEvent:'mousedown',
49536                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49537                 tabIndex:-1
49538             };
49539         }
49540         // create a new element.
49541         var wdiv = editor.wrap.createChild({
49542                 tag: 'div'
49543             }, editor.wrap.dom.firstChild.nextSibling, true);
49544         
49545         // can we do this more than once??
49546         
49547          // stop form submits
49548       
49549  
49550         // disable everything...
49551         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49552         this.toolbars = {};
49553            
49554         for (var i in  ty) {
49555           
49556             this.toolbars[i] = this.buildToolbar(ty[i],i);
49557         }
49558         this.tb = this.toolbars.BODY;
49559         this.tb.el.show();
49560         this.buildFooter();
49561         this.footer.show();
49562         editor.on('hide', function( ) { this.footer.hide() }, this);
49563         editor.on('show', function( ) { this.footer.show() }, this);
49564         
49565          
49566         this.rendered = true;
49567         
49568         // the all the btns;
49569         editor.on('editorevent', this.updateToolbar, this);
49570         // other toolbars need to implement this..
49571         //editor.on('editmodechange', this.updateToolbar, this);
49572     },
49573     
49574     
49575     
49576     /**
49577      * Protected method that will not generally be called directly. It triggers
49578      * a toolbar update by reading the markup state of the current selection in the editor.
49579      *
49580      * Note you can force an update by calling on('editorevent', scope, false)
49581      */
49582     updateToolbar: function(editor ,ev, sel)
49583     {
49584         
49585         if (ev) {
49586             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49587         }
49588         
49589         //Roo.log(ev);
49590         // capture mouse up - this is handy for selecting images..
49591         // perhaps should go somewhere else...
49592         if(!this.editorcore.activated){
49593              this.editor.onFirstFocus();
49594             return;
49595         }
49596         Roo.log(ev ? ev.target : 'NOTARGET');
49597         
49598         
49599         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49600         // selectNode - might want to handle IE?
49601         
49602         
49603         
49604         if (ev &&
49605             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49606             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49607             // they have click on an image...
49608             // let's see if we can change the selection...
49609             sel = ev.target;
49610             
49611             // this triggers looping?
49612             //this.editorcore.selectNode(sel);
49613              
49614         }  
49615         
49616       
49617         //var updateFooter = sel ? false : true; 
49618         
49619         
49620         var ans = this.editorcore.getAllAncestors();
49621         
49622         // pick
49623         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49624         
49625         if (!sel) { 
49626             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49627             sel = sel ? sel : this.editorcore.doc.body;
49628             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49629             
49630         }
49631         
49632         var tn = sel.tagName.toUpperCase();
49633         var lastSel = this.tb.selectedNode;
49634         this.tb.selectedNode = sel;
49635         var left_label = tn;
49636         
49637         // ok see if we are editing a block?
49638         var sel_el = Roo.get(sel);
49639         var db = false;
49640         // you are not actually selecting the block.
49641         if (sel && sel.hasAttribute('data-block')) {
49642             db = sel;
49643         } else if (sel && !sel.hasAttribute('contenteditable')) {
49644             db = sel_el.findParent('[data-block]');
49645             var cepar = sel_el.findParent('[contenteditable=true]');
49646             if (db && cepar && cepar.tagName != 'BODY') {
49647                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49648             }   
49649         }
49650         
49651         
49652         var block = false;
49653         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49654         if (db) {
49655             block = Roo.htmleditor.Block.factory(db);
49656             if (block) {
49657                 tn = 'BLOCK.' + db.getAttribute('data-block');
49658                 
49659                 //this.editorcore.selectNode(db);
49660                 if (typeof(this.toolbars[tn]) == 'undefined') {
49661                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49662                 }
49663                 this.toolbars[tn].selectedNode = db;
49664                 left_label = block.friendly_name;
49665                 ans = this.editorcore.getAllAncestors();
49666             }
49667             
49668                 
49669             
49670         }
49671         
49672         
49673         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49674             return; // no change?
49675         }
49676         
49677         
49678           
49679         this.tb.el.hide();
49680         ///console.log("show: " + tn);
49681         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49682         
49683         this.tb.el.show();
49684         // update name
49685         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49686         
49687         
49688         // update attributes
49689         if (block) {
49690              
49691             this.tb.fields.each(function(e) {
49692                 e.setValue(block[e.name]);
49693             });
49694             
49695             
49696         } else  if (this.tb.fields && this.tb.selectedNode) {
49697             this.tb.fields.each( function(e) {
49698                 if (e.stylename) {
49699                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49700                     return;
49701                 } 
49702                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49703             }, this);
49704             this.updateToolbarStyles(this.tb.selectedNode);  
49705         }
49706         
49707         
49708        
49709         Roo.menu.MenuMgr.hideAll();
49710
49711         
49712         
49713     
49714         // update the footer
49715         //
49716         this.updateFooter(ans);
49717              
49718     },
49719     
49720     updateToolbarStyles : function(sel)
49721     {
49722         var hasStyles = false;
49723         for(var i in this.styles) {
49724             hasStyles = true;
49725             break;
49726         }
49727         
49728         // update styles
49729         if (hasStyles && this.tb.hasStyles) { 
49730             var st = this.tb.fields.item(0);
49731             
49732             st.store.removeAll();
49733             var cn = sel.className.split(/\s+/);
49734             
49735             var avs = [];
49736             if (this.styles['*']) {
49737                 
49738                 Roo.each(this.styles['*'], function(v) {
49739                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49740                 });
49741             }
49742             if (this.styles[tn]) { 
49743                 Roo.each(this.styles[tn], function(v) {
49744                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49745                 });
49746             }
49747             
49748             st.store.loadData(avs);
49749             st.collapse();
49750             st.setValue(cn);
49751         }
49752     },
49753     
49754      
49755     updateFooter : function(ans)
49756     {
49757         var html = '';
49758         if (ans === false) {
49759             this.footDisp.dom.innerHTML = '';
49760             return;
49761         }
49762         
49763         this.footerEls = ans.reverse();
49764         Roo.each(this.footerEls, function(a,i) {
49765             if (!a) { return; }
49766             html += html.length ? ' &gt; '  :  '';
49767             
49768             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49769             
49770         });
49771        
49772         // 
49773         var sz = this.footDisp.up('td').getSize();
49774         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49775         this.footDisp.dom.style.marginLeft = '5px';
49776         
49777         this.footDisp.dom.style.overflow = 'hidden';
49778         
49779         this.footDisp.dom.innerHTML = html;
49780             
49781         
49782     },
49783    
49784        
49785     // private
49786     onDestroy : function(){
49787         if(this.rendered){
49788             
49789             this.tb.items.each(function(item){
49790                 if(item.menu){
49791                     item.menu.removeAll();
49792                     if(item.menu.el){
49793                         item.menu.el.destroy();
49794                     }
49795                 }
49796                 item.destroy();
49797             });
49798              
49799         }
49800     },
49801     onFirstFocus: function() {
49802         // need to do this for all the toolbars..
49803         this.tb.items.each(function(item){
49804            item.enable();
49805         });
49806     },
49807     buildToolbar: function(tlist, nm, friendly_name, block)
49808     {
49809         var editor = this.editor;
49810         var editorcore = this.editorcore;
49811          // create a new element.
49812         var wdiv = editor.wrap.createChild({
49813                 tag: 'div'
49814             }, editor.wrap.dom.firstChild.nextSibling, true);
49815         
49816        
49817         var tb = new Roo.Toolbar(wdiv);
49818         ///this.tb = tb; // << this sets the active toolbar..
49819         if (tlist === false && block) {
49820             tlist = block.contextMenu(this);
49821         }
49822         
49823         tb.hasStyles = false;
49824         tb.name = nm;
49825         
49826         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49827         
49828         var styles = Array.from(this.styles);
49829         
49830         
49831         // styles...
49832         if (styles && styles.length) {
49833             tb.hasStyles = true;
49834             // this needs a multi-select checkbox...
49835             tb.addField( new Roo.form.ComboBox({
49836                 store: new Roo.data.SimpleStore({
49837                     id : 'val',
49838                     fields: ['val', 'selected'],
49839                     data : [] 
49840                 }),
49841                 name : '-roo-edit-className',
49842                 attrname : 'className',
49843                 displayField: 'val',
49844                 typeAhead: false,
49845                 mode: 'local',
49846                 editable : false,
49847                 triggerAction: 'all',
49848                 emptyText:'Select Style',
49849                 selectOnFocus:true,
49850                 width: 130,
49851                 listeners : {
49852                     'select': function(c, r, i) {
49853                         // initial support only for on class per el..
49854                         tb.selectedNode.className =  r ? r.get('val') : '';
49855                         editorcore.syncValue();
49856                     }
49857                 }
49858     
49859             }));
49860         }
49861         
49862         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49863         
49864         
49865         for (var i = 0; i < tlist.length; i++) {
49866             
49867             // newer versions will use xtype cfg to create menus.
49868             if (typeof(tlist[i].xtype) != 'undefined') {
49869                 
49870                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49871                 
49872                 
49873                 continue;
49874             }
49875             
49876             var item = tlist[i];
49877             tb.add(item.title + ":&nbsp;");
49878             
49879             
49880             //optname == used so you can configure the options available..
49881             var opts = item.opts ? item.opts : false;
49882             if (item.optname) { // use the b
49883                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49884            
49885             }
49886             
49887             if (opts) {
49888                 // opts == pulldown..
49889                 tb.addField( new Roo.form.ComboBox({
49890                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49891                         id : 'val',
49892                         fields: ['val', 'display'],
49893                         data : opts  
49894                     }),
49895                     name : '-roo-edit-' + tlist[i].name,
49896                     
49897                     attrname : tlist[i].name,
49898                     stylename : item.style ? item.style : false,
49899                     
49900                     displayField: item.displayField ? item.displayField : 'val',
49901                     valueField :  'val',
49902                     typeAhead: false,
49903                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
49904                     editable : false,
49905                     triggerAction: 'all',
49906                     emptyText:'Select',
49907                     selectOnFocus:true,
49908                     width: item.width ? item.width  : 130,
49909                     listeners : {
49910                         'select': function(c, r, i) {
49911                             if (tb.selectedNode.hasAttribute('data-block')) {
49912                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49913                                 b[c.attrname] = r.get('val');
49914                                 b.updateElement(tb.selectedNode);
49915                                 editorcore.syncValue();
49916                                 return;
49917                             }
49918                             
49919                             if (c.stylename) {
49920                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49921                                 editorcore.syncValue();
49922                                 return;
49923                             }
49924                             if (r === false) {
49925                                 tb.selectedNode.removeAttribute(c.attrname);
49926                                 editorcore.syncValue();
49927                                 return;
49928                             }
49929                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49930                             editorcore.syncValue();
49931                         }
49932                     }
49933
49934                 }));
49935                 continue;
49936                     
49937                  
49938                 /*
49939                 tb.addField( new Roo.form.TextField({
49940                     name: i,
49941                     width: 100,
49942                     //allowBlank:false,
49943                     value: ''
49944                 }));
49945                 continue;
49946                 */
49947             }
49948             tb.addField( new Roo.form.TextField({
49949                 name: '-roo-edit-' + tlist[i].name,
49950                 attrname : tlist[i].name,
49951                 
49952                 width: item.width,
49953                 //allowBlank:true,
49954                 value: '',
49955                 listeners: {
49956                     'change' : function(f, nv, ov) {
49957                         
49958                         if (tb.selectedNode.hasAttribute('data-block')) {
49959                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49960                             b[f.attrname] = nv;
49961                             b.updateElement(tb.selectedNode);
49962                             editorcore.syncValue();
49963                             return;
49964                         }
49965                         
49966                         tb.selectedNode.setAttribute(f.attrname, nv);
49967                         editorcore.syncValue();
49968                     }
49969                 }
49970             }));
49971              
49972         }
49973         
49974         var _this = this;
49975         
49976         if(nm == 'BODY'){
49977             tb.addSeparator();
49978         
49979             tb.addButton( {
49980                 text: 'Stylesheets',
49981
49982                 listeners : {
49983                     click : function ()
49984                     {
49985                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49986                     }
49987                 }
49988             });
49989         }
49990         
49991         tb.addFill();
49992         tb.addButton({
49993             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49994     
49995             listeners : {
49996                 click : function ()
49997                 {
49998                     var sn = tb.selectedNode;
49999                     if (block) {
50000                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50001                         
50002                     }
50003                     if (!sn) {
50004                         return;
50005                     }
50006                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50007                     if (sn.hasAttribute('data-block')) {
50008                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50009                         sn.parentNode.removeChild(sn);
50010                         
50011                     } else if (sn && sn.tagName != 'BODY') {
50012                         // remove and keep parents.
50013                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50014                         a.removeTag(sn);
50015                     }
50016                     
50017                     
50018                     var range = editorcore.createRange();
50019         
50020                     range.setStart(stn,0);
50021                     range.setEnd(stn,0); 
50022                     var selection = editorcore.getSelection();
50023                     selection.removeAllRanges();
50024                     selection.addRange(range);
50025                     
50026                     
50027                     //_this.updateToolbar(null, null, pn);
50028                     _this.updateToolbar(null, null, null);
50029                     _this.updateFooter(false);
50030                     
50031                 }
50032             }
50033             
50034                     
50035                 
50036             
50037         });
50038         
50039         
50040         tb.el.on('click', function(e){
50041             e.preventDefault(); // what does this do?
50042         });
50043         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50044         tb.el.hide();
50045         
50046         // dont need to disable them... as they will get hidden
50047         return tb;
50048          
50049         
50050     },
50051     buildFooter : function()
50052     {
50053         
50054         var fel = this.editor.wrap.createChild();
50055         this.footer = new Roo.Toolbar(fel);
50056         // toolbar has scrolly on left / right?
50057         var footDisp= new Roo.Toolbar.Fill();
50058         var _t = this;
50059         this.footer.add(
50060             {
50061                 text : '&lt;',
50062                 xtype: 'Button',
50063                 handler : function() {
50064                     _t.footDisp.scrollTo('left',0,true)
50065                 }
50066             }
50067         );
50068         this.footer.add( footDisp );
50069         this.footer.add( 
50070             {
50071                 text : '&gt;',
50072                 xtype: 'Button',
50073                 handler : function() {
50074                     // no animation..
50075                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50076                 }
50077             }
50078         );
50079         var fel = Roo.get(footDisp.el);
50080         fel.addClass('x-editor-context');
50081         this.footDispWrap = fel; 
50082         this.footDispWrap.overflow  = 'hidden';
50083         
50084         this.footDisp = fel.createChild();
50085         this.footDispWrap.on('click', this.onContextClick, this)
50086         
50087         
50088     },
50089     // when the footer contect changes
50090     onContextClick : function (ev,dom)
50091     {
50092         ev.preventDefault();
50093         var  cn = dom.className;
50094         //Roo.log(cn);
50095         if (!cn.match(/x-ed-loc-/)) {
50096             return;
50097         }
50098         var n = cn.split('-').pop();
50099         var ans = this.footerEls;
50100         var sel = ans[n];
50101         
50102          // pick
50103         var range = this.editorcore.createRange();
50104         
50105         range.selectNodeContents(sel);
50106         //range.selectNode(sel);
50107         
50108         
50109         var selection = this.editorcore.getSelection();
50110         selection.removeAllRanges();
50111         selection.addRange(range);
50112         
50113         
50114         
50115         this.updateToolbar(null, null, sel);
50116         
50117         
50118     }
50119     
50120     
50121     
50122     
50123     
50124 });
50125
50126
50127
50128
50129
50130 /*
50131  * Based on:
50132  * Ext JS Library 1.1.1
50133  * Copyright(c) 2006-2007, Ext JS, LLC.
50134  *
50135  * Originally Released Under LGPL - original licence link has changed is not relivant.
50136  *
50137  * Fork - LGPL
50138  * <script type="text/javascript">
50139  */
50140  
50141 /**
50142  * @class Roo.form.BasicForm
50143  * @extends Roo.util.Observable
50144  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50145  * @constructor
50146  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50147  * @param {Object} config Configuration options
50148  */
50149 Roo.form.BasicForm = function(el, config){
50150     this.allItems = [];
50151     this.childForms = [];
50152     Roo.apply(this, config);
50153     /*
50154      * The Roo.form.Field items in this form.
50155      * @type MixedCollection
50156      */
50157      
50158      
50159     this.items = new Roo.util.MixedCollection(false, function(o){
50160         return o.id || (o.id = Roo.id());
50161     });
50162     this.addEvents({
50163         /**
50164          * @event beforeaction
50165          * Fires before any action is performed. Return false to cancel the action.
50166          * @param {Form} this
50167          * @param {Action} action The action to be performed
50168          */
50169         beforeaction: true,
50170         /**
50171          * @event actionfailed
50172          * Fires when an action fails.
50173          * @param {Form} this
50174          * @param {Action} action The action that failed
50175          */
50176         actionfailed : true,
50177         /**
50178          * @event actioncomplete
50179          * Fires when an action is completed.
50180          * @param {Form} this
50181          * @param {Action} action The action that completed
50182          */
50183         actioncomplete : true
50184     });
50185     if(el){
50186         this.initEl(el);
50187     }
50188     Roo.form.BasicForm.superclass.constructor.call(this);
50189     
50190     Roo.form.BasicForm.popover.apply();
50191 };
50192
50193 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50194     /**
50195      * @cfg {String} method
50196      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50197      */
50198     /**
50199      * @cfg {DataReader} reader
50200      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50201      * This is optional as there is built-in support for processing JSON.
50202      */
50203     /**
50204      * @cfg {DataReader} errorReader
50205      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50206      * This is completely optional as there is built-in support for processing JSON.
50207      */
50208     /**
50209      * @cfg {String} url
50210      * The URL to use for form actions if one isn't supplied in the action options.
50211      */
50212     /**
50213      * @cfg {Boolean} fileUpload
50214      * Set to true if this form is a file upload.
50215      */
50216      
50217     /**
50218      * @cfg {Object} baseParams
50219      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50220      */
50221      /**
50222      
50223     /**
50224      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50225      */
50226     timeout: 30,
50227
50228     // private
50229     activeAction : null,
50230
50231     /**
50232      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50233      * or setValues() data instead of when the form was first created.
50234      */
50235     trackResetOnLoad : false,
50236     
50237     
50238     /**
50239      * childForms - used for multi-tab forms
50240      * @type {Array}
50241      */
50242     childForms : false,
50243     
50244     /**
50245      * allItems - full list of fields.
50246      * @type {Array}
50247      */
50248     allItems : false,
50249     
50250     /**
50251      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50252      * element by passing it or its id or mask the form itself by passing in true.
50253      * @type Mixed
50254      */
50255     waitMsgTarget : false,
50256     
50257     /**
50258      * @type Boolean
50259      */
50260     disableMask : false,
50261     
50262     /**
50263      * @cfg {Boolean} errorMask (true|false) default false
50264      */
50265     errorMask : false,
50266     
50267     /**
50268      * @cfg {Number} maskOffset Default 100
50269      */
50270     maskOffset : 100,
50271
50272     // private
50273     initEl : function(el){
50274         this.el = Roo.get(el);
50275         this.id = this.el.id || Roo.id();
50276         this.el.on('submit', this.onSubmit, this);
50277         this.el.addClass('x-form');
50278     },
50279
50280     // private
50281     onSubmit : function(e){
50282         e.stopEvent();
50283     },
50284
50285     /**
50286      * Returns true if client-side validation on the form is successful.
50287      * @return Boolean
50288      */
50289     isValid : function(){
50290         var valid = true;
50291         var target = false;
50292         this.items.each(function(f){
50293             if(f.validate()){
50294                 return;
50295             }
50296             
50297             valid = false;
50298                 
50299             if(!target && f.el.isVisible(true)){
50300                 target = f;
50301             }
50302         });
50303         
50304         if(this.errorMask && !valid){
50305             Roo.form.BasicForm.popover.mask(this, target);
50306         }
50307         
50308         return valid;
50309     },
50310     /**
50311      * Returns array of invalid form fields.
50312      * @return Array
50313      */
50314     
50315     invalidFields : function()
50316     {
50317         var ret = [];
50318         this.items.each(function(f){
50319             if(f.validate()){
50320                 return;
50321             }
50322             ret.push(f);
50323             
50324         });
50325         
50326         return ret;
50327     },
50328     
50329     
50330     /**
50331      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50332      * @return Boolean
50333      */
50334     isDirty : function(){
50335         var dirty = false;
50336         this.items.each(function(f){
50337            if(f.isDirty()){
50338                dirty = true;
50339                return false;
50340            }
50341         });
50342         return dirty;
50343     },
50344     
50345     /**
50346      * Returns true if any fields in this form have changed since their original load. (New version)
50347      * @return Boolean
50348      */
50349     
50350     hasChanged : function()
50351     {
50352         var dirty = false;
50353         this.items.each(function(f){
50354            if(f.hasChanged()){
50355                dirty = true;
50356                return false;
50357            }
50358         });
50359         return dirty;
50360         
50361     },
50362     /**
50363      * Resets all hasChanged to 'false' -
50364      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50365      * So hasChanged storage is only to be used for this purpose
50366      * @return Boolean
50367      */
50368     resetHasChanged : function()
50369     {
50370         this.items.each(function(f){
50371            f.resetHasChanged();
50372         });
50373         
50374     },
50375     
50376     
50377     /**
50378      * Performs a predefined action (submit or load) or custom actions you define on this form.
50379      * @param {String} actionName The name of the action type
50380      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50381      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50382      * accept other config options):
50383      * <pre>
50384 Property          Type             Description
50385 ----------------  ---------------  ----------------------------------------------------------------------------------
50386 url               String           The url for the action (defaults to the form's url)
50387 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50388 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50389 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50390                                    validate the form on the client (defaults to false)
50391      * </pre>
50392      * @return {BasicForm} this
50393      */
50394     doAction : function(action, options){
50395         if(typeof action == 'string'){
50396             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50397         }
50398         if(this.fireEvent('beforeaction', this, action) !== false){
50399             this.beforeAction(action);
50400             action.run.defer(100, action);
50401         }
50402         return this;
50403     },
50404
50405     /**
50406      * Shortcut to do a submit action.
50407      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50408      * @return {BasicForm} this
50409      */
50410     submit : function(options){
50411         this.doAction('submit', options);
50412         return this;
50413     },
50414
50415     /**
50416      * Shortcut to do a load action.
50417      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50418      * @return {BasicForm} this
50419      */
50420     load : function(options){
50421         this.doAction('load', options);
50422         return this;
50423     },
50424
50425     /**
50426      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50427      * @param {Record} record The record to edit
50428      * @return {BasicForm} this
50429      */
50430     updateRecord : function(record){
50431         record.beginEdit();
50432         var fs = record.fields;
50433         fs.each(function(f){
50434             var field = this.findField(f.name);
50435             if(field){
50436                 record.set(f.name, field.getValue());
50437             }
50438         }, this);
50439         record.endEdit();
50440         return this;
50441     },
50442
50443     /**
50444      * Loads an Roo.data.Record into this form.
50445      * @param {Record} record The record to load
50446      * @return {BasicForm} this
50447      */
50448     loadRecord : function(record){
50449         this.setValues(record.data);
50450         return this;
50451     },
50452
50453     // private
50454     beforeAction : function(action){
50455         var o = action.options;
50456         
50457         if(!this.disableMask) {
50458             if(this.waitMsgTarget === true){
50459                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50460             }else if(this.waitMsgTarget){
50461                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50462                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50463             }else {
50464                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50465             }
50466         }
50467         
50468          
50469     },
50470
50471     // private
50472     afterAction : function(action, success){
50473         this.activeAction = null;
50474         var o = action.options;
50475         
50476         if(!this.disableMask) {
50477             if(this.waitMsgTarget === true){
50478                 this.el.unmask();
50479             }else if(this.waitMsgTarget){
50480                 this.waitMsgTarget.unmask();
50481             }else{
50482                 Roo.MessageBox.updateProgress(1);
50483                 Roo.MessageBox.hide();
50484             }
50485         }
50486         
50487         if(success){
50488             if(o.reset){
50489                 this.reset();
50490             }
50491             Roo.callback(o.success, o.scope, [this, action]);
50492             this.fireEvent('actioncomplete', this, action);
50493             
50494         }else{
50495             
50496             // failure condition..
50497             // we have a scenario where updates need confirming.
50498             // eg. if a locking scenario exists..
50499             // we look for { errors : { needs_confirm : true }} in the response.
50500             if (
50501                 (typeof(action.result) != 'undefined')  &&
50502                 (typeof(action.result.errors) != 'undefined')  &&
50503                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50504            ){
50505                 var _t = this;
50506                 Roo.MessageBox.confirm(
50507                     "Change requires confirmation",
50508                     action.result.errorMsg,
50509                     function(r) {
50510                         if (r != 'yes') {
50511                             return;
50512                         }
50513                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50514                     }
50515                     
50516                 );
50517                 
50518                 
50519                 
50520                 return;
50521             }
50522             
50523             Roo.callback(o.failure, o.scope, [this, action]);
50524             // show an error message if no failed handler is set..
50525             if (!this.hasListener('actionfailed')) {
50526                 Roo.MessageBox.alert("Error",
50527                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50528                         action.result.errorMsg :
50529                         "Saving Failed, please check your entries or try again"
50530                 );
50531             }
50532             
50533             this.fireEvent('actionfailed', this, action);
50534         }
50535         
50536     },
50537
50538     /**
50539      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50540      * @param {String} id The value to search for
50541      * @return Field
50542      */
50543     findField : function(id){
50544         var field = this.items.get(id);
50545         if(!field){
50546             this.items.each(function(f){
50547                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50548                     field = f;
50549                     return false;
50550                 }
50551             });
50552         }
50553         return field || null;
50554     },
50555
50556     /**
50557      * Add a secondary form to this one, 
50558      * Used to provide tabbed forms. One form is primary, with hidden values 
50559      * which mirror the elements from the other forms.
50560      * 
50561      * @param {Roo.form.Form} form to add.
50562      * 
50563      */
50564     addForm : function(form)
50565     {
50566        
50567         if (this.childForms.indexOf(form) > -1) {
50568             // already added..
50569             return;
50570         }
50571         this.childForms.push(form);
50572         var n = '';
50573         Roo.each(form.allItems, function (fe) {
50574             
50575             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50576             if (this.findField(n)) { // already added..
50577                 return;
50578             }
50579             var add = new Roo.form.Hidden({
50580                 name : n
50581             });
50582             add.render(this.el);
50583             
50584             this.add( add );
50585         }, this);
50586         
50587     },
50588     /**
50589      * Mark fields in this form invalid in bulk.
50590      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50591      * @return {BasicForm} this
50592      */
50593     markInvalid : function(errors){
50594         if(errors instanceof Array){
50595             for(var i = 0, len = errors.length; i < len; i++){
50596                 var fieldError = errors[i];
50597                 var f = this.findField(fieldError.id);
50598                 if(f){
50599                     f.markInvalid(fieldError.msg);
50600                 }
50601             }
50602         }else{
50603             var field, id;
50604             for(id in errors){
50605                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50606                     field.markInvalid(errors[id]);
50607                 }
50608             }
50609         }
50610         Roo.each(this.childForms || [], function (f) {
50611             f.markInvalid(errors);
50612         });
50613         
50614         return this;
50615     },
50616
50617     /**
50618      * Set values for fields in this form in bulk.
50619      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50620      * @return {BasicForm} this
50621      */
50622     setValues : function(values){
50623         if(values instanceof Array){ // array of objects
50624             for(var i = 0, len = values.length; i < len; i++){
50625                 var v = values[i];
50626                 var f = this.findField(v.id);
50627                 if(f){
50628                     f.setValue(v.value);
50629                     if(this.trackResetOnLoad){
50630                         f.originalValue = f.getValue();
50631                     }
50632                 }
50633             }
50634         }else{ // object hash
50635             var field, id;
50636             for(id in values){
50637                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50638                     
50639                     if (field.setFromData && 
50640                         field.valueField && 
50641                         field.displayField &&
50642                         // combos' with local stores can 
50643                         // be queried via setValue()
50644                         // to set their value..
50645                         (field.store && !field.store.isLocal)
50646                         ) {
50647                         // it's a combo
50648                         var sd = { };
50649                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50650                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50651                         field.setFromData(sd);
50652                         
50653                     } else {
50654                         field.setValue(values[id]);
50655                     }
50656                     
50657                     
50658                     if(this.trackResetOnLoad){
50659                         field.originalValue = field.getValue();
50660                     }
50661                 }
50662             }
50663         }
50664         this.resetHasChanged();
50665         
50666         
50667         Roo.each(this.childForms || [], function (f) {
50668             f.setValues(values);
50669             f.resetHasChanged();
50670         });
50671                 
50672         return this;
50673     },
50674  
50675     /**
50676      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50677      * they are returned as an array.
50678      * @param {Boolean} asString
50679      * @return {Object}
50680      */
50681     getValues : function(asString)
50682     {
50683         if (this.childForms) {
50684             // copy values from the child forms
50685             Roo.each(this.childForms, function (f) {
50686                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50687             }, this);
50688         }
50689         
50690         // use formdata
50691         if (typeof(FormData) != 'undefined' && asString !== true) {
50692             // this relies on a 'recent' version of chrome apparently...
50693             try {
50694                 var fd = (new FormData(this.el.dom)).entries();
50695                 var ret = {};
50696                 var ent = fd.next();
50697                 while (!ent.done) {
50698                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50699                     ent = fd.next();
50700                 };
50701                 return ret;
50702             } catch(e) {
50703                 
50704             }
50705             
50706         }
50707         
50708         
50709         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50710         if(asString === true){
50711             return fs;
50712         }
50713         return Roo.urlDecode(fs);
50714     },
50715     
50716     /**
50717      * Returns the fields in this form as an object with key/value pairs. 
50718      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50719      * @return {Object}
50720      */
50721     getFieldValues : function(with_hidden)
50722     {
50723         if (this.childForms) {
50724             // copy values from the child forms
50725             // should this call getFieldValues - probably not as we do not currently copy
50726             // hidden fields when we generate..
50727             Roo.each(this.childForms, function (f) {
50728                 this.setValues(f.getFieldValues());
50729             }, this);
50730         }
50731         
50732         var ret = {};
50733         this.items.each(function(f){
50734             
50735             if (f.readOnly) {
50736                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50737                         // if a subform contains a copy of them.
50738                         // if you have subforms with the same editable data, you will need to copy the data back
50739                         // and forth.
50740             }
50741             
50742             if (!f.getName()) {
50743                 return;
50744             }
50745             var v = f.getValue();
50746             if (f.inputType =='radio') {
50747                 if (typeof(ret[f.getName()]) == 'undefined') {
50748                     ret[f.getName()] = ''; // empty..
50749                 }
50750                 
50751                 if (!f.el.dom.checked) {
50752                     return;
50753                     
50754                 }
50755                 v = f.el.dom.value;
50756                 
50757             }
50758             
50759             // not sure if this supported any more..
50760             if ((typeof(v) == 'object') && f.getRawValue) {
50761                 v = f.getRawValue() ; // dates..
50762             }
50763             // combo boxes where name != hiddenName...
50764             if (f.name != f.getName()) {
50765                 ret[f.name] = f.getRawValue();
50766             }
50767             ret[f.getName()] = v;
50768         });
50769         
50770         return ret;
50771     },
50772
50773     /**
50774      * Clears all invalid messages in this form.
50775      * @return {BasicForm} this
50776      */
50777     clearInvalid : function(){
50778         this.items.each(function(f){
50779            f.clearInvalid();
50780         });
50781         
50782         Roo.each(this.childForms || [], function (f) {
50783             f.clearInvalid();
50784         });
50785         
50786         
50787         return this;
50788     },
50789
50790     /**
50791      * Resets this form.
50792      * @return {BasicForm} this
50793      */
50794     reset : function(){
50795         this.items.each(function(f){
50796             f.reset();
50797         });
50798         
50799         Roo.each(this.childForms || [], function (f) {
50800             f.reset();
50801         });
50802         this.resetHasChanged();
50803         
50804         return this;
50805     },
50806
50807     /**
50808      * Add Roo.form components to this form.
50809      * @param {Field} field1
50810      * @param {Field} field2 (optional)
50811      * @param {Field} etc (optional)
50812      * @return {BasicForm} this
50813      */
50814     add : function(){
50815         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50816         return this;
50817     },
50818
50819
50820     /**
50821      * Removes a field from the items collection (does NOT remove its markup).
50822      * @param {Field} field
50823      * @return {BasicForm} this
50824      */
50825     remove : function(field){
50826         this.items.remove(field);
50827         return this;
50828     },
50829
50830     /**
50831      * Looks at the fields in this form, checks them for an id attribute,
50832      * and calls applyTo on the existing dom element with that id.
50833      * @return {BasicForm} this
50834      */
50835     render : function(){
50836         this.items.each(function(f){
50837             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50838                 f.applyTo(f.id);
50839             }
50840         });
50841         return this;
50842     },
50843
50844     /**
50845      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50846      * @param {Object} values
50847      * @return {BasicForm} this
50848      */
50849     applyToFields : function(o){
50850         this.items.each(function(f){
50851            Roo.apply(f, o);
50852         });
50853         return this;
50854     },
50855
50856     /**
50857      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50858      * @param {Object} values
50859      * @return {BasicForm} this
50860      */
50861     applyIfToFields : function(o){
50862         this.items.each(function(f){
50863            Roo.applyIf(f, o);
50864         });
50865         return this;
50866     }
50867 });
50868
50869 // back compat
50870 Roo.BasicForm = Roo.form.BasicForm;
50871
50872 Roo.apply(Roo.form.BasicForm, {
50873     
50874     popover : {
50875         
50876         padding : 5,
50877         
50878         isApplied : false,
50879         
50880         isMasked : false,
50881         
50882         form : false,
50883         
50884         target : false,
50885         
50886         intervalID : false,
50887         
50888         maskEl : false,
50889         
50890         apply : function()
50891         {
50892             if(this.isApplied){
50893                 return;
50894             }
50895             
50896             this.maskEl = {
50897                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50898                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50899                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50900                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50901             };
50902             
50903             this.maskEl.top.enableDisplayMode("block");
50904             this.maskEl.left.enableDisplayMode("block");
50905             this.maskEl.bottom.enableDisplayMode("block");
50906             this.maskEl.right.enableDisplayMode("block");
50907             
50908             Roo.get(document.body).on('click', function(){
50909                 this.unmask();
50910             }, this);
50911             
50912             Roo.get(document.body).on('touchstart', function(){
50913                 this.unmask();
50914             }, this);
50915             
50916             this.isApplied = true
50917         },
50918         
50919         mask : function(form, target)
50920         {
50921             this.form = form;
50922             
50923             this.target = target;
50924             
50925             if(!this.form.errorMask || !target.el){
50926                 return;
50927             }
50928             
50929             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50930             
50931             var ot = this.target.el.calcOffsetsTo(scrollable);
50932             
50933             var scrollTo = ot[1] - this.form.maskOffset;
50934             
50935             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50936             
50937             scrollable.scrollTo('top', scrollTo);
50938             
50939             var el = this.target.wrap || this.target.el;
50940             
50941             var box = el.getBox();
50942             
50943             this.maskEl.top.setStyle('position', 'absolute');
50944             this.maskEl.top.setStyle('z-index', 10000);
50945             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50946             this.maskEl.top.setLeft(0);
50947             this.maskEl.top.setTop(0);
50948             this.maskEl.top.show();
50949             
50950             this.maskEl.left.setStyle('position', 'absolute');
50951             this.maskEl.left.setStyle('z-index', 10000);
50952             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50953             this.maskEl.left.setLeft(0);
50954             this.maskEl.left.setTop(box.y - this.padding);
50955             this.maskEl.left.show();
50956
50957             this.maskEl.bottom.setStyle('position', 'absolute');
50958             this.maskEl.bottom.setStyle('z-index', 10000);
50959             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50960             this.maskEl.bottom.setLeft(0);
50961             this.maskEl.bottom.setTop(box.bottom + this.padding);
50962             this.maskEl.bottom.show();
50963
50964             this.maskEl.right.setStyle('position', 'absolute');
50965             this.maskEl.right.setStyle('z-index', 10000);
50966             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50967             this.maskEl.right.setLeft(box.right + this.padding);
50968             this.maskEl.right.setTop(box.y - this.padding);
50969             this.maskEl.right.show();
50970
50971             this.intervalID = window.setInterval(function() {
50972                 Roo.form.BasicForm.popover.unmask();
50973             }, 10000);
50974
50975             window.onwheel = function(){ return false;};
50976             
50977             (function(){ this.isMasked = true; }).defer(500, this);
50978             
50979         },
50980         
50981         unmask : function()
50982         {
50983             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50984                 return;
50985             }
50986             
50987             this.maskEl.top.setStyle('position', 'absolute');
50988             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50989             this.maskEl.top.hide();
50990
50991             this.maskEl.left.setStyle('position', 'absolute');
50992             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50993             this.maskEl.left.hide();
50994
50995             this.maskEl.bottom.setStyle('position', 'absolute');
50996             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50997             this.maskEl.bottom.hide();
50998
50999             this.maskEl.right.setStyle('position', 'absolute');
51000             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51001             this.maskEl.right.hide();
51002             
51003             window.onwheel = function(){ return true;};
51004             
51005             if(this.intervalID){
51006                 window.clearInterval(this.intervalID);
51007                 this.intervalID = false;
51008             }
51009             
51010             this.isMasked = false;
51011             
51012         }
51013         
51014     }
51015     
51016 });/*
51017  * Based on:
51018  * Ext JS Library 1.1.1
51019  * Copyright(c) 2006-2007, Ext JS, LLC.
51020  *
51021  * Originally Released Under LGPL - original licence link has changed is not relivant.
51022  *
51023  * Fork - LGPL
51024  * <script type="text/javascript">
51025  */
51026
51027 /**
51028  * @class Roo.form.Form
51029  * @extends Roo.form.BasicForm
51030  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51031  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51032  * @constructor
51033  * @param {Object} config Configuration options
51034  */
51035 Roo.form.Form = function(config){
51036     var xitems =  [];
51037     if (config.items) {
51038         xitems = config.items;
51039         delete config.items;
51040     }
51041    
51042     
51043     Roo.form.Form.superclass.constructor.call(this, null, config);
51044     this.url = this.url || this.action;
51045     if(!this.root){
51046         this.root = new Roo.form.Layout(Roo.applyIf({
51047             id: Roo.id()
51048         }, config));
51049     }
51050     this.active = this.root;
51051     /**
51052      * Array of all the buttons that have been added to this form via {@link addButton}
51053      * @type Array
51054      */
51055     this.buttons = [];
51056     this.allItems = [];
51057     this.addEvents({
51058         /**
51059          * @event clientvalidation
51060          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51061          * @param {Form} this
51062          * @param {Boolean} valid true if the form has passed client-side validation
51063          */
51064         clientvalidation: true,
51065         /**
51066          * @event rendered
51067          * Fires when the form is rendered
51068          * @param {Roo.form.Form} form
51069          */
51070         rendered : true
51071     });
51072     
51073     if (this.progressUrl) {
51074             // push a hidden field onto the list of fields..
51075             this.addxtype( {
51076                     xns: Roo.form, 
51077                     xtype : 'Hidden', 
51078                     name : 'UPLOAD_IDENTIFIER' 
51079             });
51080         }
51081         
51082     
51083     Roo.each(xitems, this.addxtype, this);
51084     
51085 };
51086
51087 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51088      /**
51089      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51090      */
51091     
51092     /**
51093      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51094      */
51095     /**
51096      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51097      */
51098     /**
51099      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51100      */
51101     buttonAlign:'center',
51102
51103     /**
51104      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51105      */
51106     minButtonWidth:75,
51107
51108     /**
51109      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51110      * This property cascades to child containers if not set.
51111      */
51112     labelAlign:'left',
51113
51114     /**
51115      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51116      * fires a looping event with that state. This is required to bind buttons to the valid
51117      * state using the config value formBind:true on the button.
51118      */
51119     monitorValid : false,
51120
51121     /**
51122      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51123      */
51124     monitorPoll : 200,
51125     
51126     /**
51127      * @cfg {String} progressUrl - Url to return progress data 
51128      */
51129     
51130     progressUrl : false,
51131     /**
51132      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51133      * sending a formdata with extra parameters - eg uploaded elements.
51134      */
51135     
51136     formData : false,
51137     
51138     /**
51139      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51140      * fields are added and the column is closed. If no fields are passed the column remains open
51141      * until end() is called.
51142      * @param {Object} config The config to pass to the column
51143      * @param {Field} field1 (optional)
51144      * @param {Field} field2 (optional)
51145      * @param {Field} etc (optional)
51146      * @return Column The column container object
51147      */
51148     column : function(c){
51149         var col = new Roo.form.Column(c);
51150         this.start(col);
51151         if(arguments.length > 1){ // duplicate code required because of Opera
51152             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51153             this.end();
51154         }
51155         return col;
51156     },
51157
51158     /**
51159      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51160      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51161      * until end() is called.
51162      * @param {Object} config The config to pass to the fieldset
51163      * @param {Field} field1 (optional)
51164      * @param {Field} field2 (optional)
51165      * @param {Field} etc (optional)
51166      * @return FieldSet The fieldset container object
51167      */
51168     fieldset : function(c){
51169         var fs = new Roo.form.FieldSet(c);
51170         this.start(fs);
51171         if(arguments.length > 1){ // duplicate code required because of Opera
51172             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51173             this.end();
51174         }
51175         return fs;
51176     },
51177
51178     /**
51179      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51180      * fields are added and the container is closed. If no fields are passed the container remains open
51181      * until end() is called.
51182      * @param {Object} config The config to pass to the Layout
51183      * @param {Field} field1 (optional)
51184      * @param {Field} field2 (optional)
51185      * @param {Field} etc (optional)
51186      * @return Layout The container object
51187      */
51188     container : function(c){
51189         var l = new Roo.form.Layout(c);
51190         this.start(l);
51191         if(arguments.length > 1){ // duplicate code required because of Opera
51192             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51193             this.end();
51194         }
51195         return l;
51196     },
51197
51198     /**
51199      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51200      * @param {Object} container A Roo.form.Layout or subclass of Layout
51201      * @return {Form} this
51202      */
51203     start : function(c){
51204         // cascade label info
51205         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51206         this.active.stack.push(c);
51207         c.ownerCt = this.active;
51208         this.active = c;
51209         return this;
51210     },
51211
51212     /**
51213      * Closes the current open container
51214      * @return {Form} this
51215      */
51216     end : function(){
51217         if(this.active == this.root){
51218             return this;
51219         }
51220         this.active = this.active.ownerCt;
51221         return this;
51222     },
51223
51224     /**
51225      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51226      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51227      * as the label of the field.
51228      * @param {Field} field1
51229      * @param {Field} field2 (optional)
51230      * @param {Field} etc. (optional)
51231      * @return {Form} this
51232      */
51233     add : function(){
51234         this.active.stack.push.apply(this.active.stack, arguments);
51235         this.allItems.push.apply(this.allItems,arguments);
51236         var r = [];
51237         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51238             if(a[i].isFormField){
51239                 r.push(a[i]);
51240             }
51241         }
51242         if(r.length > 0){
51243             Roo.form.Form.superclass.add.apply(this, r);
51244         }
51245         return this;
51246     },
51247     
51248
51249     
51250     
51251     
51252      /**
51253      * Find any element that has been added to a form, using it's ID or name
51254      * This can include framesets, columns etc. along with regular fields..
51255      * @param {String} id - id or name to find.
51256      
51257      * @return {Element} e - or false if nothing found.
51258      */
51259     findbyId : function(id)
51260     {
51261         var ret = false;
51262         if (!id) {
51263             return ret;
51264         }
51265         Roo.each(this.allItems, function(f){
51266             if (f.id == id || f.name == id ){
51267                 ret = f;
51268                 return false;
51269             }
51270         });
51271         return ret;
51272     },
51273
51274     
51275     
51276     /**
51277      * Render this form into the passed container. This should only be called once!
51278      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51279      * @return {Form} this
51280      */
51281     render : function(ct)
51282     {
51283         
51284         
51285         
51286         ct = Roo.get(ct);
51287         var o = this.autoCreate || {
51288             tag: 'form',
51289             method : this.method || 'POST',
51290             id : this.id || Roo.id()
51291         };
51292         this.initEl(ct.createChild(o));
51293
51294         this.root.render(this.el);
51295         
51296        
51297              
51298         this.items.each(function(f){
51299             f.render('x-form-el-'+f.id);
51300         });
51301
51302         if(this.buttons.length > 0){
51303             // tables are required to maintain order and for correct IE layout
51304             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51305                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51306                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51307             }}, null, true);
51308             var tr = tb.getElementsByTagName('tr')[0];
51309             for(var i = 0, len = this.buttons.length; i < len; i++) {
51310                 var b = this.buttons[i];
51311                 var td = document.createElement('td');
51312                 td.className = 'x-form-btn-td';
51313                 b.render(tr.appendChild(td));
51314             }
51315         }
51316         if(this.monitorValid){ // initialize after render
51317             this.startMonitoring();
51318         }
51319         this.fireEvent('rendered', this);
51320         return this;
51321     },
51322
51323     /**
51324      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51325      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51326      * object or a valid Roo.DomHelper element config
51327      * @param {Function} handler The function called when the button is clicked
51328      * @param {Object} scope (optional) The scope of the handler function
51329      * @return {Roo.Button}
51330      */
51331     addButton : function(config, handler, scope){
51332         var bc = {
51333             handler: handler,
51334             scope: scope,
51335             minWidth: this.minButtonWidth,
51336             hideParent:true
51337         };
51338         if(typeof config == "string"){
51339             bc.text = config;
51340         }else{
51341             Roo.apply(bc, config);
51342         }
51343         var btn = new Roo.Button(null, bc);
51344         this.buttons.push(btn);
51345         return btn;
51346     },
51347
51348      /**
51349      * Adds a series of form elements (using the xtype property as the factory method.
51350      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51351      * @param {Object} config 
51352      */
51353     
51354     addxtype : function()
51355     {
51356         var ar = Array.prototype.slice.call(arguments, 0);
51357         var ret = false;
51358         for(var i = 0; i < ar.length; i++) {
51359             if (!ar[i]) {
51360                 continue; // skip -- if this happends something invalid got sent, we 
51361                 // should ignore it, as basically that interface element will not show up
51362                 // and that should be pretty obvious!!
51363             }
51364             
51365             if (Roo.form[ar[i].xtype]) {
51366                 ar[i].form = this;
51367                 var fe = Roo.factory(ar[i], Roo.form);
51368                 if (!ret) {
51369                     ret = fe;
51370                 }
51371                 fe.form = this;
51372                 if (fe.store) {
51373                     fe.store.form = this;
51374                 }
51375                 if (fe.isLayout) {  
51376                          
51377                     this.start(fe);
51378                     this.allItems.push(fe);
51379                     if (fe.items && fe.addxtype) {
51380                         fe.addxtype.apply(fe, fe.items);
51381                         delete fe.items;
51382                     }
51383                      this.end();
51384                     continue;
51385                 }
51386                 
51387                 
51388                  
51389                 this.add(fe);
51390               //  console.log('adding ' + ar[i].xtype);
51391             }
51392             if (ar[i].xtype == 'Button') {  
51393                 //console.log('adding button');
51394                 //console.log(ar[i]);
51395                 this.addButton(ar[i]);
51396                 this.allItems.push(fe);
51397                 continue;
51398             }
51399             
51400             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51401                 alert('end is not supported on xtype any more, use items');
51402             //    this.end();
51403             //    //console.log('adding end');
51404             }
51405             
51406         }
51407         return ret;
51408     },
51409     
51410     /**
51411      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51412      * option "monitorValid"
51413      */
51414     startMonitoring : function(){
51415         if(!this.bound){
51416             this.bound = true;
51417             Roo.TaskMgr.start({
51418                 run : this.bindHandler,
51419                 interval : this.monitorPoll || 200,
51420                 scope: this
51421             });
51422         }
51423     },
51424
51425     /**
51426      * Stops monitoring of the valid state of this form
51427      */
51428     stopMonitoring : function(){
51429         this.bound = false;
51430     },
51431
51432     // private
51433     bindHandler : function(){
51434         if(!this.bound){
51435             return false; // stops binding
51436         }
51437         var valid = true;
51438         this.items.each(function(f){
51439             if(!f.isValid(true)){
51440                 valid = false;
51441                 return false;
51442             }
51443         });
51444         for(var i = 0, len = this.buttons.length; i < len; i++){
51445             var btn = this.buttons[i];
51446             if(btn.formBind === true && btn.disabled === valid){
51447                 btn.setDisabled(!valid);
51448             }
51449         }
51450         this.fireEvent('clientvalidation', this, valid);
51451     }
51452     
51453     
51454     
51455     
51456     
51457     
51458     
51459     
51460 });
51461
51462
51463 // back compat
51464 Roo.Form = Roo.form.Form;
51465 /*
51466  * Based on:
51467  * Ext JS Library 1.1.1
51468  * Copyright(c) 2006-2007, Ext JS, LLC.
51469  *
51470  * Originally Released Under LGPL - original licence link has changed is not relivant.
51471  *
51472  * Fork - LGPL
51473  * <script type="text/javascript">
51474  */
51475
51476 // as we use this in bootstrap.
51477 Roo.namespace('Roo.form');
51478  /**
51479  * @class Roo.form.Action
51480  * Internal Class used to handle form actions
51481  * @constructor
51482  * @param {Roo.form.BasicForm} el The form element or its id
51483  * @param {Object} config Configuration options
51484  */
51485
51486  
51487  
51488 // define the action interface
51489 Roo.form.Action = function(form, options){
51490     this.form = form;
51491     this.options = options || {};
51492 };
51493 /**
51494  * Client Validation Failed
51495  * @const 
51496  */
51497 Roo.form.Action.CLIENT_INVALID = 'client';
51498 /**
51499  * Server Validation Failed
51500  * @const 
51501  */
51502 Roo.form.Action.SERVER_INVALID = 'server';
51503  /**
51504  * Connect to Server Failed
51505  * @const 
51506  */
51507 Roo.form.Action.CONNECT_FAILURE = 'connect';
51508 /**
51509  * Reading Data from Server Failed
51510  * @const 
51511  */
51512 Roo.form.Action.LOAD_FAILURE = 'load';
51513
51514 Roo.form.Action.prototype = {
51515     type : 'default',
51516     failureType : undefined,
51517     response : undefined,
51518     result : undefined,
51519
51520     // interface method
51521     run : function(options){
51522
51523     },
51524
51525     // interface method
51526     success : function(response){
51527
51528     },
51529
51530     // interface method
51531     handleResponse : function(response){
51532
51533     },
51534
51535     // default connection failure
51536     failure : function(response){
51537         
51538         this.response = response;
51539         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51540         this.form.afterAction(this, false);
51541     },
51542
51543     processResponse : function(response){
51544         this.response = response;
51545         if(!response.responseText){
51546             return true;
51547         }
51548         this.result = this.handleResponse(response);
51549         return this.result;
51550     },
51551
51552     // utility functions used internally
51553     getUrl : function(appendParams){
51554         var url = this.options.url || this.form.url || this.form.el.dom.action;
51555         if(appendParams){
51556             var p = this.getParams();
51557             if(p){
51558                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51559             }
51560         }
51561         return url;
51562     },
51563
51564     getMethod : function(){
51565         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51566     },
51567
51568     getParams : function(){
51569         var bp = this.form.baseParams;
51570         var p = this.options.params;
51571         if(p){
51572             if(typeof p == "object"){
51573                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51574             }else if(typeof p == 'string' && bp){
51575                 p += '&' + Roo.urlEncode(bp);
51576             }
51577         }else if(bp){
51578             p = Roo.urlEncode(bp);
51579         }
51580         return p;
51581     },
51582
51583     createCallback : function(){
51584         return {
51585             success: this.success,
51586             failure: this.failure,
51587             scope: this,
51588             timeout: (this.form.timeout*1000),
51589             upload: this.form.fileUpload ? this.success : undefined
51590         };
51591     }
51592 };
51593
51594 Roo.form.Action.Submit = function(form, options){
51595     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51596 };
51597
51598 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51599     type : 'submit',
51600
51601     haveProgress : false,
51602     uploadComplete : false,
51603     
51604     // uploadProgress indicator.
51605     uploadProgress : function()
51606     {
51607         if (!this.form.progressUrl) {
51608             return;
51609         }
51610         
51611         if (!this.haveProgress) {
51612             Roo.MessageBox.progress("Uploading", "Uploading");
51613         }
51614         if (this.uploadComplete) {
51615            Roo.MessageBox.hide();
51616            return;
51617         }
51618         
51619         this.haveProgress = true;
51620    
51621         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51622         
51623         var c = new Roo.data.Connection();
51624         c.request({
51625             url : this.form.progressUrl,
51626             params: {
51627                 id : uid
51628             },
51629             method: 'GET',
51630             success : function(req){
51631                //console.log(data);
51632                 var rdata = false;
51633                 var edata;
51634                 try  {
51635                    rdata = Roo.decode(req.responseText)
51636                 } catch (e) {
51637                     Roo.log("Invalid data from server..");
51638                     Roo.log(edata);
51639                     return;
51640                 }
51641                 if (!rdata || !rdata.success) {
51642                     Roo.log(rdata);
51643                     Roo.MessageBox.alert(Roo.encode(rdata));
51644                     return;
51645                 }
51646                 var data = rdata.data;
51647                 
51648                 if (this.uploadComplete) {
51649                    Roo.MessageBox.hide();
51650                    return;
51651                 }
51652                    
51653                 if (data){
51654                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51655                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51656                     );
51657                 }
51658                 this.uploadProgress.defer(2000,this);
51659             },
51660        
51661             failure: function(data) {
51662                 Roo.log('progress url failed ');
51663                 Roo.log(data);
51664             },
51665             scope : this
51666         });
51667            
51668     },
51669     
51670     
51671     run : function()
51672     {
51673         // run get Values on the form, so it syncs any secondary forms.
51674         this.form.getValues();
51675         
51676         var o = this.options;
51677         var method = this.getMethod();
51678         var isPost = method == 'POST';
51679         if(o.clientValidation === false || this.form.isValid()){
51680             
51681             if (this.form.progressUrl) {
51682                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51683                     (new Date() * 1) + '' + Math.random());
51684                     
51685             } 
51686             
51687             
51688             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51689                 form:this.form.el.dom,
51690                 url:this.getUrl(!isPost),
51691                 method: method,
51692                 params:isPost ? this.getParams() : null,
51693                 isUpload: this.form.fileUpload,
51694                 formData : this.form.formData
51695             }));
51696             
51697             this.uploadProgress();
51698
51699         }else if (o.clientValidation !== false){ // client validation failed
51700             this.failureType = Roo.form.Action.CLIENT_INVALID;
51701             this.form.afterAction(this, false);
51702         }
51703     },
51704
51705     success : function(response)
51706     {
51707         this.uploadComplete= true;
51708         if (this.haveProgress) {
51709             Roo.MessageBox.hide();
51710         }
51711         
51712         
51713         var result = this.processResponse(response);
51714         if(result === true || result.success){
51715             this.form.afterAction(this, true);
51716             return;
51717         }
51718         if(result.errors){
51719             this.form.markInvalid(result.errors);
51720             this.failureType = Roo.form.Action.SERVER_INVALID;
51721         }
51722         this.form.afterAction(this, false);
51723     },
51724     failure : function(response)
51725     {
51726         this.uploadComplete= true;
51727         if (this.haveProgress) {
51728             Roo.MessageBox.hide();
51729         }
51730         
51731         this.response = response;
51732         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51733         this.form.afterAction(this, false);
51734     },
51735     
51736     handleResponse : function(response){
51737         if(this.form.errorReader){
51738             var rs = this.form.errorReader.read(response);
51739             var errors = [];
51740             if(rs.records){
51741                 for(var i = 0, len = rs.records.length; i < len; i++) {
51742                     var r = rs.records[i];
51743                     errors[i] = r.data;
51744                 }
51745             }
51746             if(errors.length < 1){
51747                 errors = null;
51748             }
51749             return {
51750                 success : rs.success,
51751                 errors : errors
51752             };
51753         }
51754         var ret = false;
51755         try {
51756             ret = Roo.decode(response.responseText);
51757         } catch (e) {
51758             ret = {
51759                 success: false,
51760                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51761                 errors : []
51762             };
51763         }
51764         return ret;
51765         
51766     }
51767 });
51768
51769
51770 Roo.form.Action.Load = function(form, options){
51771     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51772     this.reader = this.form.reader;
51773 };
51774
51775 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51776     type : 'load',
51777
51778     run : function(){
51779         
51780         Roo.Ajax.request(Roo.apply(
51781                 this.createCallback(), {
51782                     method:this.getMethod(),
51783                     url:this.getUrl(false),
51784                     params:this.getParams()
51785         }));
51786     },
51787
51788     success : function(response){
51789         
51790         var result = this.processResponse(response);
51791         if(result === true || !result.success || !result.data){
51792             this.failureType = Roo.form.Action.LOAD_FAILURE;
51793             this.form.afterAction(this, false);
51794             return;
51795         }
51796         this.form.clearInvalid();
51797         this.form.setValues(result.data);
51798         this.form.afterAction(this, true);
51799     },
51800
51801     handleResponse : function(response){
51802         if(this.form.reader){
51803             var rs = this.form.reader.read(response);
51804             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51805             return {
51806                 success : rs.success,
51807                 data : data
51808             };
51809         }
51810         return Roo.decode(response.responseText);
51811     }
51812 });
51813
51814 Roo.form.Action.ACTION_TYPES = {
51815     'load' : Roo.form.Action.Load,
51816     'submit' : Roo.form.Action.Submit
51817 };/*
51818  * Based on:
51819  * Ext JS Library 1.1.1
51820  * Copyright(c) 2006-2007, Ext JS, LLC.
51821  *
51822  * Originally Released Under LGPL - original licence link has changed is not relivant.
51823  *
51824  * Fork - LGPL
51825  * <script type="text/javascript">
51826  */
51827  
51828 /**
51829  * @class Roo.form.Layout
51830  * @extends Roo.Component
51831  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51832  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51833  * @constructor
51834  * @param {Object} config Configuration options
51835  */
51836 Roo.form.Layout = function(config){
51837     var xitems = [];
51838     if (config.items) {
51839         xitems = config.items;
51840         delete config.items;
51841     }
51842     Roo.form.Layout.superclass.constructor.call(this, config);
51843     this.stack = [];
51844     Roo.each(xitems, this.addxtype, this);
51845      
51846 };
51847
51848 Roo.extend(Roo.form.Layout, Roo.Component, {
51849     /**
51850      * @cfg {String/Object} autoCreate
51851      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51852      */
51853     /**
51854      * @cfg {String/Object/Function} style
51855      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51856      * a function which returns such a specification.
51857      */
51858     /**
51859      * @cfg {String} labelAlign
51860      * Valid values are "left," "top" and "right" (defaults to "left")
51861      */
51862     /**
51863      * @cfg {Number} labelWidth
51864      * Fixed width in pixels of all field labels (defaults to undefined)
51865      */
51866     /**
51867      * @cfg {Boolean} clear
51868      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51869      */
51870     clear : true,
51871     /**
51872      * @cfg {String} labelSeparator
51873      * The separator to use after field labels (defaults to ':')
51874      */
51875     labelSeparator : ':',
51876     /**
51877      * @cfg {Boolean} hideLabels
51878      * True to suppress the display of field labels in this layout (defaults to false)
51879      */
51880     hideLabels : false,
51881
51882     // private
51883     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51884     
51885     isLayout : true,
51886     
51887     // private
51888     onRender : function(ct, position){
51889         if(this.el){ // from markup
51890             this.el = Roo.get(this.el);
51891         }else {  // generate
51892             var cfg = this.getAutoCreate();
51893             this.el = ct.createChild(cfg, position);
51894         }
51895         if(this.style){
51896             this.el.applyStyles(this.style);
51897         }
51898         if(this.labelAlign){
51899             this.el.addClass('x-form-label-'+this.labelAlign);
51900         }
51901         if(this.hideLabels){
51902             this.labelStyle = "display:none";
51903             this.elementStyle = "padding-left:0;";
51904         }else{
51905             if(typeof this.labelWidth == 'number'){
51906                 this.labelStyle = "width:"+this.labelWidth+"px;";
51907                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51908             }
51909             if(this.labelAlign == 'top'){
51910                 this.labelStyle = "width:auto;";
51911                 this.elementStyle = "padding-left:0;";
51912             }
51913         }
51914         var stack = this.stack;
51915         var slen = stack.length;
51916         if(slen > 0){
51917             if(!this.fieldTpl){
51918                 var t = new Roo.Template(
51919                     '<div class="x-form-item {5}">',
51920                         '<label for="{0}" style="{2}">{1}{4}</label>',
51921                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51922                         '</div>',
51923                     '</div><div class="x-form-clear-left"></div>'
51924                 );
51925                 t.disableFormats = true;
51926                 t.compile();
51927                 Roo.form.Layout.prototype.fieldTpl = t;
51928             }
51929             for(var i = 0; i < slen; i++) {
51930                 if(stack[i].isFormField){
51931                     this.renderField(stack[i]);
51932                 }else{
51933                     this.renderComponent(stack[i]);
51934                 }
51935             }
51936         }
51937         if(this.clear){
51938             this.el.createChild({cls:'x-form-clear'});
51939         }
51940     },
51941
51942     // private
51943     renderField : function(f){
51944         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51945                f.id, //0
51946                f.fieldLabel, //1
51947                f.labelStyle||this.labelStyle||'', //2
51948                this.elementStyle||'', //3
51949                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51950                f.itemCls||this.itemCls||''  //5
51951        ], true).getPrevSibling());
51952     },
51953
51954     // private
51955     renderComponent : function(c){
51956         c.render(c.isLayout ? this.el : this.el.createChild());    
51957     },
51958     /**
51959      * Adds a object form elements (using the xtype property as the factory method.)
51960      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51961      * @param {Object} config 
51962      */
51963     addxtype : function(o)
51964     {
51965         // create the lement.
51966         o.form = this.form;
51967         var fe = Roo.factory(o, Roo.form);
51968         this.form.allItems.push(fe);
51969         this.stack.push(fe);
51970         
51971         if (fe.isFormField) {
51972             this.form.items.add(fe);
51973         }
51974          
51975         return fe;
51976     }
51977 });
51978
51979 /**
51980  * @class Roo.form.Column
51981  * @extends Roo.form.Layout
51982  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51983  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51984  * @constructor
51985  * @param {Object} config Configuration options
51986  */
51987 Roo.form.Column = function(config){
51988     Roo.form.Column.superclass.constructor.call(this, config);
51989 };
51990
51991 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51992     /**
51993      * @cfg {Number/String} width
51994      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51995      */
51996     /**
51997      * @cfg {String/Object} autoCreate
51998      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51999      */
52000
52001     // private
52002     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52003
52004     // private
52005     onRender : function(ct, position){
52006         Roo.form.Column.superclass.onRender.call(this, ct, position);
52007         if(this.width){
52008             this.el.setWidth(this.width);
52009         }
52010     }
52011 });
52012
52013
52014 /**
52015  * @class Roo.form.Row
52016  * @extends Roo.form.Layout
52017  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52018  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52019  * @constructor
52020  * @param {Object} config Configuration options
52021  */
52022
52023  
52024 Roo.form.Row = function(config){
52025     Roo.form.Row.superclass.constructor.call(this, config);
52026 };
52027  
52028 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52029       /**
52030      * @cfg {Number/String} width
52031      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52032      */
52033     /**
52034      * @cfg {Number/String} height
52035      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52036      */
52037     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52038     
52039     padWidth : 20,
52040     // private
52041     onRender : function(ct, position){
52042         //console.log('row render');
52043         if(!this.rowTpl){
52044             var t = new Roo.Template(
52045                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52046                     '<label for="{0}" style="{2}">{1}{4}</label>',
52047                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52048                     '</div>',
52049                 '</div>'
52050             );
52051             t.disableFormats = true;
52052             t.compile();
52053             Roo.form.Layout.prototype.rowTpl = t;
52054         }
52055         this.fieldTpl = this.rowTpl;
52056         
52057         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52058         var labelWidth = 100;
52059         
52060         if ((this.labelAlign != 'top')) {
52061             if (typeof this.labelWidth == 'number') {
52062                 labelWidth = this.labelWidth
52063             }
52064             this.padWidth =  20 + labelWidth;
52065             
52066         }
52067         
52068         Roo.form.Column.superclass.onRender.call(this, ct, position);
52069         if(this.width){
52070             this.el.setWidth(this.width);
52071         }
52072         if(this.height){
52073             this.el.setHeight(this.height);
52074         }
52075     },
52076     
52077     // private
52078     renderField : function(f){
52079         f.fieldEl = this.fieldTpl.append(this.el, [
52080                f.id, f.fieldLabel,
52081                f.labelStyle||this.labelStyle||'',
52082                this.elementStyle||'',
52083                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52084                f.itemCls||this.itemCls||'',
52085                f.width ? f.width + this.padWidth : 160 + this.padWidth
52086        ],true);
52087     }
52088 });
52089  
52090
52091 /**
52092  * @class Roo.form.FieldSet
52093  * @extends Roo.form.Layout
52094  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52095  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52096  * @constructor
52097  * @param {Object} config Configuration options
52098  */
52099 Roo.form.FieldSet = function(config){
52100     Roo.form.FieldSet.superclass.constructor.call(this, config);
52101 };
52102
52103 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52104     /**
52105      * @cfg {String} legend
52106      * The text to display as the legend for the FieldSet (defaults to '')
52107      */
52108     /**
52109      * @cfg {String/Object} autoCreate
52110      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52111      */
52112
52113     // private
52114     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52115
52116     // private
52117     onRender : function(ct, position){
52118         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52119         if(this.legend){
52120             this.setLegend(this.legend);
52121         }
52122     },
52123
52124     // private
52125     setLegend : function(text){
52126         if(this.rendered){
52127             this.el.child('legend').update(text);
52128         }
52129     }
52130 });/*
52131  * Based on:
52132  * Ext JS Library 1.1.1
52133  * Copyright(c) 2006-2007, Ext JS, LLC.
52134  *
52135  * Originally Released Under LGPL - original licence link has changed is not relivant.
52136  *
52137  * Fork - LGPL
52138  * <script type="text/javascript">
52139  */
52140 /**
52141  * @class Roo.form.VTypes
52142  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52143  * @static
52144  */
52145 Roo.form.VTypes = function(){
52146     // closure these in so they are only created once.
52147     var alpha = /^[a-zA-Z_]+$/;
52148     var alphanum = /^[a-zA-Z0-9_]+$/;
52149     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52150     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52151
52152     // All these messages and functions are configurable
52153     return {
52154         /**
52155          * The function used to validate email addresses
52156          * @param {String} value The email address
52157          */
52158         'email' : function(v){
52159             return email.test(v);
52160         },
52161         /**
52162          * The error text to display when the email validation function returns false
52163          * @type String
52164          */
52165         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52166         /**
52167          * The keystroke filter mask to be applied on email input
52168          * @type RegExp
52169          */
52170         'emailMask' : /[a-z0-9_\.\-@]/i,
52171
52172         /**
52173          * The function used to validate URLs
52174          * @param {String} value The URL
52175          */
52176         'url' : function(v){
52177             return url.test(v);
52178         },
52179         /**
52180          * The error text to display when the url validation function returns false
52181          * @type String
52182          */
52183         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52184         
52185         /**
52186          * The function used to validate alpha values
52187          * @param {String} value The value
52188          */
52189         'alpha' : function(v){
52190             return alpha.test(v);
52191         },
52192         /**
52193          * The error text to display when the alpha validation function returns false
52194          * @type String
52195          */
52196         'alphaText' : 'This field should only contain letters and _',
52197         /**
52198          * The keystroke filter mask to be applied on alpha input
52199          * @type RegExp
52200          */
52201         'alphaMask' : /[a-z_]/i,
52202
52203         /**
52204          * The function used to validate alphanumeric values
52205          * @param {String} value The value
52206          */
52207         'alphanum' : function(v){
52208             return alphanum.test(v);
52209         },
52210         /**
52211          * The error text to display when the alphanumeric validation function returns false
52212          * @type String
52213          */
52214         'alphanumText' : 'This field should only contain letters, numbers and _',
52215         /**
52216          * The keystroke filter mask to be applied on alphanumeric input
52217          * @type RegExp
52218          */
52219         'alphanumMask' : /[a-z0-9_]/i
52220     };
52221 }();//<script type="text/javascript">
52222
52223 /**
52224  * @class Roo.form.FCKeditor
52225  * @extends Roo.form.TextArea
52226  * Wrapper around the FCKEditor http://www.fckeditor.net
52227  * @constructor
52228  * Creates a new FCKeditor
52229  * @param {Object} config Configuration options
52230  */
52231 Roo.form.FCKeditor = function(config){
52232     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52233     this.addEvents({
52234          /**
52235          * @event editorinit
52236          * Fired when the editor is initialized - you can add extra handlers here..
52237          * @param {FCKeditor} this
52238          * @param {Object} the FCK object.
52239          */
52240         editorinit : true
52241     });
52242     
52243     
52244 };
52245 Roo.form.FCKeditor.editors = { };
52246 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52247 {
52248     //defaultAutoCreate : {
52249     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52250     //},
52251     // private
52252     /**
52253      * @cfg {Object} fck options - see fck manual for details.
52254      */
52255     fckconfig : false,
52256     
52257     /**
52258      * @cfg {Object} fck toolbar set (Basic or Default)
52259      */
52260     toolbarSet : 'Basic',
52261     /**
52262      * @cfg {Object} fck BasePath
52263      */ 
52264     basePath : '/fckeditor/',
52265     
52266     
52267     frame : false,
52268     
52269     value : '',
52270     
52271    
52272     onRender : function(ct, position)
52273     {
52274         if(!this.el){
52275             this.defaultAutoCreate = {
52276                 tag: "textarea",
52277                 style:"width:300px;height:60px;",
52278                 autocomplete: "new-password"
52279             };
52280         }
52281         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52282         /*
52283         if(this.grow){
52284             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52285             if(this.preventScrollbars){
52286                 this.el.setStyle("overflow", "hidden");
52287             }
52288             this.el.setHeight(this.growMin);
52289         }
52290         */
52291         //console.log('onrender' + this.getId() );
52292         Roo.form.FCKeditor.editors[this.getId()] = this;
52293          
52294
52295         this.replaceTextarea() ;
52296         
52297     },
52298     
52299     getEditor : function() {
52300         return this.fckEditor;
52301     },
52302     /**
52303      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52304      * @param {Mixed} value The value to set
52305      */
52306     
52307     
52308     setValue : function(value)
52309     {
52310         //console.log('setValue: ' + value);
52311         
52312         if(typeof(value) == 'undefined') { // not sure why this is happending...
52313             return;
52314         }
52315         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52316         
52317         //if(!this.el || !this.getEditor()) {
52318         //    this.value = value;
52319             //this.setValue.defer(100,this,[value]);    
52320         //    return;
52321         //} 
52322         
52323         if(!this.getEditor()) {
52324             return;
52325         }
52326         
52327         this.getEditor().SetData(value);
52328         
52329         //
52330
52331     },
52332
52333     /**
52334      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52335      * @return {Mixed} value The field value
52336      */
52337     getValue : function()
52338     {
52339         
52340         if (this.frame && this.frame.dom.style.display == 'none') {
52341             return Roo.form.FCKeditor.superclass.getValue.call(this);
52342         }
52343         
52344         if(!this.el || !this.getEditor()) {
52345            
52346            // this.getValue.defer(100,this); 
52347             return this.value;
52348         }
52349        
52350         
52351         var value=this.getEditor().GetData();
52352         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52353         return Roo.form.FCKeditor.superclass.getValue.call(this);
52354         
52355
52356     },
52357
52358     /**
52359      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52360      * @return {Mixed} value The field value
52361      */
52362     getRawValue : function()
52363     {
52364         if (this.frame && this.frame.dom.style.display == 'none') {
52365             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52366         }
52367         
52368         if(!this.el || !this.getEditor()) {
52369             //this.getRawValue.defer(100,this); 
52370             return this.value;
52371             return;
52372         }
52373         
52374         
52375         
52376         var value=this.getEditor().GetData();
52377         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52378         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52379          
52380     },
52381     
52382     setSize : function(w,h) {
52383         
52384         
52385         
52386         //if (this.frame && this.frame.dom.style.display == 'none') {
52387         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52388         //    return;
52389         //}
52390         //if(!this.el || !this.getEditor()) {
52391         //    this.setSize.defer(100,this, [w,h]); 
52392         //    return;
52393         //}
52394         
52395         
52396         
52397         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52398         
52399         this.frame.dom.setAttribute('width', w);
52400         this.frame.dom.setAttribute('height', h);
52401         this.frame.setSize(w,h);
52402         
52403     },
52404     
52405     toggleSourceEdit : function(value) {
52406         
52407       
52408          
52409         this.el.dom.style.display = value ? '' : 'none';
52410         this.frame.dom.style.display = value ?  'none' : '';
52411         
52412     },
52413     
52414     
52415     focus: function(tag)
52416     {
52417         if (this.frame.dom.style.display == 'none') {
52418             return Roo.form.FCKeditor.superclass.focus.call(this);
52419         }
52420         if(!this.el || !this.getEditor()) {
52421             this.focus.defer(100,this, [tag]); 
52422             return;
52423         }
52424         
52425         
52426         
52427         
52428         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52429         this.getEditor().Focus();
52430         if (tgs.length) {
52431             if (!this.getEditor().Selection.GetSelection()) {
52432                 this.focus.defer(100,this, [tag]); 
52433                 return;
52434             }
52435             
52436             
52437             var r = this.getEditor().EditorDocument.createRange();
52438             r.setStart(tgs[0],0);
52439             r.setEnd(tgs[0],0);
52440             this.getEditor().Selection.GetSelection().removeAllRanges();
52441             this.getEditor().Selection.GetSelection().addRange(r);
52442             this.getEditor().Focus();
52443         }
52444         
52445     },
52446     
52447     
52448     
52449     replaceTextarea : function()
52450     {
52451         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52452             return ;
52453         }
52454         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52455         //{
52456             // We must check the elements firstly using the Id and then the name.
52457         var oTextarea = document.getElementById( this.getId() );
52458         
52459         var colElementsByName = document.getElementsByName( this.getId() ) ;
52460          
52461         oTextarea.style.display = 'none' ;
52462
52463         if ( oTextarea.tabIndex ) {            
52464             this.TabIndex = oTextarea.tabIndex ;
52465         }
52466         
52467         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52468         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52469         this.frame = Roo.get(this.getId() + '___Frame')
52470     },
52471     
52472     _getConfigHtml : function()
52473     {
52474         var sConfig = '' ;
52475
52476         for ( var o in this.fckconfig ) {
52477             sConfig += sConfig.length > 0  ? '&amp;' : '';
52478             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52479         }
52480
52481         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52482     },
52483     
52484     
52485     _getIFrameHtml : function()
52486     {
52487         var sFile = 'fckeditor.html' ;
52488         /* no idea what this is about..
52489         try
52490         {
52491             if ( (/fcksource=true/i).test( window.top.location.search ) )
52492                 sFile = 'fckeditor.original.html' ;
52493         }
52494         catch (e) { 
52495         */
52496
52497         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52498         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52499         
52500         
52501         var html = '<iframe id="' + this.getId() +
52502             '___Frame" src="' + sLink +
52503             '" width="' + this.width +
52504             '" height="' + this.height + '"' +
52505             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52506             ' frameborder="0" scrolling="no"></iframe>' ;
52507
52508         return html ;
52509     },
52510     
52511     _insertHtmlBefore : function( html, element )
52512     {
52513         if ( element.insertAdjacentHTML )       {
52514             // IE
52515             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52516         } else { // Gecko
52517             var oRange = document.createRange() ;
52518             oRange.setStartBefore( element ) ;
52519             var oFragment = oRange.createContextualFragment( html );
52520             element.parentNode.insertBefore( oFragment, element ) ;
52521         }
52522     }
52523     
52524     
52525   
52526     
52527     
52528     
52529     
52530
52531 });
52532
52533 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52534
52535 function FCKeditor_OnComplete(editorInstance){
52536     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52537     f.fckEditor = editorInstance;
52538     //console.log("loaded");
52539     f.fireEvent('editorinit', f, editorInstance);
52540
52541   
52542
52543  
52544
52545
52546
52547
52548
52549
52550
52551
52552
52553
52554
52555
52556
52557
52558
52559 //<script type="text/javascript">
52560 /**
52561  * @class Roo.form.GridField
52562  * @extends Roo.form.Field
52563  * Embed a grid (or editable grid into a form)
52564  * STATUS ALPHA
52565  * 
52566  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52567  * it needs 
52568  * xgrid.store = Roo.data.Store
52569  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52570  * xgrid.store.reader = Roo.data.JsonReader 
52571  * 
52572  * 
52573  * @constructor
52574  * Creates a new GridField
52575  * @param {Object} config Configuration options
52576  */
52577 Roo.form.GridField = function(config){
52578     Roo.form.GridField.superclass.constructor.call(this, config);
52579      
52580 };
52581
52582 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52583     /**
52584      * @cfg {Number} width  - used to restrict width of grid..
52585      */
52586     width : 100,
52587     /**
52588      * @cfg {Number} height - used to restrict height of grid..
52589      */
52590     height : 50,
52591      /**
52592      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52593          * 
52594          *}
52595      */
52596     xgrid : false, 
52597     /**
52598      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52599      * {tag: "input", type: "checkbox", autocomplete: "off"})
52600      */
52601    // defaultAutoCreate : { tag: 'div' },
52602     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52603     /**
52604      * @cfg {String} addTitle Text to include for adding a title.
52605      */
52606     addTitle : false,
52607     //
52608     onResize : function(){
52609         Roo.form.Field.superclass.onResize.apply(this, arguments);
52610     },
52611
52612     initEvents : function(){
52613         // Roo.form.Checkbox.superclass.initEvents.call(this);
52614         // has no events...
52615        
52616     },
52617
52618
52619     getResizeEl : function(){
52620         return this.wrap;
52621     },
52622
52623     getPositionEl : function(){
52624         return this.wrap;
52625     },
52626
52627     // private
52628     onRender : function(ct, position){
52629         
52630         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52631         var style = this.style;
52632         delete this.style;
52633         
52634         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52635         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52636         this.viewEl = this.wrap.createChild({ tag: 'div' });
52637         if (style) {
52638             this.viewEl.applyStyles(style);
52639         }
52640         if (this.width) {
52641             this.viewEl.setWidth(this.width);
52642         }
52643         if (this.height) {
52644             this.viewEl.setHeight(this.height);
52645         }
52646         //if(this.inputValue !== undefined){
52647         //this.setValue(this.value);
52648         
52649         
52650         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52651         
52652         
52653         this.grid.render();
52654         this.grid.getDataSource().on('remove', this.refreshValue, this);
52655         this.grid.getDataSource().on('update', this.refreshValue, this);
52656         this.grid.on('afteredit', this.refreshValue, this);
52657  
52658     },
52659      
52660     
52661     /**
52662      * Sets the value of the item. 
52663      * @param {String} either an object  or a string..
52664      */
52665     setValue : function(v){
52666         //this.value = v;
52667         v = v || []; // empty set..
52668         // this does not seem smart - it really only affects memoryproxy grids..
52669         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52670             var ds = this.grid.getDataSource();
52671             // assumes a json reader..
52672             var data = {}
52673             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52674             ds.loadData( data);
52675         }
52676         // clear selection so it does not get stale.
52677         if (this.grid.sm) { 
52678             this.grid.sm.clearSelections();
52679         }
52680         
52681         Roo.form.GridField.superclass.setValue.call(this, v);
52682         this.refreshValue();
52683         // should load data in the grid really....
52684     },
52685     
52686     // private
52687     refreshValue: function() {
52688          var val = [];
52689         this.grid.getDataSource().each(function(r) {
52690             val.push(r.data);
52691         });
52692         this.el.dom.value = Roo.encode(val);
52693     }
52694     
52695      
52696     
52697     
52698 });/*
52699  * Based on:
52700  * Ext JS Library 1.1.1
52701  * Copyright(c) 2006-2007, Ext JS, LLC.
52702  *
52703  * Originally Released Under LGPL - original licence link has changed is not relivant.
52704  *
52705  * Fork - LGPL
52706  * <script type="text/javascript">
52707  */
52708 /**
52709  * @class Roo.form.DisplayField
52710  * @extends Roo.form.Field
52711  * A generic Field to display non-editable data.
52712  * @cfg {Boolean} closable (true|false) default false
52713  * @constructor
52714  * Creates a new Display Field item.
52715  * @param {Object} config Configuration options
52716  */
52717 Roo.form.DisplayField = function(config){
52718     Roo.form.DisplayField.superclass.constructor.call(this, config);
52719     
52720     this.addEvents({
52721         /**
52722          * @event close
52723          * Fires after the click the close btn
52724              * @param {Roo.form.DisplayField} this
52725              */
52726         close : true
52727     });
52728 };
52729
52730 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52731     inputType:      'hidden',
52732     allowBlank:     true,
52733     readOnly:         true,
52734     
52735  
52736     /**
52737      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52738      */
52739     focusClass : undefined,
52740     /**
52741      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52742      */
52743     fieldClass: 'x-form-field',
52744     
52745      /**
52746      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52747      */
52748     valueRenderer: undefined,
52749     
52750     width: 100,
52751     /**
52752      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52753      * {tag: "input", type: "checkbox", autocomplete: "off"})
52754      */
52755      
52756  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52757  
52758     closable : false,
52759     
52760     onResize : function(){
52761         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52762         
52763     },
52764
52765     initEvents : function(){
52766         // Roo.form.Checkbox.superclass.initEvents.call(this);
52767         // has no events...
52768         
52769         if(this.closable){
52770             this.closeEl.on('click', this.onClose, this);
52771         }
52772        
52773     },
52774
52775
52776     getResizeEl : function(){
52777         return this.wrap;
52778     },
52779
52780     getPositionEl : function(){
52781         return this.wrap;
52782     },
52783
52784     // private
52785     onRender : function(ct, position){
52786         
52787         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52788         //if(this.inputValue !== undefined){
52789         this.wrap = this.el.wrap();
52790         
52791         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52792         
52793         if(this.closable){
52794             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52795         }
52796         
52797         if (this.bodyStyle) {
52798             this.viewEl.applyStyles(this.bodyStyle);
52799         }
52800         //this.viewEl.setStyle('padding', '2px');
52801         
52802         this.setValue(this.value);
52803         
52804     },
52805 /*
52806     // private
52807     initValue : Roo.emptyFn,
52808
52809   */
52810
52811         // private
52812     onClick : function(){
52813         
52814     },
52815
52816     /**
52817      * Sets the checked state of the checkbox.
52818      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52819      */
52820     setValue : function(v){
52821         this.value = v;
52822         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52823         // this might be called before we have a dom element..
52824         if (!this.viewEl) {
52825             return;
52826         }
52827         this.viewEl.dom.innerHTML = html;
52828         Roo.form.DisplayField.superclass.setValue.call(this, v);
52829
52830     },
52831     
52832     onClose : function(e)
52833     {
52834         e.preventDefault();
52835         
52836         this.fireEvent('close', this);
52837     }
52838 });/*
52839  * 
52840  * Licence- LGPL
52841  * 
52842  */
52843
52844 /**
52845  * @class Roo.form.DayPicker
52846  * @extends Roo.form.Field
52847  * A Day picker show [M] [T] [W] ....
52848  * @constructor
52849  * Creates a new Day Picker
52850  * @param {Object} config Configuration options
52851  */
52852 Roo.form.DayPicker= function(config){
52853     Roo.form.DayPicker.superclass.constructor.call(this, config);
52854      
52855 };
52856
52857 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52858     /**
52859      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52860      */
52861     focusClass : undefined,
52862     /**
52863      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52864      */
52865     fieldClass: "x-form-field",
52866    
52867     /**
52868      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52869      * {tag: "input", type: "checkbox", autocomplete: "off"})
52870      */
52871     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52872     
52873    
52874     actionMode : 'viewEl', 
52875     //
52876     // private
52877  
52878     inputType : 'hidden',
52879     
52880      
52881     inputElement: false, // real input element?
52882     basedOn: false, // ????
52883     
52884     isFormField: true, // not sure where this is needed!!!!
52885
52886     onResize : function(){
52887         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52888         if(!this.boxLabel){
52889             this.el.alignTo(this.wrap, 'c-c');
52890         }
52891     },
52892
52893     initEvents : function(){
52894         Roo.form.Checkbox.superclass.initEvents.call(this);
52895         this.el.on("click", this.onClick,  this);
52896         this.el.on("change", this.onClick,  this);
52897     },
52898
52899
52900     getResizeEl : function(){
52901         return this.wrap;
52902     },
52903
52904     getPositionEl : function(){
52905         return this.wrap;
52906     },
52907
52908     
52909     // private
52910     onRender : function(ct, position){
52911         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52912        
52913         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52914         
52915         var r1 = '<table><tr>';
52916         var r2 = '<tr class="x-form-daypick-icons">';
52917         for (var i=0; i < 7; i++) {
52918             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52919             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52920         }
52921         
52922         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52923         viewEl.select('img').on('click', this.onClick, this);
52924         this.viewEl = viewEl;   
52925         
52926         
52927         // this will not work on Chrome!!!
52928         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52929         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52930         
52931         
52932           
52933
52934     },
52935
52936     // private
52937     initValue : Roo.emptyFn,
52938
52939     /**
52940      * Returns the checked state of the checkbox.
52941      * @return {Boolean} True if checked, else false
52942      */
52943     getValue : function(){
52944         return this.el.dom.value;
52945         
52946     },
52947
52948         // private
52949     onClick : function(e){ 
52950         //this.setChecked(!this.checked);
52951         Roo.get(e.target).toggleClass('x-menu-item-checked');
52952         this.refreshValue();
52953         //if(this.el.dom.checked != this.checked){
52954         //    this.setValue(this.el.dom.checked);
52955        // }
52956     },
52957     
52958     // private
52959     refreshValue : function()
52960     {
52961         var val = '';
52962         this.viewEl.select('img',true).each(function(e,i,n)  {
52963             val += e.is(".x-menu-item-checked") ? String(n) : '';
52964         });
52965         this.setValue(val, true);
52966     },
52967
52968     /**
52969      * Sets the checked state of the checkbox.
52970      * On is always based on a string comparison between inputValue and the param.
52971      * @param {Boolean/String} value - the value to set 
52972      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52973      */
52974     setValue : function(v,suppressEvent){
52975         if (!this.el.dom) {
52976             return;
52977         }
52978         var old = this.el.dom.value ;
52979         this.el.dom.value = v;
52980         if (suppressEvent) {
52981             return ;
52982         }
52983          
52984         // update display..
52985         this.viewEl.select('img',true).each(function(e,i,n)  {
52986             
52987             var on = e.is(".x-menu-item-checked");
52988             var newv = v.indexOf(String(n)) > -1;
52989             if (on != newv) {
52990                 e.toggleClass('x-menu-item-checked');
52991             }
52992             
52993         });
52994         
52995         
52996         this.fireEvent('change', this, v, old);
52997         
52998         
52999     },
53000    
53001     // handle setting of hidden value by some other method!!?!?
53002     setFromHidden: function()
53003     {
53004         if(!this.el){
53005             return;
53006         }
53007         //console.log("SET FROM HIDDEN");
53008         //alert('setFrom hidden');
53009         this.setValue(this.el.dom.value);
53010     },
53011     
53012     onDestroy : function()
53013     {
53014         if(this.viewEl){
53015             Roo.get(this.viewEl).remove();
53016         }
53017          
53018         Roo.form.DayPicker.superclass.onDestroy.call(this);
53019     }
53020
53021 });/*
53022  * RooJS Library 1.1.1
53023  * Copyright(c) 2008-2011  Alan Knowles
53024  *
53025  * License - LGPL
53026  */
53027  
53028
53029 /**
53030  * @class Roo.form.ComboCheck
53031  * @extends Roo.form.ComboBox
53032  * A combobox for multiple select items.
53033  *
53034  * FIXME - could do with a reset button..
53035  * 
53036  * @constructor
53037  * Create a new ComboCheck
53038  * @param {Object} config Configuration options
53039  */
53040 Roo.form.ComboCheck = function(config){
53041     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53042     // should verify some data...
53043     // like
53044     // hiddenName = required..
53045     // displayField = required
53046     // valudField == required
53047     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53048     var _t = this;
53049     Roo.each(req, function(e) {
53050         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53051             throw "Roo.form.ComboCheck : missing value for: " + e;
53052         }
53053     });
53054     
53055     
53056 };
53057
53058 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53059      
53060      
53061     editable : false,
53062      
53063     selectedClass: 'x-menu-item-checked', 
53064     
53065     // private
53066     onRender : function(ct, position){
53067         var _t = this;
53068         
53069         
53070         
53071         if(!this.tpl){
53072             var cls = 'x-combo-list';
53073
53074             
53075             this.tpl =  new Roo.Template({
53076                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53077                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53078                    '<span>{' + this.displayField + '}</span>' +
53079                     '</div>' 
53080                 
53081             });
53082         }
53083  
53084         
53085         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53086         this.view.singleSelect = false;
53087         this.view.multiSelect = true;
53088         this.view.toggleSelect = true;
53089         this.pageTb.add(new Roo.Toolbar.Fill(), {
53090             
53091             text: 'Done',
53092             handler: function()
53093             {
53094                 _t.collapse();
53095             }
53096         });
53097     },
53098     
53099     onViewOver : function(e, t){
53100         // do nothing...
53101         return;
53102         
53103     },
53104     
53105     onViewClick : function(doFocus,index){
53106         return;
53107         
53108     },
53109     select: function () {
53110         //Roo.log("SELECT CALLED");
53111     },
53112      
53113     selectByValue : function(xv, scrollIntoView){
53114         var ar = this.getValueArray();
53115         var sels = [];
53116         
53117         Roo.each(ar, function(v) {
53118             if(v === undefined || v === null){
53119                 return;
53120             }
53121             var r = this.findRecord(this.valueField, v);
53122             if(r){
53123                 sels.push(this.store.indexOf(r))
53124                 
53125             }
53126         },this);
53127         this.view.select(sels);
53128         return false;
53129     },
53130     
53131     
53132     
53133     onSelect : function(record, index){
53134        // Roo.log("onselect Called");
53135        // this is only called by the clear button now..
53136         this.view.clearSelections();
53137         this.setValue('[]');
53138         if (this.value != this.valueBefore) {
53139             this.fireEvent('change', this, this.value, this.valueBefore);
53140             this.valueBefore = this.value;
53141         }
53142     },
53143     getValueArray : function()
53144     {
53145         var ar = [] ;
53146         
53147         try {
53148             //Roo.log(this.value);
53149             if (typeof(this.value) == 'undefined') {
53150                 return [];
53151             }
53152             var ar = Roo.decode(this.value);
53153             return  ar instanceof Array ? ar : []; //?? valid?
53154             
53155         } catch(e) {
53156             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53157             return [];
53158         }
53159          
53160     },
53161     expand : function ()
53162     {
53163         
53164         Roo.form.ComboCheck.superclass.expand.call(this);
53165         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53166         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53167         
53168
53169     },
53170     
53171     collapse : function(){
53172         Roo.form.ComboCheck.superclass.collapse.call(this);
53173         var sl = this.view.getSelectedIndexes();
53174         var st = this.store;
53175         var nv = [];
53176         var tv = [];
53177         var r;
53178         Roo.each(sl, function(i) {
53179             r = st.getAt(i);
53180             nv.push(r.get(this.valueField));
53181         },this);
53182         this.setValue(Roo.encode(nv));
53183         if (this.value != this.valueBefore) {
53184
53185             this.fireEvent('change', this, this.value, this.valueBefore);
53186             this.valueBefore = this.value;
53187         }
53188         
53189     },
53190     
53191     setValue : function(v){
53192         // Roo.log(v);
53193         this.value = v;
53194         
53195         var vals = this.getValueArray();
53196         var tv = [];
53197         Roo.each(vals, function(k) {
53198             var r = this.findRecord(this.valueField, k);
53199             if(r){
53200                 tv.push(r.data[this.displayField]);
53201             }else if(this.valueNotFoundText !== undefined){
53202                 tv.push( this.valueNotFoundText );
53203             }
53204         },this);
53205        // Roo.log(tv);
53206         
53207         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53208         this.hiddenField.value = v;
53209         this.value = v;
53210     }
53211     
53212 });/*
53213  * Based on:
53214  * Ext JS Library 1.1.1
53215  * Copyright(c) 2006-2007, Ext JS, LLC.
53216  *
53217  * Originally Released Under LGPL - original licence link has changed is not relivant.
53218  *
53219  * Fork - LGPL
53220  * <script type="text/javascript">
53221  */
53222  
53223 /**
53224  * @class Roo.form.Signature
53225  * @extends Roo.form.Field
53226  * Signature field.  
53227  * @constructor
53228  * 
53229  * @param {Object} config Configuration options
53230  */
53231
53232 Roo.form.Signature = function(config){
53233     Roo.form.Signature.superclass.constructor.call(this, config);
53234     
53235     this.addEvents({// not in used??
53236          /**
53237          * @event confirm
53238          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53239              * @param {Roo.form.Signature} combo This combo box
53240              */
53241         'confirm' : true,
53242         /**
53243          * @event reset
53244          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53245              * @param {Roo.form.ComboBox} combo This combo box
53246              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53247              */
53248         'reset' : true
53249     });
53250 };
53251
53252 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53253     /**
53254      * @cfg {Object} labels Label to use when rendering a form.
53255      * defaults to 
53256      * labels : { 
53257      *      clear : "Clear",
53258      *      confirm : "Confirm"
53259      *  }
53260      */
53261     labels : { 
53262         clear : "Clear",
53263         confirm : "Confirm"
53264     },
53265     /**
53266      * @cfg {Number} width The signature panel width (defaults to 300)
53267      */
53268     width: 300,
53269     /**
53270      * @cfg {Number} height The signature panel height (defaults to 100)
53271      */
53272     height : 100,
53273     /**
53274      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53275      */
53276     allowBlank : false,
53277     
53278     //private
53279     // {Object} signPanel The signature SVG panel element (defaults to {})
53280     signPanel : {},
53281     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53282     isMouseDown : false,
53283     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53284     isConfirmed : false,
53285     // {String} signatureTmp SVG mapping string (defaults to empty string)
53286     signatureTmp : '',
53287     
53288     
53289     defaultAutoCreate : { // modified by initCompnoent..
53290         tag: "input",
53291         type:"hidden"
53292     },
53293
53294     // private
53295     onRender : function(ct, position){
53296         
53297         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53298         
53299         this.wrap = this.el.wrap({
53300             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53301         });
53302         
53303         this.createToolbar(this);
53304         this.signPanel = this.wrap.createChild({
53305                 tag: 'div',
53306                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53307             }, this.el
53308         );
53309             
53310         this.svgID = Roo.id();
53311         this.svgEl = this.signPanel.createChild({
53312               xmlns : 'http://www.w3.org/2000/svg',
53313               tag : 'svg',
53314               id : this.svgID + "-svg",
53315               width: this.width,
53316               height: this.height,
53317               viewBox: '0 0 '+this.width+' '+this.height,
53318               cn : [
53319                 {
53320                     tag: "rect",
53321                     id: this.svgID + "-svg-r",
53322                     width: this.width,
53323                     height: this.height,
53324                     fill: "#ffa"
53325                 },
53326                 {
53327                     tag: "line",
53328                     id: this.svgID + "-svg-l",
53329                     x1: "0", // start
53330                     y1: (this.height*0.8), // start set the line in 80% of height
53331                     x2: this.width, // end
53332                     y2: (this.height*0.8), // end set the line in 80% of height
53333                     'stroke': "#666",
53334                     'stroke-width': "1",
53335                     'stroke-dasharray': "3",
53336                     'shape-rendering': "crispEdges",
53337                     'pointer-events': "none"
53338                 },
53339                 {
53340                     tag: "path",
53341                     id: this.svgID + "-svg-p",
53342                     'stroke': "navy",
53343                     'stroke-width': "3",
53344                     'fill': "none",
53345                     'pointer-events': 'none'
53346                 }
53347               ]
53348         });
53349         this.createSVG();
53350         this.svgBox = this.svgEl.dom.getScreenCTM();
53351     },
53352     createSVG : function(){ 
53353         var svg = this.signPanel;
53354         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53355         var t = this;
53356
53357         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53358         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53359         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53360         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53361         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53362         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53363         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53364         
53365     },
53366     isTouchEvent : function(e){
53367         return e.type.match(/^touch/);
53368     },
53369     getCoords : function (e) {
53370         var pt    = this.svgEl.dom.createSVGPoint();
53371         pt.x = e.clientX; 
53372         pt.y = e.clientY;
53373         if (this.isTouchEvent(e)) {
53374             pt.x =  e.targetTouches[0].clientX;
53375             pt.y = e.targetTouches[0].clientY;
53376         }
53377         var a = this.svgEl.dom.getScreenCTM();
53378         var b = a.inverse();
53379         var mx = pt.matrixTransform(b);
53380         return mx.x + ',' + mx.y;
53381     },
53382     //mouse event headler 
53383     down : function (e) {
53384         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53385         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53386         
53387         this.isMouseDown = true;
53388         
53389         e.preventDefault();
53390     },
53391     move : function (e) {
53392         if (this.isMouseDown) {
53393             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53394             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53395         }
53396         
53397         e.preventDefault();
53398     },
53399     up : function (e) {
53400         this.isMouseDown = false;
53401         var sp = this.signatureTmp.split(' ');
53402         
53403         if(sp.length > 1){
53404             if(!sp[sp.length-2].match(/^L/)){
53405                 sp.pop();
53406                 sp.pop();
53407                 sp.push("");
53408                 this.signatureTmp = sp.join(" ");
53409             }
53410         }
53411         if(this.getValue() != this.signatureTmp){
53412             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53413             this.isConfirmed = false;
53414         }
53415         e.preventDefault();
53416     },
53417     
53418     /**
53419      * Protected method that will not generally be called directly. It
53420      * is called when the editor creates its toolbar. Override this method if you need to
53421      * add custom toolbar buttons.
53422      * @param {HtmlEditor} editor
53423      */
53424     createToolbar : function(editor){
53425          function btn(id, toggle, handler){
53426             var xid = fid + '-'+ id ;
53427             return {
53428                 id : xid,
53429                 cmd : id,
53430                 cls : 'x-btn-icon x-edit-'+id,
53431                 enableToggle:toggle !== false,
53432                 scope: editor, // was editor...
53433                 handler:handler||editor.relayBtnCmd,
53434                 clickEvent:'mousedown',
53435                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53436                 tabIndex:-1
53437             };
53438         }
53439         
53440         
53441         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53442         this.tb = tb;
53443         this.tb.add(
53444            {
53445                 cls : ' x-signature-btn x-signature-'+id,
53446                 scope: editor, // was editor...
53447                 handler: this.reset,
53448                 clickEvent:'mousedown',
53449                 text: this.labels.clear
53450             },
53451             {
53452                  xtype : 'Fill',
53453                  xns: Roo.Toolbar
53454             }, 
53455             {
53456                 cls : '  x-signature-btn x-signature-'+id,
53457                 scope: editor, // was editor...
53458                 handler: this.confirmHandler,
53459                 clickEvent:'mousedown',
53460                 text: this.labels.confirm
53461             }
53462         );
53463     
53464     },
53465     //public
53466     /**
53467      * when user is clicked confirm then show this image.....
53468      * 
53469      * @return {String} Image Data URI
53470      */
53471     getImageDataURI : function(){
53472         var svg = this.svgEl.dom.parentNode.innerHTML;
53473         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53474         return src; 
53475     },
53476     /**
53477      * 
53478      * @return {Boolean} this.isConfirmed
53479      */
53480     getConfirmed : function(){
53481         return this.isConfirmed;
53482     },
53483     /**
53484      * 
53485      * @return {Number} this.width
53486      */
53487     getWidth : function(){
53488         return this.width;
53489     },
53490     /**
53491      * 
53492      * @return {Number} this.height
53493      */
53494     getHeight : function(){
53495         return this.height;
53496     },
53497     // private
53498     getSignature : function(){
53499         return this.signatureTmp;
53500     },
53501     // private
53502     reset : function(){
53503         this.signatureTmp = '';
53504         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53505         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53506         this.isConfirmed = false;
53507         Roo.form.Signature.superclass.reset.call(this);
53508     },
53509     setSignature : function(s){
53510         this.signatureTmp = s;
53511         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53512         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53513         this.setValue(s);
53514         this.isConfirmed = false;
53515         Roo.form.Signature.superclass.reset.call(this);
53516     }, 
53517     test : function(){
53518 //        Roo.log(this.signPanel.dom.contentWindow.up())
53519     },
53520     //private
53521     setConfirmed : function(){
53522         
53523         
53524         
53525 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53526     },
53527     // private
53528     confirmHandler : function(){
53529         if(!this.getSignature()){
53530             return;
53531         }
53532         
53533         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53534         this.setValue(this.getSignature());
53535         this.isConfirmed = true;
53536         
53537         this.fireEvent('confirm', this);
53538     },
53539     // private
53540     // Subclasses should provide the validation implementation by overriding this
53541     validateValue : function(value){
53542         if(this.allowBlank){
53543             return true;
53544         }
53545         
53546         if(this.isConfirmed){
53547             return true;
53548         }
53549         return false;
53550     }
53551 });/*
53552  * Based on:
53553  * Ext JS Library 1.1.1
53554  * Copyright(c) 2006-2007, Ext JS, LLC.
53555  *
53556  * Originally Released Under LGPL - original licence link has changed is not relivant.
53557  *
53558  * Fork - LGPL
53559  * <script type="text/javascript">
53560  */
53561  
53562
53563 /**
53564  * @class Roo.form.ComboBox
53565  * @extends Roo.form.TriggerField
53566  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53567  * @constructor
53568  * Create a new ComboBox.
53569  * @param {Object} config Configuration options
53570  */
53571 Roo.form.Select = function(config){
53572     Roo.form.Select.superclass.constructor.call(this, config);
53573      
53574 };
53575
53576 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53577     /**
53578      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53579      */
53580     /**
53581      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53582      * rendering into an Roo.Editor, defaults to false)
53583      */
53584     /**
53585      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53586      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53587      */
53588     /**
53589      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53590      */
53591     /**
53592      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53593      * the dropdown list (defaults to undefined, with no header element)
53594      */
53595
53596      /**
53597      * @cfg {String/Roo.Template} tpl The template to use to render the output
53598      */
53599      
53600     // private
53601     defaultAutoCreate : {tag: "select"  },
53602     /**
53603      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53604      */
53605     listWidth: undefined,
53606     /**
53607      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53608      * mode = 'remote' or 'text' if mode = 'local')
53609      */
53610     displayField: undefined,
53611     /**
53612      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53613      * mode = 'remote' or 'value' if mode = 'local'). 
53614      * Note: use of a valueField requires the user make a selection
53615      * in order for a value to be mapped.
53616      */
53617     valueField: undefined,
53618     
53619     
53620     /**
53621      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53622      * field's data value (defaults to the underlying DOM element's name)
53623      */
53624     hiddenName: undefined,
53625     /**
53626      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53627      */
53628     listClass: '',
53629     /**
53630      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53631      */
53632     selectedClass: 'x-combo-selected',
53633     /**
53634      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53635      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53636      * which displays a downward arrow icon).
53637      */
53638     triggerClass : 'x-form-arrow-trigger',
53639     /**
53640      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53641      */
53642     shadow:'sides',
53643     /**
53644      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53645      * anchor positions (defaults to 'tl-bl')
53646      */
53647     listAlign: 'tl-bl?',
53648     /**
53649      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53650      */
53651     maxHeight: 300,
53652     /**
53653      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53654      * query specified by the allQuery config option (defaults to 'query')
53655      */
53656     triggerAction: 'query',
53657     /**
53658      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53659      * (defaults to 4, does not apply if editable = false)
53660      */
53661     minChars : 4,
53662     /**
53663      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53664      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53665      */
53666     typeAhead: false,
53667     /**
53668      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53669      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53670      */
53671     queryDelay: 500,
53672     /**
53673      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53674      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53675      */
53676     pageSize: 0,
53677     /**
53678      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53679      * when editable = true (defaults to false)
53680      */
53681     selectOnFocus:false,
53682     /**
53683      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53684      */
53685     queryParam: 'query',
53686     /**
53687      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53688      * when mode = 'remote' (defaults to 'Loading...')
53689      */
53690     loadingText: 'Loading...',
53691     /**
53692      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53693      */
53694     resizable: false,
53695     /**
53696      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53697      */
53698     handleHeight : 8,
53699     /**
53700      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53701      * traditional select (defaults to true)
53702      */
53703     editable: true,
53704     /**
53705      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53706      */
53707     allQuery: '',
53708     /**
53709      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53710      */
53711     mode: 'remote',
53712     /**
53713      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53714      * listWidth has a higher value)
53715      */
53716     minListWidth : 70,
53717     /**
53718      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53719      * allow the user to set arbitrary text into the field (defaults to false)
53720      */
53721     forceSelection:false,
53722     /**
53723      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53724      * if typeAhead = true (defaults to 250)
53725      */
53726     typeAheadDelay : 250,
53727     /**
53728      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53729      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53730      */
53731     valueNotFoundText : undefined,
53732     
53733     /**
53734      * @cfg {String} defaultValue The value displayed after loading the store.
53735      */
53736     defaultValue: '',
53737     
53738     /**
53739      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53740      */
53741     blockFocus : false,
53742     
53743     /**
53744      * @cfg {Boolean} disableClear Disable showing of clear button.
53745      */
53746     disableClear : false,
53747     /**
53748      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53749      */
53750     alwaysQuery : false,
53751     
53752     //private
53753     addicon : false,
53754     editicon: false,
53755     
53756     // element that contains real text value.. (when hidden is used..)
53757      
53758     // private
53759     onRender : function(ct, position){
53760         Roo.form.Field.prototype.onRender.call(this, ct, position);
53761         
53762         if(this.store){
53763             this.store.on('beforeload', this.onBeforeLoad, this);
53764             this.store.on('load', this.onLoad, this);
53765             this.store.on('loadexception', this.onLoadException, this);
53766             this.store.load({});
53767         }
53768         
53769         
53770         
53771     },
53772
53773     // private
53774     initEvents : function(){
53775         //Roo.form.ComboBox.superclass.initEvents.call(this);
53776  
53777     },
53778
53779     onDestroy : function(){
53780        
53781         if(this.store){
53782             this.store.un('beforeload', this.onBeforeLoad, this);
53783             this.store.un('load', this.onLoad, this);
53784             this.store.un('loadexception', this.onLoadException, this);
53785         }
53786         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53787     },
53788
53789     // private
53790     fireKey : function(e){
53791         if(e.isNavKeyPress() && !this.list.isVisible()){
53792             this.fireEvent("specialkey", this, e);
53793         }
53794     },
53795
53796     // private
53797     onResize: function(w, h){
53798         
53799         return; 
53800     
53801         
53802     },
53803
53804     /**
53805      * Allow or prevent the user from directly editing the field text.  If false is passed,
53806      * the user will only be able to select from the items defined in the dropdown list.  This method
53807      * is the runtime equivalent of setting the 'editable' config option at config time.
53808      * @param {Boolean} value True to allow the user to directly edit the field text
53809      */
53810     setEditable : function(value){
53811          
53812     },
53813
53814     // private
53815     onBeforeLoad : function(){
53816         
53817         Roo.log("Select before load");
53818         return;
53819     
53820         this.innerList.update(this.loadingText ?
53821                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53822         //this.restrictHeight();
53823         this.selectedIndex = -1;
53824     },
53825
53826     // private
53827     onLoad : function(){
53828
53829     
53830         var dom = this.el.dom;
53831         dom.innerHTML = '';
53832          var od = dom.ownerDocument;
53833          
53834         if (this.emptyText) {
53835             var op = od.createElement('option');
53836             op.setAttribute('value', '');
53837             op.innerHTML = String.format('{0}', this.emptyText);
53838             dom.appendChild(op);
53839         }
53840         if(this.store.getCount() > 0){
53841            
53842             var vf = this.valueField;
53843             var df = this.displayField;
53844             this.store.data.each(function(r) {
53845                 // which colmsn to use... testing - cdoe / title..
53846                 var op = od.createElement('option');
53847                 op.setAttribute('value', r.data[vf]);
53848                 op.innerHTML = String.format('{0}', r.data[df]);
53849                 dom.appendChild(op);
53850             });
53851             if (typeof(this.defaultValue != 'undefined')) {
53852                 this.setValue(this.defaultValue);
53853             }
53854             
53855              
53856         }else{
53857             //this.onEmptyResults();
53858         }
53859         //this.el.focus();
53860     },
53861     // private
53862     onLoadException : function()
53863     {
53864         dom.innerHTML = '';
53865             
53866         Roo.log("Select on load exception");
53867         return;
53868     
53869         this.collapse();
53870         Roo.log(this.store.reader.jsonData);
53871         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53872             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53873         }
53874         
53875         
53876     },
53877     // private
53878     onTypeAhead : function(){
53879          
53880     },
53881
53882     // private
53883     onSelect : function(record, index){
53884         Roo.log('on select?');
53885         return;
53886         if(this.fireEvent('beforeselect', this, record, index) !== false){
53887             this.setFromData(index > -1 ? record.data : false);
53888             this.collapse();
53889             this.fireEvent('select', this, record, index);
53890         }
53891     },
53892
53893     /**
53894      * Returns the currently selected field value or empty string if no value is set.
53895      * @return {String} value The selected value
53896      */
53897     getValue : function(){
53898         var dom = this.el.dom;
53899         this.value = dom.options[dom.selectedIndex].value;
53900         return this.value;
53901         
53902     },
53903
53904     /**
53905      * Clears any text/value currently set in the field
53906      */
53907     clearValue : function(){
53908         this.value = '';
53909         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53910         
53911     },
53912
53913     /**
53914      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53915      * will be displayed in the field.  If the value does not match the data value of an existing item,
53916      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53917      * Otherwise the field will be blank (although the value will still be set).
53918      * @param {String} value The value to match
53919      */
53920     setValue : function(v){
53921         var d = this.el.dom;
53922         for (var i =0; i < d.options.length;i++) {
53923             if (v == d.options[i].value) {
53924                 d.selectedIndex = i;
53925                 this.value = v;
53926                 return;
53927             }
53928         }
53929         this.clearValue();
53930     },
53931     /**
53932      * @property {Object} the last set data for the element
53933      */
53934     
53935     lastData : false,
53936     /**
53937      * Sets the value of the field based on a object which is related to the record format for the store.
53938      * @param {Object} value the value to set as. or false on reset?
53939      */
53940     setFromData : function(o){
53941         Roo.log('setfrom data?');
53942          
53943         
53944         
53945     },
53946     // private
53947     reset : function(){
53948         this.clearValue();
53949     },
53950     // private
53951     findRecord : function(prop, value){
53952         
53953         return false;
53954     
53955         var record;
53956         if(this.store.getCount() > 0){
53957             this.store.each(function(r){
53958                 if(r.data[prop] == value){
53959                     record = r;
53960                     return false;
53961                 }
53962                 return true;
53963             });
53964         }
53965         return record;
53966     },
53967     
53968     getName: function()
53969     {
53970         // returns hidden if it's set..
53971         if (!this.rendered) {return ''};
53972         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53973         
53974     },
53975      
53976
53977     
53978
53979     // private
53980     onEmptyResults : function(){
53981         Roo.log('empty results');
53982         //this.collapse();
53983     },
53984
53985     /**
53986      * Returns true if the dropdown list is expanded, else false.
53987      */
53988     isExpanded : function(){
53989         return false;
53990     },
53991
53992     /**
53993      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53994      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53995      * @param {String} value The data value of the item to select
53996      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53997      * selected item if it is not currently in view (defaults to true)
53998      * @return {Boolean} True if the value matched an item in the list, else false
53999      */
54000     selectByValue : function(v, scrollIntoView){
54001         Roo.log('select By Value');
54002         return false;
54003     
54004         if(v !== undefined && v !== null){
54005             var r = this.findRecord(this.valueField || this.displayField, v);
54006             if(r){
54007                 this.select(this.store.indexOf(r), scrollIntoView);
54008                 return true;
54009             }
54010         }
54011         return false;
54012     },
54013
54014     /**
54015      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54016      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54017      * @param {Number} index The zero-based index of the list item to select
54018      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54019      * selected item if it is not currently in view (defaults to true)
54020      */
54021     select : function(index, scrollIntoView){
54022         Roo.log('select ');
54023         return  ;
54024         
54025         this.selectedIndex = index;
54026         this.view.select(index);
54027         if(scrollIntoView !== false){
54028             var el = this.view.getNode(index);
54029             if(el){
54030                 this.innerList.scrollChildIntoView(el, false);
54031             }
54032         }
54033     },
54034
54035       
54036
54037     // private
54038     validateBlur : function(){
54039         
54040         return;
54041         
54042     },
54043
54044     // private
54045     initQuery : function(){
54046         this.doQuery(this.getRawValue());
54047     },
54048
54049     // private
54050     doForce : function(){
54051         if(this.el.dom.value.length > 0){
54052             this.el.dom.value =
54053                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54054              
54055         }
54056     },
54057
54058     /**
54059      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54060      * query allowing the query action to be canceled if needed.
54061      * @param {String} query The SQL query to execute
54062      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54063      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54064      * saved in the current store (defaults to false)
54065      */
54066     doQuery : function(q, forceAll){
54067         
54068         Roo.log('doQuery?');
54069         if(q === undefined || q === null){
54070             q = '';
54071         }
54072         var qe = {
54073             query: q,
54074             forceAll: forceAll,
54075             combo: this,
54076             cancel:false
54077         };
54078         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54079             return false;
54080         }
54081         q = qe.query;
54082         forceAll = qe.forceAll;
54083         if(forceAll === true || (q.length >= this.minChars)){
54084             if(this.lastQuery != q || this.alwaysQuery){
54085                 this.lastQuery = q;
54086                 if(this.mode == 'local'){
54087                     this.selectedIndex = -1;
54088                     if(forceAll){
54089                         this.store.clearFilter();
54090                     }else{
54091                         this.store.filter(this.displayField, q);
54092                     }
54093                     this.onLoad();
54094                 }else{
54095                     this.store.baseParams[this.queryParam] = q;
54096                     this.store.load({
54097                         params: this.getParams(q)
54098                     });
54099                     this.expand();
54100                 }
54101             }else{
54102                 this.selectedIndex = -1;
54103                 this.onLoad();   
54104             }
54105         }
54106     },
54107
54108     // private
54109     getParams : function(q){
54110         var p = {};
54111         //p[this.queryParam] = q;
54112         if(this.pageSize){
54113             p.start = 0;
54114             p.limit = this.pageSize;
54115         }
54116         return p;
54117     },
54118
54119     /**
54120      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54121      */
54122     collapse : function(){
54123         
54124     },
54125
54126     // private
54127     collapseIf : function(e){
54128         
54129     },
54130
54131     /**
54132      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54133      */
54134     expand : function(){
54135         
54136     } ,
54137
54138     // private
54139      
54140
54141     /** 
54142     * @cfg {Boolean} grow 
54143     * @hide 
54144     */
54145     /** 
54146     * @cfg {Number} growMin 
54147     * @hide 
54148     */
54149     /** 
54150     * @cfg {Number} growMax 
54151     * @hide 
54152     */
54153     /**
54154      * @hide
54155      * @method autoSize
54156      */
54157     
54158     setWidth : function()
54159     {
54160         
54161     },
54162     getResizeEl : function(){
54163         return this.el;
54164     }
54165 });//<script type="text/javasscript">
54166  
54167
54168 /**
54169  * @class Roo.DDView
54170  * A DnD enabled version of Roo.View.
54171  * @param {Element/String} container The Element in which to create the View.
54172  * @param {String} tpl The template string used to create the markup for each element of the View
54173  * @param {Object} config The configuration properties. These include all the config options of
54174  * {@link Roo.View} plus some specific to this class.<br>
54175  * <p>
54176  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54177  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54178  * <p>
54179  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54180 .x-view-drag-insert-above {
54181         border-top:1px dotted #3366cc;
54182 }
54183 .x-view-drag-insert-below {
54184         border-bottom:1px dotted #3366cc;
54185 }
54186 </code></pre>
54187  * 
54188  */
54189  
54190 Roo.DDView = function(container, tpl, config) {
54191     Roo.DDView.superclass.constructor.apply(this, arguments);
54192     this.getEl().setStyle("outline", "0px none");
54193     this.getEl().unselectable();
54194     if (this.dragGroup) {
54195         this.setDraggable(this.dragGroup.split(","));
54196     }
54197     if (this.dropGroup) {
54198         this.setDroppable(this.dropGroup.split(","));
54199     }
54200     if (this.deletable) {
54201         this.setDeletable();
54202     }
54203     this.isDirtyFlag = false;
54204         this.addEvents({
54205                 "drop" : true
54206         });
54207 };
54208
54209 Roo.extend(Roo.DDView, Roo.View, {
54210 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54211 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54212 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54213 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54214
54215         isFormField: true,
54216
54217         reset: Roo.emptyFn,
54218         
54219         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54220
54221         validate: function() {
54222                 return true;
54223         },
54224         
54225         destroy: function() {
54226                 this.purgeListeners();
54227                 this.getEl.removeAllListeners();
54228                 this.getEl().remove();
54229                 if (this.dragZone) {
54230                         if (this.dragZone.destroy) {
54231                                 this.dragZone.destroy();
54232                         }
54233                 }
54234                 if (this.dropZone) {
54235                         if (this.dropZone.destroy) {
54236                                 this.dropZone.destroy();
54237                         }
54238                 }
54239         },
54240
54241 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54242         getName: function() {
54243                 return this.name;
54244         },
54245
54246 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54247         setValue: function(v) {
54248                 if (!this.store) {
54249                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54250                 }
54251                 var data = {};
54252                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54253                 this.store.proxy = new Roo.data.MemoryProxy(data);
54254                 this.store.load();
54255         },
54256
54257 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54258         getValue: function() {
54259                 var result = '(';
54260                 this.store.each(function(rec) {
54261                         result += rec.id + ',';
54262                 });
54263                 return result.substr(0, result.length - 1) + ')';
54264         },
54265         
54266         getIds: function() {
54267                 var i = 0, result = new Array(this.store.getCount());
54268                 this.store.each(function(rec) {
54269                         result[i++] = rec.id;
54270                 });
54271                 return result;
54272         },
54273         
54274         isDirty: function() {
54275                 return this.isDirtyFlag;
54276         },
54277
54278 /**
54279  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54280  *      whole Element becomes the target, and this causes the drop gesture to append.
54281  */
54282     getTargetFromEvent : function(e) {
54283                 var target = e.getTarget();
54284                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54285                 target = target.parentNode;
54286                 }
54287                 if (!target) {
54288                         target = this.el.dom.lastChild || this.el.dom;
54289                 }
54290                 return target;
54291     },
54292
54293 /**
54294  *      Create the drag data which consists of an object which has the property "ddel" as
54295  *      the drag proxy element. 
54296  */
54297     getDragData : function(e) {
54298         var target = this.findItemFromChild(e.getTarget());
54299                 if(target) {
54300                         this.handleSelection(e);
54301                         var selNodes = this.getSelectedNodes();
54302             var dragData = {
54303                 source: this,
54304                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54305                 nodes: selNodes,
54306                 records: []
54307                         };
54308                         var selectedIndices = this.getSelectedIndexes();
54309                         for (var i = 0; i < selectedIndices.length; i++) {
54310                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54311                         }
54312                         if (selNodes.length == 1) {
54313                                 dragData.ddel = target.cloneNode(true); // the div element
54314                         } else {
54315                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54316                                 div.className = 'multi-proxy';
54317                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54318                                         div.appendChild(selNodes[i].cloneNode(true));
54319                                 }
54320                                 dragData.ddel = div;
54321                         }
54322             //console.log(dragData)
54323             //console.log(dragData.ddel.innerHTML)
54324                         return dragData;
54325                 }
54326         //console.log('nodragData')
54327                 return false;
54328     },
54329     
54330 /**     Specify to which ddGroup items in this DDView may be dragged. */
54331     setDraggable: function(ddGroup) {
54332         if (ddGroup instanceof Array) {
54333                 Roo.each(ddGroup, this.setDraggable, this);
54334                 return;
54335         }
54336         if (this.dragZone) {
54337                 this.dragZone.addToGroup(ddGroup);
54338         } else {
54339                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54340                                 containerScroll: true,
54341                                 ddGroup: ddGroup 
54342
54343                         });
54344 //                      Draggability implies selection. DragZone's mousedown selects the element.
54345                         if (!this.multiSelect) { this.singleSelect = true; }
54346
54347 //                      Wire the DragZone's handlers up to methods in *this*
54348                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54349                 }
54350     },
54351
54352 /**     Specify from which ddGroup this DDView accepts drops. */
54353     setDroppable: function(ddGroup) {
54354         if (ddGroup instanceof Array) {
54355                 Roo.each(ddGroup, this.setDroppable, this);
54356                 return;
54357         }
54358         if (this.dropZone) {
54359                 this.dropZone.addToGroup(ddGroup);
54360         } else {
54361                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54362                                 containerScroll: true,
54363                                 ddGroup: ddGroup
54364                         });
54365
54366 //                      Wire the DropZone's handlers up to methods in *this*
54367                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54368                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54369                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54370                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54371                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54372                 }
54373     },
54374
54375 /**     Decide whether to drop above or below a View node. */
54376     getDropPoint : function(e, n, dd){
54377         if (n == this.el.dom) { return "above"; }
54378                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54379                 var c = t + (b - t) / 2;
54380                 var y = Roo.lib.Event.getPageY(e);
54381                 if(y <= c) {
54382                         return "above";
54383                 }else{
54384                         return "below";
54385                 }
54386     },
54387
54388     onNodeEnter : function(n, dd, e, data){
54389                 return false;
54390     },
54391     
54392     onNodeOver : function(n, dd, e, data){
54393                 var pt = this.getDropPoint(e, n, dd);
54394                 // set the insert point style on the target node
54395                 var dragElClass = this.dropNotAllowed;
54396                 if (pt) {
54397                         var targetElClass;
54398                         if (pt == "above"){
54399                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54400                                 targetElClass = "x-view-drag-insert-above";
54401                         } else {
54402                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54403                                 targetElClass = "x-view-drag-insert-below";
54404                         }
54405                         if (this.lastInsertClass != targetElClass){
54406                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54407                                 this.lastInsertClass = targetElClass;
54408                         }
54409                 }
54410                 return dragElClass;
54411         },
54412
54413     onNodeOut : function(n, dd, e, data){
54414                 this.removeDropIndicators(n);
54415     },
54416
54417     onNodeDrop : function(n, dd, e, data){
54418         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54419                 return false;
54420         }
54421         var pt = this.getDropPoint(e, n, dd);
54422                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54423                 if (pt == "below") { insertAt++; }
54424                 for (var i = 0; i < data.records.length; i++) {
54425                         var r = data.records[i];
54426                         var dup = this.store.getById(r.id);
54427                         if (dup && (dd != this.dragZone)) {
54428                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54429                         } else {
54430                                 if (data.copy) {
54431                                         this.store.insert(insertAt++, r.copy());
54432                                 } else {
54433                                         data.source.isDirtyFlag = true;
54434                                         r.store.remove(r);
54435                                         this.store.insert(insertAt++, r);
54436                                 }
54437                                 this.isDirtyFlag = true;
54438                         }
54439                 }
54440                 this.dragZone.cachedTarget = null;
54441                 return true;
54442     },
54443
54444     removeDropIndicators : function(n){
54445                 if(n){
54446                         Roo.fly(n).removeClass([
54447                                 "x-view-drag-insert-above",
54448                                 "x-view-drag-insert-below"]);
54449                         this.lastInsertClass = "_noclass";
54450                 }
54451     },
54452
54453 /**
54454  *      Utility method. Add a delete option to the DDView's context menu.
54455  *      @param {String} imageUrl The URL of the "delete" icon image.
54456  */
54457         setDeletable: function(imageUrl) {
54458                 if (!this.singleSelect && !this.multiSelect) {
54459                         this.singleSelect = true;
54460                 }
54461                 var c = this.getContextMenu();
54462                 this.contextMenu.on("itemclick", function(item) {
54463                         switch (item.id) {
54464                                 case "delete":
54465                                         this.remove(this.getSelectedIndexes());
54466                                         break;
54467                         }
54468                 }, this);
54469                 this.contextMenu.add({
54470                         icon: imageUrl,
54471                         id: "delete",
54472                         text: 'Delete'
54473                 });
54474         },
54475         
54476 /**     Return the context menu for this DDView. */
54477         getContextMenu: function() {
54478                 if (!this.contextMenu) {
54479 //                      Create the View's context menu
54480                         this.contextMenu = new Roo.menu.Menu({
54481                                 id: this.id + "-contextmenu"
54482                         });
54483                         this.el.on("contextmenu", this.showContextMenu, this);
54484                 }
54485                 return this.contextMenu;
54486         },
54487         
54488         disableContextMenu: function() {
54489                 if (this.contextMenu) {
54490                         this.el.un("contextmenu", this.showContextMenu, this);
54491                 }
54492         },
54493
54494         showContextMenu: function(e, item) {
54495         item = this.findItemFromChild(e.getTarget());
54496                 if (item) {
54497                         e.stopEvent();
54498                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54499                         this.contextMenu.showAt(e.getXY());
54500             }
54501     },
54502
54503 /**
54504  *      Remove {@link Roo.data.Record}s at the specified indices.
54505  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54506  */
54507     remove: function(selectedIndices) {
54508                 selectedIndices = [].concat(selectedIndices);
54509                 for (var i = 0; i < selectedIndices.length; i++) {
54510                         var rec = this.store.getAt(selectedIndices[i]);
54511                         this.store.remove(rec);
54512                 }
54513     },
54514
54515 /**
54516  *      Double click fires the event, but also, if this is draggable, and there is only one other
54517  *      related DropZone, it transfers the selected node.
54518  */
54519     onDblClick : function(e){
54520         var item = this.findItemFromChild(e.getTarget());
54521         if(item){
54522             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54523                 return false;
54524             }
54525             if (this.dragGroup) {
54526                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54527                     while (targets.indexOf(this.dropZone) > -1) {
54528                             targets.remove(this.dropZone);
54529                                 }
54530                     if (targets.length == 1) {
54531                                         this.dragZone.cachedTarget = null;
54532                         var el = Roo.get(targets[0].getEl());
54533                         var box = el.getBox(true);
54534                         targets[0].onNodeDrop(el.dom, {
54535                                 target: el.dom,
54536                                 xy: [box.x, box.y + box.height - 1]
54537                         }, null, this.getDragData(e));
54538                     }
54539                 }
54540         }
54541     },
54542     
54543     handleSelection: function(e) {
54544                 this.dragZone.cachedTarget = null;
54545         var item = this.findItemFromChild(e.getTarget());
54546         if (!item) {
54547                 this.clearSelections(true);
54548                 return;
54549         }
54550                 if (item && (this.multiSelect || this.singleSelect)){
54551                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54552                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54553                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54554                                 this.unselect(item);
54555                         } else {
54556                                 this.select(item, this.multiSelect && e.ctrlKey);
54557                                 this.lastSelection = item;
54558                         }
54559                 }
54560     },
54561
54562     onItemClick : function(item, index, e){
54563                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54564                         return false;
54565                 }
54566                 return true;
54567     },
54568
54569     unselect : function(nodeInfo, suppressEvent){
54570                 var node = this.getNode(nodeInfo);
54571                 if(node && this.isSelected(node)){
54572                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54573                                 Roo.fly(node).removeClass(this.selectedClass);
54574                                 this.selections.remove(node);
54575                                 if(!suppressEvent){
54576                                         this.fireEvent("selectionchange", this, this.selections);
54577                                 }
54578                         }
54579                 }
54580     }
54581 });
54582 /*
54583  * Based on:
54584  * Ext JS Library 1.1.1
54585  * Copyright(c) 2006-2007, Ext JS, LLC.
54586  *
54587  * Originally Released Under LGPL - original licence link has changed is not relivant.
54588  *
54589  * Fork - LGPL
54590  * <script type="text/javascript">
54591  */
54592  
54593 /**
54594  * @class Roo.LayoutManager
54595  * @extends Roo.util.Observable
54596  * Base class for layout managers.
54597  */
54598 Roo.LayoutManager = function(container, config){
54599     Roo.LayoutManager.superclass.constructor.call(this);
54600     this.el = Roo.get(container);
54601     // ie scrollbar fix
54602     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54603         document.body.scroll = "no";
54604     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54605         this.el.position('relative');
54606     }
54607     this.id = this.el.id;
54608     this.el.addClass("x-layout-container");
54609     /** false to disable window resize monitoring @type Boolean */
54610     this.monitorWindowResize = true;
54611     this.regions = {};
54612     this.addEvents({
54613         /**
54614          * @event layout
54615          * Fires when a layout is performed. 
54616          * @param {Roo.LayoutManager} this
54617          */
54618         "layout" : true,
54619         /**
54620          * @event regionresized
54621          * Fires when the user resizes a region. 
54622          * @param {Roo.LayoutRegion} region The resized region
54623          * @param {Number} newSize The new size (width for east/west, height for north/south)
54624          */
54625         "regionresized" : true,
54626         /**
54627          * @event regioncollapsed
54628          * Fires when a region is collapsed. 
54629          * @param {Roo.LayoutRegion} region The collapsed region
54630          */
54631         "regioncollapsed" : true,
54632         /**
54633          * @event regionexpanded
54634          * Fires when a region is expanded.  
54635          * @param {Roo.LayoutRegion} region The expanded region
54636          */
54637         "regionexpanded" : true
54638     });
54639     this.updating = false;
54640     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54641 };
54642
54643 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54644     /**
54645      * Returns true if this layout is currently being updated
54646      * @return {Boolean}
54647      */
54648     isUpdating : function(){
54649         return this.updating; 
54650     },
54651     
54652     /**
54653      * Suspend the LayoutManager from doing auto-layouts while
54654      * making multiple add or remove calls
54655      */
54656     beginUpdate : function(){
54657         this.updating = true;    
54658     },
54659     
54660     /**
54661      * Restore auto-layouts and optionally disable the manager from performing a layout
54662      * @param {Boolean} noLayout true to disable a layout update 
54663      */
54664     endUpdate : function(noLayout){
54665         this.updating = false;
54666         if(!noLayout){
54667             this.layout();
54668         }    
54669     },
54670     
54671     layout: function(){
54672         
54673     },
54674     
54675     onRegionResized : function(region, newSize){
54676         this.fireEvent("regionresized", region, newSize);
54677         this.layout();
54678     },
54679     
54680     onRegionCollapsed : function(region){
54681         this.fireEvent("regioncollapsed", region);
54682     },
54683     
54684     onRegionExpanded : function(region){
54685         this.fireEvent("regionexpanded", region);
54686     },
54687         
54688     /**
54689      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54690      * performs box-model adjustments.
54691      * @return {Object} The size as an object {width: (the width), height: (the height)}
54692      */
54693     getViewSize : function(){
54694         var size;
54695         if(this.el.dom != document.body){
54696             size = this.el.getSize();
54697         }else{
54698             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54699         }
54700         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54701         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54702         return size;
54703     },
54704     
54705     /**
54706      * Returns the Element this layout is bound to.
54707      * @return {Roo.Element}
54708      */
54709     getEl : function(){
54710         return this.el;
54711     },
54712     
54713     /**
54714      * Returns the specified region.
54715      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54716      * @return {Roo.LayoutRegion}
54717      */
54718     getRegion : function(target){
54719         return this.regions[target.toLowerCase()];
54720     },
54721     
54722     onWindowResize : function(){
54723         if(this.monitorWindowResize){
54724             this.layout();
54725         }
54726     }
54727 });/*
54728  * Based on:
54729  * Ext JS Library 1.1.1
54730  * Copyright(c) 2006-2007, Ext JS, LLC.
54731  *
54732  * Originally Released Under LGPL - original licence link has changed is not relivant.
54733  *
54734  * Fork - LGPL
54735  * <script type="text/javascript">
54736  */
54737 /**
54738  * @class Roo.BorderLayout
54739  * @extends Roo.LayoutManager
54740  * @children Roo.ContentPanel
54741  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54742  * please see: <br><br>
54743  * <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>
54744  * <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>
54745  * Example:
54746  <pre><code>
54747  var layout = new Roo.BorderLayout(document.body, {
54748     north: {
54749         initialSize: 25,
54750         titlebar: false
54751     },
54752     west: {
54753         split:true,
54754         initialSize: 200,
54755         minSize: 175,
54756         maxSize: 400,
54757         titlebar: true,
54758         collapsible: true
54759     },
54760     east: {
54761         split:true,
54762         initialSize: 202,
54763         minSize: 175,
54764         maxSize: 400,
54765         titlebar: true,
54766         collapsible: true
54767     },
54768     south: {
54769         split:true,
54770         initialSize: 100,
54771         minSize: 100,
54772         maxSize: 200,
54773         titlebar: true,
54774         collapsible: true
54775     },
54776     center: {
54777         titlebar: true,
54778         autoScroll:true,
54779         resizeTabs: true,
54780         minTabWidth: 50,
54781         preferredTabWidth: 150
54782     }
54783 });
54784
54785 // shorthand
54786 var CP = Roo.ContentPanel;
54787
54788 layout.beginUpdate();
54789 layout.add("north", new CP("north", "North"));
54790 layout.add("south", new CP("south", {title: "South", closable: true}));
54791 layout.add("west", new CP("west", {title: "West"}));
54792 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54793 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54794 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54795 layout.getRegion("center").showPanel("center1");
54796 layout.endUpdate();
54797 </code></pre>
54798
54799 <b>The container the layout is rendered into can be either the body element or any other element.
54800 If it is not the body element, the container needs to either be an absolute positioned element,
54801 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54802 the container size if it is not the body element.</b>
54803
54804 * @constructor
54805 * Create a new BorderLayout
54806 * @param {String/HTMLElement/Element} container The container this layout is bound to
54807 * @param {Object} config Configuration options
54808  */
54809 Roo.BorderLayout = function(container, config){
54810     config = config || {};
54811     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54812     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54813     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54814         var target = this.factory.validRegions[i];
54815         if(config[target]){
54816             this.addRegion(target, config[target]);
54817         }
54818     }
54819 };
54820
54821 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54822         
54823         /**
54824          * @cfg {Roo.LayoutRegion} east
54825          */
54826         /**
54827          * @cfg {Roo.LayoutRegion} west
54828          */
54829         /**
54830          * @cfg {Roo.LayoutRegion} north
54831          */
54832         /**
54833          * @cfg {Roo.LayoutRegion} south
54834          */
54835         /**
54836          * @cfg {Roo.LayoutRegion} center
54837          */
54838     /**
54839      * Creates and adds a new region if it doesn't already exist.
54840      * @param {String} target The target region key (north, south, east, west or center).
54841      * @param {Object} config The regions config object
54842      * @return {BorderLayoutRegion} The new region
54843      */
54844     addRegion : function(target, config){
54845         if(!this.regions[target]){
54846             var r = this.factory.create(target, this, config);
54847             this.bindRegion(target, r);
54848         }
54849         return this.regions[target];
54850     },
54851
54852     // private (kinda)
54853     bindRegion : function(name, r){
54854         this.regions[name] = r;
54855         r.on("visibilitychange", this.layout, this);
54856         r.on("paneladded", this.layout, this);
54857         r.on("panelremoved", this.layout, this);
54858         r.on("invalidated", this.layout, this);
54859         r.on("resized", this.onRegionResized, this);
54860         r.on("collapsed", this.onRegionCollapsed, this);
54861         r.on("expanded", this.onRegionExpanded, this);
54862     },
54863
54864     /**
54865      * Performs a layout update.
54866      */
54867     layout : function(){
54868         if(this.updating) {
54869             return;
54870         }
54871         var size = this.getViewSize();
54872         var w = size.width;
54873         var h = size.height;
54874         var centerW = w;
54875         var centerH = h;
54876         var centerY = 0;
54877         var centerX = 0;
54878         //var x = 0, y = 0;
54879
54880         var rs = this.regions;
54881         var north = rs["north"];
54882         var south = rs["south"]; 
54883         var west = rs["west"];
54884         var east = rs["east"];
54885         var center = rs["center"];
54886         //if(this.hideOnLayout){ // not supported anymore
54887             //c.el.setStyle("display", "none");
54888         //}
54889         if(north && north.isVisible()){
54890             var b = north.getBox();
54891             var m = north.getMargins();
54892             b.width = w - (m.left+m.right);
54893             b.x = m.left;
54894             b.y = m.top;
54895             centerY = b.height + b.y + m.bottom;
54896             centerH -= centerY;
54897             north.updateBox(this.safeBox(b));
54898         }
54899         if(south && south.isVisible()){
54900             var b = south.getBox();
54901             var m = south.getMargins();
54902             b.width = w - (m.left+m.right);
54903             b.x = m.left;
54904             var totalHeight = (b.height + m.top + m.bottom);
54905             b.y = h - totalHeight + m.top;
54906             centerH -= totalHeight;
54907             south.updateBox(this.safeBox(b));
54908         }
54909         if(west && west.isVisible()){
54910             var b = west.getBox();
54911             var m = west.getMargins();
54912             b.height = centerH - (m.top+m.bottom);
54913             b.x = m.left;
54914             b.y = centerY + m.top;
54915             var totalWidth = (b.width + m.left + m.right);
54916             centerX += totalWidth;
54917             centerW -= totalWidth;
54918             west.updateBox(this.safeBox(b));
54919         }
54920         if(east && east.isVisible()){
54921             var b = east.getBox();
54922             var m = east.getMargins();
54923             b.height = centerH - (m.top+m.bottom);
54924             var totalWidth = (b.width + m.left + m.right);
54925             b.x = w - totalWidth + m.left;
54926             b.y = centerY + m.top;
54927             centerW -= totalWidth;
54928             east.updateBox(this.safeBox(b));
54929         }
54930         if(center){
54931             var m = center.getMargins();
54932             var centerBox = {
54933                 x: centerX + m.left,
54934                 y: centerY + m.top,
54935                 width: centerW - (m.left+m.right),
54936                 height: centerH - (m.top+m.bottom)
54937             };
54938             //if(this.hideOnLayout){
54939                 //center.el.setStyle("display", "block");
54940             //}
54941             center.updateBox(this.safeBox(centerBox));
54942         }
54943         this.el.repaint();
54944         this.fireEvent("layout", this);
54945     },
54946
54947     // private
54948     safeBox : function(box){
54949         box.width = Math.max(0, box.width);
54950         box.height = Math.max(0, box.height);
54951         return box;
54952     },
54953
54954     /**
54955      * Adds a ContentPanel (or subclass) to this layout.
54956      * @param {String} target The target region key (north, south, east, west or center).
54957      * @param {Roo.ContentPanel} panel The panel to add
54958      * @return {Roo.ContentPanel} The added panel
54959      */
54960     add : function(target, panel){
54961          
54962         target = target.toLowerCase();
54963         return this.regions[target].add(panel);
54964     },
54965
54966     /**
54967      * Remove a ContentPanel (or subclass) to this layout.
54968      * @param {String} target The target region key (north, south, east, west or center).
54969      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54970      * @return {Roo.ContentPanel} The removed panel
54971      */
54972     remove : function(target, panel){
54973         target = target.toLowerCase();
54974         return this.regions[target].remove(panel);
54975     },
54976
54977     /**
54978      * Searches all regions for a panel with the specified id
54979      * @param {String} panelId
54980      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54981      */
54982     findPanel : function(panelId){
54983         var rs = this.regions;
54984         for(var target in rs){
54985             if(typeof rs[target] != "function"){
54986                 var p = rs[target].getPanel(panelId);
54987                 if(p){
54988                     return p;
54989                 }
54990             }
54991         }
54992         return null;
54993     },
54994
54995     /**
54996      * Searches all regions for a panel with the specified id and activates (shows) it.
54997      * @param {String/ContentPanel} panelId The panels id or the panel itself
54998      * @return {Roo.ContentPanel} The shown panel or null
54999      */
55000     showPanel : function(panelId) {
55001       var rs = this.regions;
55002       for(var target in rs){
55003          var r = rs[target];
55004          if(typeof r != "function"){
55005             if(r.hasPanel(panelId)){
55006                return r.showPanel(panelId);
55007             }
55008          }
55009       }
55010       return null;
55011    },
55012
55013    /**
55014      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55015      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55016      */
55017     restoreState : function(provider){
55018         if(!provider){
55019             provider = Roo.state.Manager;
55020         }
55021         var sm = new Roo.LayoutStateManager();
55022         sm.init(this, provider);
55023     },
55024
55025     /**
55026      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55027      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55028      * a valid ContentPanel config object.  Example:
55029      * <pre><code>
55030 // Create the main layout
55031 var layout = new Roo.BorderLayout('main-ct', {
55032     west: {
55033         split:true,
55034         minSize: 175,
55035         titlebar: true
55036     },
55037     center: {
55038         title:'Components'
55039     }
55040 }, 'main-ct');
55041
55042 // Create and add multiple ContentPanels at once via configs
55043 layout.batchAdd({
55044    west: {
55045        id: 'source-files',
55046        autoCreate:true,
55047        title:'Ext Source Files',
55048        autoScroll:true,
55049        fitToFrame:true
55050    },
55051    center : {
55052        el: cview,
55053        autoScroll:true,
55054        fitToFrame:true,
55055        toolbar: tb,
55056        resizeEl:'cbody'
55057    }
55058 });
55059 </code></pre>
55060      * @param {Object} regions An object containing ContentPanel configs by region name
55061      */
55062     batchAdd : function(regions){
55063         this.beginUpdate();
55064         for(var rname in regions){
55065             var lr = this.regions[rname];
55066             if(lr){
55067                 this.addTypedPanels(lr, regions[rname]);
55068             }
55069         }
55070         this.endUpdate();
55071     },
55072
55073     // private
55074     addTypedPanels : function(lr, ps){
55075         if(typeof ps == 'string'){
55076             lr.add(new Roo.ContentPanel(ps));
55077         }
55078         else if(ps instanceof Array){
55079             for(var i =0, len = ps.length; i < len; i++){
55080                 this.addTypedPanels(lr, ps[i]);
55081             }
55082         }
55083         else if(!ps.events){ // raw config?
55084             var el = ps.el;
55085             delete ps.el; // prevent conflict
55086             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55087         }
55088         else {  // panel object assumed!
55089             lr.add(ps);
55090         }
55091     },
55092     /**
55093      * Adds a xtype elements to the layout.
55094      * <pre><code>
55095
55096 layout.addxtype({
55097        xtype : 'ContentPanel',
55098        region: 'west',
55099        items: [ .... ]
55100    }
55101 );
55102
55103 layout.addxtype({
55104         xtype : 'NestedLayoutPanel',
55105         region: 'west',
55106         layout: {
55107            center: { },
55108            west: { }   
55109         },
55110         items : [ ... list of content panels or nested layout panels.. ]
55111    }
55112 );
55113 </code></pre>
55114      * @param {Object} cfg Xtype definition of item to add.
55115      */
55116     addxtype : function(cfg)
55117     {
55118         // basically accepts a pannel...
55119         // can accept a layout region..!?!?
55120         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55121         
55122         if (!cfg.xtype.match(/Panel$/)) {
55123             return false;
55124         }
55125         var ret = false;
55126         
55127         if (typeof(cfg.region) == 'undefined') {
55128             Roo.log("Failed to add Panel, region was not set");
55129             Roo.log(cfg);
55130             return false;
55131         }
55132         var region = cfg.region;
55133         delete cfg.region;
55134         
55135           
55136         var xitems = [];
55137         if (cfg.items) {
55138             xitems = cfg.items;
55139             delete cfg.items;
55140         }
55141         var nb = false;
55142         
55143         switch(cfg.xtype) 
55144         {
55145             case 'ContentPanel':  // ContentPanel (el, cfg)
55146             case 'ScrollPanel':  // ContentPanel (el, cfg)
55147             case 'ViewPanel': 
55148                 if(cfg.autoCreate) {
55149                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55150                 } else {
55151                     var el = this.el.createChild();
55152                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55153                 }
55154                 
55155                 this.add(region, ret);
55156                 break;
55157             
55158             
55159             case 'TreePanel': // our new panel!
55160                 cfg.el = this.el.createChild();
55161                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55162                 this.add(region, ret);
55163                 break;
55164             
55165             case 'NestedLayoutPanel': 
55166                 // create a new Layout (which is  a Border Layout...
55167                 var el = this.el.createChild();
55168                 var clayout = cfg.layout;
55169                 delete cfg.layout;
55170                 clayout.items   = clayout.items  || [];
55171                 // replace this exitems with the clayout ones..
55172                 xitems = clayout.items;
55173                  
55174                 
55175                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55176                     cfg.background = false;
55177                 }
55178                 var layout = new Roo.BorderLayout(el, clayout);
55179                 
55180                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55181                 //console.log('adding nested layout panel '  + cfg.toSource());
55182                 this.add(region, ret);
55183                 nb = {}; /// find first...
55184                 break;
55185                 
55186             case 'GridPanel': 
55187             
55188                 // needs grid and region
55189                 
55190                 //var el = this.getRegion(region).el.createChild();
55191                 var el = this.el.createChild();
55192                 // create the grid first...
55193                 
55194                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55195                 delete cfg.grid;
55196                 if (region == 'center' && this.active ) {
55197                     cfg.background = false;
55198                 }
55199                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55200                 
55201                 this.add(region, ret);
55202                 if (cfg.background) {
55203                     ret.on('activate', function(gp) {
55204                         if (!gp.grid.rendered) {
55205                             gp.grid.render();
55206                         }
55207                     });
55208                 } else {
55209                     grid.render();
55210                 }
55211                 break;
55212            
55213            
55214            
55215                 
55216                 
55217                 
55218             default:
55219                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55220                     
55221                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55222                     this.add(region, ret);
55223                 } else {
55224                 
55225                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55226                     return null;
55227                 }
55228                 
55229              // GridPanel (grid, cfg)
55230             
55231         }
55232         this.beginUpdate();
55233         // add children..
55234         var region = '';
55235         var abn = {};
55236         Roo.each(xitems, function(i)  {
55237             region = nb && i.region ? i.region : false;
55238             
55239             var add = ret.addxtype(i);
55240            
55241             if (region) {
55242                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55243                 if (!i.background) {
55244                     abn[region] = nb[region] ;
55245                 }
55246             }
55247             
55248         });
55249         this.endUpdate();
55250
55251         // make the last non-background panel active..
55252         //if (nb) { Roo.log(abn); }
55253         if (nb) {
55254             
55255             for(var r in abn) {
55256                 region = this.getRegion(r);
55257                 if (region) {
55258                     // tried using nb[r], but it does not work..
55259                      
55260                     region.showPanel(abn[r]);
55261                    
55262                 }
55263             }
55264         }
55265         return ret;
55266         
55267     }
55268 });
55269
55270 /**
55271  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55272  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55273  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55274  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55275  * <pre><code>
55276 // shorthand
55277 var CP = Roo.ContentPanel;
55278
55279 var layout = Roo.BorderLayout.create({
55280     north: {
55281         initialSize: 25,
55282         titlebar: false,
55283         panels: [new CP("north", "North")]
55284     },
55285     west: {
55286         split:true,
55287         initialSize: 200,
55288         minSize: 175,
55289         maxSize: 400,
55290         titlebar: true,
55291         collapsible: true,
55292         panels: [new CP("west", {title: "West"})]
55293     },
55294     east: {
55295         split:true,
55296         initialSize: 202,
55297         minSize: 175,
55298         maxSize: 400,
55299         titlebar: true,
55300         collapsible: true,
55301         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55302     },
55303     south: {
55304         split:true,
55305         initialSize: 100,
55306         minSize: 100,
55307         maxSize: 200,
55308         titlebar: true,
55309         collapsible: true,
55310         panels: [new CP("south", {title: "South", closable: true})]
55311     },
55312     center: {
55313         titlebar: true,
55314         autoScroll:true,
55315         resizeTabs: true,
55316         minTabWidth: 50,
55317         preferredTabWidth: 150,
55318         panels: [
55319             new CP("center1", {title: "Close Me", closable: true}),
55320             new CP("center2", {title: "Center Panel", closable: false})
55321         ]
55322     }
55323 }, document.body);
55324
55325 layout.getRegion("center").showPanel("center1");
55326 </code></pre>
55327  * @param config
55328  * @param targetEl
55329  */
55330 Roo.BorderLayout.create = function(config, targetEl){
55331     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55332     layout.beginUpdate();
55333     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55334     for(var j = 0, jlen = regions.length; j < jlen; j++){
55335         var lr = regions[j];
55336         if(layout.regions[lr] && config[lr].panels){
55337             var r = layout.regions[lr];
55338             var ps = config[lr].panels;
55339             layout.addTypedPanels(r, ps);
55340         }
55341     }
55342     layout.endUpdate();
55343     return layout;
55344 };
55345
55346 // private
55347 Roo.BorderLayout.RegionFactory = {
55348     // private
55349     validRegions : ["north","south","east","west","center"],
55350
55351     // private
55352     create : function(target, mgr, config){
55353         target = target.toLowerCase();
55354         if(config.lightweight || config.basic){
55355             return new Roo.BasicLayoutRegion(mgr, config, target);
55356         }
55357         switch(target){
55358             case "north":
55359                 return new Roo.NorthLayoutRegion(mgr, config);
55360             case "south":
55361                 return new Roo.SouthLayoutRegion(mgr, config);
55362             case "east":
55363                 return new Roo.EastLayoutRegion(mgr, config);
55364             case "west":
55365                 return new Roo.WestLayoutRegion(mgr, config);
55366             case "center":
55367                 return new Roo.CenterLayoutRegion(mgr, config);
55368         }
55369         throw 'Layout region "'+target+'" not supported.';
55370     }
55371 };/*
55372  * Based on:
55373  * Ext JS Library 1.1.1
55374  * Copyright(c) 2006-2007, Ext JS, LLC.
55375  *
55376  * Originally Released Under LGPL - original licence link has changed is not relivant.
55377  *
55378  * Fork - LGPL
55379  * <script type="text/javascript">
55380  */
55381  
55382 /**
55383  * @class Roo.BasicLayoutRegion
55384  * @extends Roo.util.Observable
55385  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55386  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55387  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55388  */
55389 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55390     this.mgr = mgr;
55391     this.position  = pos;
55392     this.events = {
55393         /**
55394          * @scope Roo.BasicLayoutRegion
55395          */
55396         
55397         /**
55398          * @event beforeremove
55399          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55400          * @param {Roo.LayoutRegion} this
55401          * @param {Roo.ContentPanel} panel The panel
55402          * @param {Object} e The cancel event object
55403          */
55404         "beforeremove" : true,
55405         /**
55406          * @event invalidated
55407          * Fires when the layout for this region is changed.
55408          * @param {Roo.LayoutRegion} this
55409          */
55410         "invalidated" : true,
55411         /**
55412          * @event visibilitychange
55413          * Fires when this region is shown or hidden 
55414          * @param {Roo.LayoutRegion} this
55415          * @param {Boolean} visibility true or false
55416          */
55417         "visibilitychange" : true,
55418         /**
55419          * @event paneladded
55420          * Fires when a panel is added. 
55421          * @param {Roo.LayoutRegion} this
55422          * @param {Roo.ContentPanel} panel The panel
55423          */
55424         "paneladded" : true,
55425         /**
55426          * @event panelremoved
55427          * Fires when a panel is removed. 
55428          * @param {Roo.LayoutRegion} this
55429          * @param {Roo.ContentPanel} panel The panel
55430          */
55431         "panelremoved" : true,
55432         /**
55433          * @event beforecollapse
55434          * Fires when this region before collapse.
55435          * @param {Roo.LayoutRegion} this
55436          */
55437         "beforecollapse" : true,
55438         /**
55439          * @event collapsed
55440          * Fires when this region is collapsed.
55441          * @param {Roo.LayoutRegion} this
55442          */
55443         "collapsed" : true,
55444         /**
55445          * @event expanded
55446          * Fires when this region is expanded.
55447          * @param {Roo.LayoutRegion} this
55448          */
55449         "expanded" : true,
55450         /**
55451          * @event slideshow
55452          * Fires when this region is slid into view.
55453          * @param {Roo.LayoutRegion} this
55454          */
55455         "slideshow" : true,
55456         /**
55457          * @event slidehide
55458          * Fires when this region slides out of view. 
55459          * @param {Roo.LayoutRegion} this
55460          */
55461         "slidehide" : true,
55462         /**
55463          * @event panelactivated
55464          * Fires when a panel is activated. 
55465          * @param {Roo.LayoutRegion} this
55466          * @param {Roo.ContentPanel} panel The activated panel
55467          */
55468         "panelactivated" : true,
55469         /**
55470          * @event resized
55471          * Fires when the user resizes this region. 
55472          * @param {Roo.LayoutRegion} this
55473          * @param {Number} newSize The new size (width for east/west, height for north/south)
55474          */
55475         "resized" : true
55476     };
55477     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55478     this.panels = new Roo.util.MixedCollection();
55479     this.panels.getKey = this.getPanelId.createDelegate(this);
55480     this.box = null;
55481     this.activePanel = null;
55482     // ensure listeners are added...
55483     
55484     if (config.listeners || config.events) {
55485         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55486             listeners : config.listeners || {},
55487             events : config.events || {}
55488         });
55489     }
55490     
55491     if(skipConfig !== true){
55492         this.applyConfig(config);
55493     }
55494 };
55495
55496 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55497     getPanelId : function(p){
55498         return p.getId();
55499     },
55500     
55501     applyConfig : function(config){
55502         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55503         this.config = config;
55504         
55505     },
55506     
55507     /**
55508      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55509      * the width, for horizontal (north, south) the height.
55510      * @param {Number} newSize The new width or height
55511      */
55512     resizeTo : function(newSize){
55513         var el = this.el ? this.el :
55514                  (this.activePanel ? this.activePanel.getEl() : null);
55515         if(el){
55516             switch(this.position){
55517                 case "east":
55518                 case "west":
55519                     el.setWidth(newSize);
55520                     this.fireEvent("resized", this, newSize);
55521                 break;
55522                 case "north":
55523                 case "south":
55524                     el.setHeight(newSize);
55525                     this.fireEvent("resized", this, newSize);
55526                 break;                
55527             }
55528         }
55529     },
55530     
55531     getBox : function(){
55532         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55533     },
55534     
55535     getMargins : function(){
55536         return this.margins;
55537     },
55538     
55539     updateBox : function(box){
55540         this.box = box;
55541         var el = this.activePanel.getEl();
55542         el.dom.style.left = box.x + "px";
55543         el.dom.style.top = box.y + "px";
55544         this.activePanel.setSize(box.width, box.height);
55545     },
55546     
55547     /**
55548      * Returns the container element for this region.
55549      * @return {Roo.Element}
55550      */
55551     getEl : function(){
55552         return this.activePanel;
55553     },
55554     
55555     /**
55556      * Returns true if this region is currently visible.
55557      * @return {Boolean}
55558      */
55559     isVisible : function(){
55560         return this.activePanel ? true : false;
55561     },
55562     
55563     setActivePanel : function(panel){
55564         panel = this.getPanel(panel);
55565         if(this.activePanel && this.activePanel != panel){
55566             this.activePanel.setActiveState(false);
55567             this.activePanel.getEl().setLeftTop(-10000,-10000);
55568         }
55569         this.activePanel = panel;
55570         panel.setActiveState(true);
55571         if(this.box){
55572             panel.setSize(this.box.width, this.box.height);
55573         }
55574         this.fireEvent("panelactivated", this, panel);
55575         this.fireEvent("invalidated");
55576     },
55577     
55578     /**
55579      * Show the specified panel.
55580      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55581      * @return {Roo.ContentPanel} The shown panel or null
55582      */
55583     showPanel : function(panel){
55584         if(panel = this.getPanel(panel)){
55585             this.setActivePanel(panel);
55586         }
55587         return panel;
55588     },
55589     
55590     /**
55591      * Get the active panel for this region.
55592      * @return {Roo.ContentPanel} The active panel or null
55593      */
55594     getActivePanel : function(){
55595         return this.activePanel;
55596     },
55597     
55598     /**
55599      * Add the passed ContentPanel(s)
55600      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55601      * @return {Roo.ContentPanel} The panel added (if only one was added)
55602      */
55603     add : function(panel){
55604         if(arguments.length > 1){
55605             for(var i = 0, len = arguments.length; i < len; i++) {
55606                 this.add(arguments[i]);
55607             }
55608             return null;
55609         }
55610         if(this.hasPanel(panel)){
55611             this.showPanel(panel);
55612             return panel;
55613         }
55614         var el = panel.getEl();
55615         if(el.dom.parentNode != this.mgr.el.dom){
55616             this.mgr.el.dom.appendChild(el.dom);
55617         }
55618         if(panel.setRegion){
55619             panel.setRegion(this);
55620         }
55621         this.panels.add(panel);
55622         el.setStyle("position", "absolute");
55623         if(!panel.background){
55624             this.setActivePanel(panel);
55625             if(this.config.initialSize && this.panels.getCount()==1){
55626                 this.resizeTo(this.config.initialSize);
55627             }
55628         }
55629         this.fireEvent("paneladded", this, panel);
55630         return panel;
55631     },
55632     
55633     /**
55634      * Returns true if the panel is in this region.
55635      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55636      * @return {Boolean}
55637      */
55638     hasPanel : function(panel){
55639         if(typeof panel == "object"){ // must be panel obj
55640             panel = panel.getId();
55641         }
55642         return this.getPanel(panel) ? true : false;
55643     },
55644     
55645     /**
55646      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55647      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55648      * @param {Boolean} preservePanel Overrides the config preservePanel option
55649      * @return {Roo.ContentPanel} The panel that was removed
55650      */
55651     remove : function(panel, preservePanel){
55652         panel = this.getPanel(panel);
55653         if(!panel){
55654             return null;
55655         }
55656         var e = {};
55657         this.fireEvent("beforeremove", this, panel, e);
55658         if(e.cancel === true){
55659             return null;
55660         }
55661         var panelId = panel.getId();
55662         this.panels.removeKey(panelId);
55663         return panel;
55664     },
55665     
55666     /**
55667      * Returns the panel specified or null if it's not in this region.
55668      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55669      * @return {Roo.ContentPanel}
55670      */
55671     getPanel : function(id){
55672         if(typeof id == "object"){ // must be panel obj
55673             return id;
55674         }
55675         return this.panels.get(id);
55676     },
55677     
55678     /**
55679      * Returns this regions position (north/south/east/west/center).
55680      * @return {String} 
55681      */
55682     getPosition: function(){
55683         return this.position;    
55684     }
55685 });/*
55686  * Based on:
55687  * Ext JS Library 1.1.1
55688  * Copyright(c) 2006-2007, Ext JS, LLC.
55689  *
55690  * Originally Released Under LGPL - original licence link has changed is not relivant.
55691  *
55692  * Fork - LGPL
55693  * <script type="text/javascript">
55694  */
55695  
55696 /**
55697  * @class Roo.LayoutRegion
55698  * @extends Roo.BasicLayoutRegion
55699  * This class represents a region in a layout manager.
55700  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55701  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55702  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55703  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55704  * @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})
55705  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55706  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55707  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55708  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55709  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55710  * @cfg {String}    title           The title for the region (overrides panel titles)
55711  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55712  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55713  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55714  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55715  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55716  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55717  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55718  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55719  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55720  * @cfg {Boolean}   showPin         True to show a pin button
55721  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55722  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55723  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55724  * @cfg {Number}    width           For East/West panels
55725  * @cfg {Number}    height          For North/South panels
55726  * @cfg {Boolean}   split           To show the splitter
55727  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55728  */
55729 Roo.LayoutRegion = function(mgr, config, pos){
55730     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55731     var dh = Roo.DomHelper;
55732     /** This region's container element 
55733     * @type Roo.Element */
55734     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55735     /** This region's title element 
55736     * @type Roo.Element */
55737
55738     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55739         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55740         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55741     ]}, true);
55742     this.titleEl.enableDisplayMode();
55743     /** This region's title text element 
55744     * @type HTMLElement */
55745     this.titleTextEl = this.titleEl.dom.firstChild;
55746     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55747     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55748     this.closeBtn.enableDisplayMode();
55749     this.closeBtn.on("click", this.closeClicked, this);
55750     this.closeBtn.hide();
55751
55752     this.createBody(config);
55753     this.visible = true;
55754     this.collapsed = false;
55755
55756     if(config.hideWhenEmpty){
55757         this.hide();
55758         this.on("paneladded", this.validateVisibility, this);
55759         this.on("panelremoved", this.validateVisibility, this);
55760     }
55761     this.applyConfig(config);
55762 };
55763
55764 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55765
55766     createBody : function(){
55767         /** This region's body element 
55768         * @type Roo.Element */
55769         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55770     },
55771
55772     applyConfig : function(c){
55773         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55774             var dh = Roo.DomHelper;
55775             if(c.titlebar !== false){
55776                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55777                 this.collapseBtn.on("click", this.collapse, this);
55778                 this.collapseBtn.enableDisplayMode();
55779
55780                 if(c.showPin === true || this.showPin){
55781                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55782                     this.stickBtn.enableDisplayMode();
55783                     this.stickBtn.on("click", this.expand, this);
55784                     this.stickBtn.hide();
55785                 }
55786             }
55787             /** This region's collapsed element
55788             * @type Roo.Element */
55789             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55790                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55791             ]}, true);
55792             if(c.floatable !== false){
55793                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55794                this.collapsedEl.on("click", this.collapseClick, this);
55795             }
55796
55797             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55798                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55799                    id: "message", unselectable: "on", style:{"float":"left"}});
55800                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55801              }
55802             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55803             this.expandBtn.on("click", this.expand, this);
55804         }
55805         if(this.collapseBtn){
55806             this.collapseBtn.setVisible(c.collapsible == true);
55807         }
55808         this.cmargins = c.cmargins || this.cmargins ||
55809                          (this.position == "west" || this.position == "east" ?
55810                              {top: 0, left: 2, right:2, bottom: 0} :
55811                              {top: 2, left: 0, right:0, bottom: 2});
55812         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55813         this.bottomTabs = c.tabPosition != "top";
55814         this.autoScroll = c.autoScroll || false;
55815         if(this.autoScroll){
55816             this.bodyEl.setStyle("overflow", "auto");
55817         }else{
55818             this.bodyEl.setStyle("overflow", "hidden");
55819         }
55820         //if(c.titlebar !== false){
55821             if((!c.titlebar && !c.title) || c.titlebar === false){
55822                 this.titleEl.hide();
55823             }else{
55824                 this.titleEl.show();
55825                 if(c.title){
55826                     this.titleTextEl.innerHTML = c.title;
55827                 }
55828             }
55829         //}
55830         this.duration = c.duration || .30;
55831         this.slideDuration = c.slideDuration || .45;
55832         this.config = c;
55833         if(c.collapsed){
55834             this.collapse(true);
55835         }
55836         if(c.hidden){
55837             this.hide();
55838         }
55839     },
55840     /**
55841      * Returns true if this region is currently visible.
55842      * @return {Boolean}
55843      */
55844     isVisible : function(){
55845         return this.visible;
55846     },
55847
55848     /**
55849      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55850      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55851      */
55852     setCollapsedTitle : function(title){
55853         title = title || "&#160;";
55854         if(this.collapsedTitleTextEl){
55855             this.collapsedTitleTextEl.innerHTML = title;
55856         }
55857     },
55858
55859     getBox : function(){
55860         var b;
55861         if(!this.collapsed){
55862             b = this.el.getBox(false, true);
55863         }else{
55864             b = this.collapsedEl.getBox(false, true);
55865         }
55866         return b;
55867     },
55868
55869     getMargins : function(){
55870         return this.collapsed ? this.cmargins : this.margins;
55871     },
55872
55873     highlight : function(){
55874         this.el.addClass("x-layout-panel-dragover");
55875     },
55876
55877     unhighlight : function(){
55878         this.el.removeClass("x-layout-panel-dragover");
55879     },
55880
55881     updateBox : function(box){
55882         this.box = box;
55883         if(!this.collapsed){
55884             this.el.dom.style.left = box.x + "px";
55885             this.el.dom.style.top = box.y + "px";
55886             this.updateBody(box.width, box.height);
55887         }else{
55888             this.collapsedEl.dom.style.left = box.x + "px";
55889             this.collapsedEl.dom.style.top = box.y + "px";
55890             this.collapsedEl.setSize(box.width, box.height);
55891         }
55892         if(this.tabs){
55893             this.tabs.autoSizeTabs();
55894         }
55895     },
55896
55897     updateBody : function(w, h){
55898         if(w !== null){
55899             this.el.setWidth(w);
55900             w -= this.el.getBorderWidth("rl");
55901             if(this.config.adjustments){
55902                 w += this.config.adjustments[0];
55903             }
55904         }
55905         if(h !== null){
55906             this.el.setHeight(h);
55907             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55908             h -= this.el.getBorderWidth("tb");
55909             if(this.config.adjustments){
55910                 h += this.config.adjustments[1];
55911             }
55912             this.bodyEl.setHeight(h);
55913             if(this.tabs){
55914                 h = this.tabs.syncHeight(h);
55915             }
55916         }
55917         if(this.panelSize){
55918             w = w !== null ? w : this.panelSize.width;
55919             h = h !== null ? h : this.panelSize.height;
55920         }
55921         if(this.activePanel){
55922             var el = this.activePanel.getEl();
55923             w = w !== null ? w : el.getWidth();
55924             h = h !== null ? h : el.getHeight();
55925             this.panelSize = {width: w, height: h};
55926             this.activePanel.setSize(w, h);
55927         }
55928         if(Roo.isIE && this.tabs){
55929             this.tabs.el.repaint();
55930         }
55931     },
55932
55933     /**
55934      * Returns the container element for this region.
55935      * @return {Roo.Element}
55936      */
55937     getEl : function(){
55938         return this.el;
55939     },
55940
55941     /**
55942      * Hides this region.
55943      */
55944     hide : function(){
55945         if(!this.collapsed){
55946             this.el.dom.style.left = "-2000px";
55947             this.el.hide();
55948         }else{
55949             this.collapsedEl.dom.style.left = "-2000px";
55950             this.collapsedEl.hide();
55951         }
55952         this.visible = false;
55953         this.fireEvent("visibilitychange", this, false);
55954     },
55955
55956     /**
55957      * Shows this region if it was previously hidden.
55958      */
55959     show : function(){
55960         if(!this.collapsed){
55961             this.el.show();
55962         }else{
55963             this.collapsedEl.show();
55964         }
55965         this.visible = true;
55966         this.fireEvent("visibilitychange", this, true);
55967     },
55968
55969     closeClicked : function(){
55970         if(this.activePanel){
55971             this.remove(this.activePanel);
55972         }
55973     },
55974
55975     collapseClick : function(e){
55976         if(this.isSlid){
55977            e.stopPropagation();
55978            this.slideIn();
55979         }else{
55980            e.stopPropagation();
55981            this.slideOut();
55982         }
55983     },
55984
55985     /**
55986      * Collapses this region.
55987      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55988      */
55989     collapse : function(skipAnim, skipCheck){
55990         if(this.collapsed) {
55991             return;
55992         }
55993         
55994         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55995             
55996             this.collapsed = true;
55997             if(this.split){
55998                 this.split.el.hide();
55999             }
56000             if(this.config.animate && skipAnim !== true){
56001                 this.fireEvent("invalidated", this);
56002                 this.animateCollapse();
56003             }else{
56004                 this.el.setLocation(-20000,-20000);
56005                 this.el.hide();
56006                 this.collapsedEl.show();
56007                 this.fireEvent("collapsed", this);
56008                 this.fireEvent("invalidated", this);
56009             }
56010         }
56011         
56012     },
56013
56014     animateCollapse : function(){
56015         // overridden
56016     },
56017
56018     /**
56019      * Expands this region if it was previously collapsed.
56020      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56021      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56022      */
56023     expand : function(e, skipAnim){
56024         if(e) {
56025             e.stopPropagation();
56026         }
56027         if(!this.collapsed || this.el.hasActiveFx()) {
56028             return;
56029         }
56030         if(this.isSlid){
56031             this.afterSlideIn();
56032             skipAnim = true;
56033         }
56034         this.collapsed = false;
56035         if(this.config.animate && skipAnim !== true){
56036             this.animateExpand();
56037         }else{
56038             this.el.show();
56039             if(this.split){
56040                 this.split.el.show();
56041             }
56042             this.collapsedEl.setLocation(-2000,-2000);
56043             this.collapsedEl.hide();
56044             this.fireEvent("invalidated", this);
56045             this.fireEvent("expanded", this);
56046         }
56047     },
56048
56049     animateExpand : function(){
56050         // overridden
56051     },
56052
56053     initTabs : function()
56054     {
56055         this.bodyEl.setStyle("overflow", "hidden");
56056         var ts = new Roo.TabPanel(
56057                 this.bodyEl.dom,
56058                 {
56059                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56060                     disableTooltips: this.config.disableTabTips,
56061                     toolbar : this.config.toolbar
56062                 }
56063         );
56064         if(this.config.hideTabs){
56065             ts.stripWrap.setDisplayed(false);
56066         }
56067         this.tabs = ts;
56068         ts.resizeTabs = this.config.resizeTabs === true;
56069         ts.minTabWidth = this.config.minTabWidth || 40;
56070         ts.maxTabWidth = this.config.maxTabWidth || 250;
56071         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56072         ts.monitorResize = false;
56073         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56074         ts.bodyEl.addClass('x-layout-tabs-body');
56075         this.panels.each(this.initPanelAsTab, this);
56076     },
56077
56078     initPanelAsTab : function(panel){
56079         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56080                     this.config.closeOnTab && panel.isClosable());
56081         if(panel.tabTip !== undefined){
56082             ti.setTooltip(panel.tabTip);
56083         }
56084         ti.on("activate", function(){
56085               this.setActivePanel(panel);
56086         }, this);
56087         if(this.config.closeOnTab){
56088             ti.on("beforeclose", function(t, e){
56089                 e.cancel = true;
56090                 this.remove(panel);
56091             }, this);
56092         }
56093         return ti;
56094     },
56095
56096     updatePanelTitle : function(panel, title){
56097         if(this.activePanel == panel){
56098             this.updateTitle(title);
56099         }
56100         if(this.tabs){
56101             var ti = this.tabs.getTab(panel.getEl().id);
56102             ti.setText(title);
56103             if(panel.tabTip !== undefined){
56104                 ti.setTooltip(panel.tabTip);
56105             }
56106         }
56107     },
56108
56109     updateTitle : function(title){
56110         if(this.titleTextEl && !this.config.title){
56111             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56112         }
56113     },
56114
56115     setActivePanel : function(panel){
56116         panel = this.getPanel(panel);
56117         if(this.activePanel && this.activePanel != panel){
56118             this.activePanel.setActiveState(false);
56119         }
56120         this.activePanel = panel;
56121         panel.setActiveState(true);
56122         if(this.panelSize){
56123             panel.setSize(this.panelSize.width, this.panelSize.height);
56124         }
56125         if(this.closeBtn){
56126             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56127         }
56128         this.updateTitle(panel.getTitle());
56129         if(this.tabs){
56130             this.fireEvent("invalidated", this);
56131         }
56132         this.fireEvent("panelactivated", this, panel);
56133     },
56134
56135     /**
56136      * Shows the specified panel.
56137      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56138      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56139      */
56140     showPanel : function(panel)
56141     {
56142         panel = this.getPanel(panel);
56143         if(panel){
56144             if(this.tabs){
56145                 var tab = this.tabs.getTab(panel.getEl().id);
56146                 if(tab.isHidden()){
56147                     this.tabs.unhideTab(tab.id);
56148                 }
56149                 tab.activate();
56150             }else{
56151                 this.setActivePanel(panel);
56152             }
56153         }
56154         return panel;
56155     },
56156
56157     /**
56158      * Get the active panel for this region.
56159      * @return {Roo.ContentPanel} The active panel or null
56160      */
56161     getActivePanel : function(){
56162         return this.activePanel;
56163     },
56164
56165     validateVisibility : function(){
56166         if(this.panels.getCount() < 1){
56167             this.updateTitle("&#160;");
56168             this.closeBtn.hide();
56169             this.hide();
56170         }else{
56171             if(!this.isVisible()){
56172                 this.show();
56173             }
56174         }
56175     },
56176
56177     /**
56178      * Adds the passed ContentPanel(s) to this region.
56179      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56180      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56181      */
56182     add : function(panel){
56183         if(arguments.length > 1){
56184             for(var i = 0, len = arguments.length; i < len; i++) {
56185                 this.add(arguments[i]);
56186             }
56187             return null;
56188         }
56189         if(this.hasPanel(panel)){
56190             this.showPanel(panel);
56191             return panel;
56192         }
56193         panel.setRegion(this);
56194         this.panels.add(panel);
56195         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56196             this.bodyEl.dom.appendChild(panel.getEl().dom);
56197             if(panel.background !== true){
56198                 this.setActivePanel(panel);
56199             }
56200             this.fireEvent("paneladded", this, panel);
56201             return panel;
56202         }
56203         if(!this.tabs){
56204             this.initTabs();
56205         }else{
56206             this.initPanelAsTab(panel);
56207         }
56208         if(panel.background !== true){
56209             this.tabs.activate(panel.getEl().id);
56210         }
56211         this.fireEvent("paneladded", this, panel);
56212         return panel;
56213     },
56214
56215     /**
56216      * Hides the tab for the specified panel.
56217      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56218      */
56219     hidePanel : function(panel){
56220         if(this.tabs && (panel = this.getPanel(panel))){
56221             this.tabs.hideTab(panel.getEl().id);
56222         }
56223     },
56224
56225     /**
56226      * Unhides the tab for a previously hidden panel.
56227      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56228      */
56229     unhidePanel : function(panel){
56230         if(this.tabs && (panel = this.getPanel(panel))){
56231             this.tabs.unhideTab(panel.getEl().id);
56232         }
56233     },
56234
56235     clearPanels : function(){
56236         while(this.panels.getCount() > 0){
56237              this.remove(this.panels.first());
56238         }
56239     },
56240
56241     /**
56242      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56243      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56244      * @param {Boolean} preservePanel Overrides the config preservePanel option
56245      * @return {Roo.ContentPanel} The panel that was removed
56246      */
56247     remove : function(panel, preservePanel){
56248         panel = this.getPanel(panel);
56249         if(!panel){
56250             return null;
56251         }
56252         var e = {};
56253         this.fireEvent("beforeremove", this, panel, e);
56254         if(e.cancel === true){
56255             return null;
56256         }
56257         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56258         var panelId = panel.getId();
56259         this.panels.removeKey(panelId);
56260         if(preservePanel){
56261             document.body.appendChild(panel.getEl().dom);
56262         }
56263         if(this.tabs){
56264             this.tabs.removeTab(panel.getEl().id);
56265         }else if (!preservePanel){
56266             this.bodyEl.dom.removeChild(panel.getEl().dom);
56267         }
56268         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56269             var p = this.panels.first();
56270             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56271             tempEl.appendChild(p.getEl().dom);
56272             this.bodyEl.update("");
56273             this.bodyEl.dom.appendChild(p.getEl().dom);
56274             tempEl = null;
56275             this.updateTitle(p.getTitle());
56276             this.tabs = null;
56277             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56278             this.setActivePanel(p);
56279         }
56280         panel.setRegion(null);
56281         if(this.activePanel == panel){
56282             this.activePanel = null;
56283         }
56284         if(this.config.autoDestroy !== false && preservePanel !== true){
56285             try{panel.destroy();}catch(e){}
56286         }
56287         this.fireEvent("panelremoved", this, panel);
56288         return panel;
56289     },
56290
56291     /**
56292      * Returns the TabPanel component used by this region
56293      * @return {Roo.TabPanel}
56294      */
56295     getTabs : function(){
56296         return this.tabs;
56297     },
56298
56299     createTool : function(parentEl, className){
56300         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56301             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56302         btn.addClassOnOver("x-layout-tools-button-over");
56303         return btn;
56304     }
56305 });/*
56306  * Based on:
56307  * Ext JS Library 1.1.1
56308  * Copyright(c) 2006-2007, Ext JS, LLC.
56309  *
56310  * Originally Released Under LGPL - original licence link has changed is not relivant.
56311  *
56312  * Fork - LGPL
56313  * <script type="text/javascript">
56314  */
56315  
56316
56317
56318 /**
56319  * @class Roo.SplitLayoutRegion
56320  * @extends Roo.LayoutRegion
56321  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56322  */
56323 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56324     this.cursor = cursor;
56325     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56326 };
56327
56328 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56329     splitTip : "Drag to resize.",
56330     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56331     useSplitTips : false,
56332
56333     applyConfig : function(config){
56334         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56335         if(config.split){
56336             if(!this.split){
56337                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56338                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56339                 /** The SplitBar for this region 
56340                 * @type Roo.SplitBar */
56341                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56342                 this.split.on("moved", this.onSplitMove, this);
56343                 this.split.useShim = config.useShim === true;
56344                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56345                 if(this.useSplitTips){
56346                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56347                 }
56348                 if(config.collapsible){
56349                     this.split.el.on("dblclick", this.collapse,  this);
56350                 }
56351             }
56352             if(typeof config.minSize != "undefined"){
56353                 this.split.minSize = config.minSize;
56354             }
56355             if(typeof config.maxSize != "undefined"){
56356                 this.split.maxSize = config.maxSize;
56357             }
56358             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56359                 this.hideSplitter();
56360             }
56361         }
56362     },
56363
56364     getHMaxSize : function(){
56365          var cmax = this.config.maxSize || 10000;
56366          var center = this.mgr.getRegion("center");
56367          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56368     },
56369
56370     getVMaxSize : function(){
56371          var cmax = this.config.maxSize || 10000;
56372          var center = this.mgr.getRegion("center");
56373          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56374     },
56375
56376     onSplitMove : function(split, newSize){
56377         this.fireEvent("resized", this, newSize);
56378     },
56379     
56380     /** 
56381      * Returns the {@link Roo.SplitBar} for this region.
56382      * @return {Roo.SplitBar}
56383      */
56384     getSplitBar : function(){
56385         return this.split;
56386     },
56387     
56388     hide : function(){
56389         this.hideSplitter();
56390         Roo.SplitLayoutRegion.superclass.hide.call(this);
56391     },
56392
56393     hideSplitter : function(){
56394         if(this.split){
56395             this.split.el.setLocation(-2000,-2000);
56396             this.split.el.hide();
56397         }
56398     },
56399
56400     show : function(){
56401         if(this.split){
56402             this.split.el.show();
56403         }
56404         Roo.SplitLayoutRegion.superclass.show.call(this);
56405     },
56406     
56407     beforeSlide: function(){
56408         if(Roo.isGecko){// firefox overflow auto bug workaround
56409             this.bodyEl.clip();
56410             if(this.tabs) {
56411                 this.tabs.bodyEl.clip();
56412             }
56413             if(this.activePanel){
56414                 this.activePanel.getEl().clip();
56415                 
56416                 if(this.activePanel.beforeSlide){
56417                     this.activePanel.beforeSlide();
56418                 }
56419             }
56420         }
56421     },
56422     
56423     afterSlide : function(){
56424         if(Roo.isGecko){// firefox overflow auto bug workaround
56425             this.bodyEl.unclip();
56426             if(this.tabs) {
56427                 this.tabs.bodyEl.unclip();
56428             }
56429             if(this.activePanel){
56430                 this.activePanel.getEl().unclip();
56431                 if(this.activePanel.afterSlide){
56432                     this.activePanel.afterSlide();
56433                 }
56434             }
56435         }
56436     },
56437
56438     initAutoHide : function(){
56439         if(this.autoHide !== false){
56440             if(!this.autoHideHd){
56441                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56442                 this.autoHideHd = {
56443                     "mouseout": function(e){
56444                         if(!e.within(this.el, true)){
56445                             st.delay(500);
56446                         }
56447                     },
56448                     "mouseover" : function(e){
56449                         st.cancel();
56450                     },
56451                     scope : this
56452                 };
56453             }
56454             this.el.on(this.autoHideHd);
56455         }
56456     },
56457
56458     clearAutoHide : function(){
56459         if(this.autoHide !== false){
56460             this.el.un("mouseout", this.autoHideHd.mouseout);
56461             this.el.un("mouseover", this.autoHideHd.mouseover);
56462         }
56463     },
56464
56465     clearMonitor : function(){
56466         Roo.get(document).un("click", this.slideInIf, this);
56467     },
56468
56469     // these names are backwards but not changed for compat
56470     slideOut : function(){
56471         if(this.isSlid || this.el.hasActiveFx()){
56472             return;
56473         }
56474         this.isSlid = true;
56475         if(this.collapseBtn){
56476             this.collapseBtn.hide();
56477         }
56478         this.closeBtnState = this.closeBtn.getStyle('display');
56479         this.closeBtn.hide();
56480         if(this.stickBtn){
56481             this.stickBtn.show();
56482         }
56483         this.el.show();
56484         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56485         this.beforeSlide();
56486         this.el.setStyle("z-index", 10001);
56487         this.el.slideIn(this.getSlideAnchor(), {
56488             callback: function(){
56489                 this.afterSlide();
56490                 this.initAutoHide();
56491                 Roo.get(document).on("click", this.slideInIf, this);
56492                 this.fireEvent("slideshow", this);
56493             },
56494             scope: this,
56495             block: true
56496         });
56497     },
56498
56499     afterSlideIn : function(){
56500         this.clearAutoHide();
56501         this.isSlid = false;
56502         this.clearMonitor();
56503         this.el.setStyle("z-index", "");
56504         if(this.collapseBtn){
56505             this.collapseBtn.show();
56506         }
56507         this.closeBtn.setStyle('display', this.closeBtnState);
56508         if(this.stickBtn){
56509             this.stickBtn.hide();
56510         }
56511         this.fireEvent("slidehide", this);
56512     },
56513
56514     slideIn : function(cb){
56515         if(!this.isSlid || this.el.hasActiveFx()){
56516             Roo.callback(cb);
56517             return;
56518         }
56519         this.isSlid = false;
56520         this.beforeSlide();
56521         this.el.slideOut(this.getSlideAnchor(), {
56522             callback: function(){
56523                 this.el.setLeftTop(-10000, -10000);
56524                 this.afterSlide();
56525                 this.afterSlideIn();
56526                 Roo.callback(cb);
56527             },
56528             scope: this,
56529             block: true
56530         });
56531     },
56532     
56533     slideInIf : function(e){
56534         if(!e.within(this.el)){
56535             this.slideIn();
56536         }
56537     },
56538
56539     animateCollapse : function(){
56540         this.beforeSlide();
56541         this.el.setStyle("z-index", 20000);
56542         var anchor = this.getSlideAnchor();
56543         this.el.slideOut(anchor, {
56544             callback : function(){
56545                 this.el.setStyle("z-index", "");
56546                 this.collapsedEl.slideIn(anchor, {duration:.3});
56547                 this.afterSlide();
56548                 this.el.setLocation(-10000,-10000);
56549                 this.el.hide();
56550                 this.fireEvent("collapsed", this);
56551             },
56552             scope: this,
56553             block: true
56554         });
56555     },
56556
56557     animateExpand : function(){
56558         this.beforeSlide();
56559         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56560         this.el.setStyle("z-index", 20000);
56561         this.collapsedEl.hide({
56562             duration:.1
56563         });
56564         this.el.slideIn(this.getSlideAnchor(), {
56565             callback : function(){
56566                 this.el.setStyle("z-index", "");
56567                 this.afterSlide();
56568                 if(this.split){
56569                     this.split.el.show();
56570                 }
56571                 this.fireEvent("invalidated", this);
56572                 this.fireEvent("expanded", this);
56573             },
56574             scope: this,
56575             block: true
56576         });
56577     },
56578
56579     anchors : {
56580         "west" : "left",
56581         "east" : "right",
56582         "north" : "top",
56583         "south" : "bottom"
56584     },
56585
56586     sanchors : {
56587         "west" : "l",
56588         "east" : "r",
56589         "north" : "t",
56590         "south" : "b"
56591     },
56592
56593     canchors : {
56594         "west" : "tl-tr",
56595         "east" : "tr-tl",
56596         "north" : "tl-bl",
56597         "south" : "bl-tl"
56598     },
56599
56600     getAnchor : function(){
56601         return this.anchors[this.position];
56602     },
56603
56604     getCollapseAnchor : function(){
56605         return this.canchors[this.position];
56606     },
56607
56608     getSlideAnchor : function(){
56609         return this.sanchors[this.position];
56610     },
56611
56612     getAlignAdj : function(){
56613         var cm = this.cmargins;
56614         switch(this.position){
56615             case "west":
56616                 return [0, 0];
56617             break;
56618             case "east":
56619                 return [0, 0];
56620             break;
56621             case "north":
56622                 return [0, 0];
56623             break;
56624             case "south":
56625                 return [0, 0];
56626             break;
56627         }
56628     },
56629
56630     getExpandAdj : function(){
56631         var c = this.collapsedEl, cm = this.cmargins;
56632         switch(this.position){
56633             case "west":
56634                 return [-(cm.right+c.getWidth()+cm.left), 0];
56635             break;
56636             case "east":
56637                 return [cm.right+c.getWidth()+cm.left, 0];
56638             break;
56639             case "north":
56640                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56641             break;
56642             case "south":
56643                 return [0, cm.top+cm.bottom+c.getHeight()];
56644             break;
56645         }
56646     }
56647 });/*
56648  * Based on:
56649  * Ext JS Library 1.1.1
56650  * Copyright(c) 2006-2007, Ext JS, LLC.
56651  *
56652  * Originally Released Under LGPL - original licence link has changed is not relivant.
56653  *
56654  * Fork - LGPL
56655  * <script type="text/javascript">
56656  */
56657 /*
56658  * These classes are private internal classes
56659  */
56660 Roo.CenterLayoutRegion = function(mgr, config){
56661     Roo.LayoutRegion.call(this, mgr, config, "center");
56662     this.visible = true;
56663     this.minWidth = config.minWidth || 20;
56664     this.minHeight = config.minHeight || 20;
56665 };
56666
56667 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56668     hide : function(){
56669         // center panel can't be hidden
56670     },
56671     
56672     show : function(){
56673         // center panel can't be hidden
56674     },
56675     
56676     getMinWidth: function(){
56677         return this.minWidth;
56678     },
56679     
56680     getMinHeight: function(){
56681         return this.minHeight;
56682     }
56683 });
56684
56685
56686 Roo.NorthLayoutRegion = function(mgr, config){
56687     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56688     if(this.split){
56689         this.split.placement = Roo.SplitBar.TOP;
56690         this.split.orientation = Roo.SplitBar.VERTICAL;
56691         this.split.el.addClass("x-layout-split-v");
56692     }
56693     var size = config.initialSize || config.height;
56694     if(typeof size != "undefined"){
56695         this.el.setHeight(size);
56696     }
56697 };
56698 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56699     orientation: Roo.SplitBar.VERTICAL,
56700     getBox : function(){
56701         if(this.collapsed){
56702             return this.collapsedEl.getBox();
56703         }
56704         var box = this.el.getBox();
56705         if(this.split){
56706             box.height += this.split.el.getHeight();
56707         }
56708         return box;
56709     },
56710     
56711     updateBox : function(box){
56712         if(this.split && !this.collapsed){
56713             box.height -= this.split.el.getHeight();
56714             this.split.el.setLeft(box.x);
56715             this.split.el.setTop(box.y+box.height);
56716             this.split.el.setWidth(box.width);
56717         }
56718         if(this.collapsed){
56719             this.updateBody(box.width, null);
56720         }
56721         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56722     }
56723 });
56724
56725 Roo.SouthLayoutRegion = function(mgr, config){
56726     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56727     if(this.split){
56728         this.split.placement = Roo.SplitBar.BOTTOM;
56729         this.split.orientation = Roo.SplitBar.VERTICAL;
56730         this.split.el.addClass("x-layout-split-v");
56731     }
56732     var size = config.initialSize || config.height;
56733     if(typeof size != "undefined"){
56734         this.el.setHeight(size);
56735     }
56736 };
56737 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56738     orientation: Roo.SplitBar.VERTICAL,
56739     getBox : function(){
56740         if(this.collapsed){
56741             return this.collapsedEl.getBox();
56742         }
56743         var box = this.el.getBox();
56744         if(this.split){
56745             var sh = this.split.el.getHeight();
56746             box.height += sh;
56747             box.y -= sh;
56748         }
56749         return box;
56750     },
56751     
56752     updateBox : function(box){
56753         if(this.split && !this.collapsed){
56754             var sh = this.split.el.getHeight();
56755             box.height -= sh;
56756             box.y += sh;
56757             this.split.el.setLeft(box.x);
56758             this.split.el.setTop(box.y-sh);
56759             this.split.el.setWidth(box.width);
56760         }
56761         if(this.collapsed){
56762             this.updateBody(box.width, null);
56763         }
56764         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56765     }
56766 });
56767
56768 Roo.EastLayoutRegion = function(mgr, config){
56769     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56770     if(this.split){
56771         this.split.placement = Roo.SplitBar.RIGHT;
56772         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56773         this.split.el.addClass("x-layout-split-h");
56774     }
56775     var size = config.initialSize || config.width;
56776     if(typeof size != "undefined"){
56777         this.el.setWidth(size);
56778     }
56779 };
56780 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56781     orientation: Roo.SplitBar.HORIZONTAL,
56782     getBox : function(){
56783         if(this.collapsed){
56784             return this.collapsedEl.getBox();
56785         }
56786         var box = this.el.getBox();
56787         if(this.split){
56788             var sw = this.split.el.getWidth();
56789             box.width += sw;
56790             box.x -= sw;
56791         }
56792         return box;
56793     },
56794
56795     updateBox : function(box){
56796         if(this.split && !this.collapsed){
56797             var sw = this.split.el.getWidth();
56798             box.width -= sw;
56799             this.split.el.setLeft(box.x);
56800             this.split.el.setTop(box.y);
56801             this.split.el.setHeight(box.height);
56802             box.x += sw;
56803         }
56804         if(this.collapsed){
56805             this.updateBody(null, box.height);
56806         }
56807         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56808     }
56809 });
56810
56811 Roo.WestLayoutRegion = function(mgr, config){
56812     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56813     if(this.split){
56814         this.split.placement = Roo.SplitBar.LEFT;
56815         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56816         this.split.el.addClass("x-layout-split-h");
56817     }
56818     var size = config.initialSize || config.width;
56819     if(typeof size != "undefined"){
56820         this.el.setWidth(size);
56821     }
56822 };
56823 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56824     orientation: Roo.SplitBar.HORIZONTAL,
56825     getBox : function(){
56826         if(this.collapsed){
56827             return this.collapsedEl.getBox();
56828         }
56829         var box = this.el.getBox();
56830         if(this.split){
56831             box.width += this.split.el.getWidth();
56832         }
56833         return box;
56834     },
56835     
56836     updateBox : function(box){
56837         if(this.split && !this.collapsed){
56838             var sw = this.split.el.getWidth();
56839             box.width -= sw;
56840             this.split.el.setLeft(box.x+box.width);
56841             this.split.el.setTop(box.y);
56842             this.split.el.setHeight(box.height);
56843         }
56844         if(this.collapsed){
56845             this.updateBody(null, box.height);
56846         }
56847         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56848     }
56849 });
56850 /*
56851  * Based on:
56852  * Ext JS Library 1.1.1
56853  * Copyright(c) 2006-2007, Ext JS, LLC.
56854  *
56855  * Originally Released Under LGPL - original licence link has changed is not relivant.
56856  *
56857  * Fork - LGPL
56858  * <script type="text/javascript">
56859  */
56860  
56861  
56862 /*
56863  * Private internal class for reading and applying state
56864  */
56865 Roo.LayoutStateManager = function(layout){
56866      // default empty state
56867      this.state = {
56868         north: {},
56869         south: {},
56870         east: {},
56871         west: {}       
56872     };
56873 };
56874
56875 Roo.LayoutStateManager.prototype = {
56876     init : function(layout, provider){
56877         this.provider = provider;
56878         var state = provider.get(layout.id+"-layout-state");
56879         if(state){
56880             var wasUpdating = layout.isUpdating();
56881             if(!wasUpdating){
56882                 layout.beginUpdate();
56883             }
56884             for(var key in state){
56885                 if(typeof state[key] != "function"){
56886                     var rstate = state[key];
56887                     var r = layout.getRegion(key);
56888                     if(r && rstate){
56889                         if(rstate.size){
56890                             r.resizeTo(rstate.size);
56891                         }
56892                         if(rstate.collapsed == true){
56893                             r.collapse(true);
56894                         }else{
56895                             r.expand(null, true);
56896                         }
56897                     }
56898                 }
56899             }
56900             if(!wasUpdating){
56901                 layout.endUpdate();
56902             }
56903             this.state = state; 
56904         }
56905         this.layout = layout;
56906         layout.on("regionresized", this.onRegionResized, this);
56907         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56908         layout.on("regionexpanded", this.onRegionExpanded, this);
56909     },
56910     
56911     storeState : function(){
56912         this.provider.set(this.layout.id+"-layout-state", this.state);
56913     },
56914     
56915     onRegionResized : function(region, newSize){
56916         this.state[region.getPosition()].size = newSize;
56917         this.storeState();
56918     },
56919     
56920     onRegionCollapsed : function(region){
56921         this.state[region.getPosition()].collapsed = true;
56922         this.storeState();
56923     },
56924     
56925     onRegionExpanded : function(region){
56926         this.state[region.getPosition()].collapsed = false;
56927         this.storeState();
56928     }
56929 };/*
56930  * Based on:
56931  * Ext JS Library 1.1.1
56932  * Copyright(c) 2006-2007, Ext JS, LLC.
56933  *
56934  * Originally Released Under LGPL - original licence link has changed is not relivant.
56935  *
56936  * Fork - LGPL
56937  * <script type="text/javascript">
56938  */
56939 /**
56940  * @class Roo.ContentPanel
56941  * @extends Roo.util.Observable
56942  * @children Roo.form.Form Roo.JsonView Roo.View
56943  * @parent Roo.BorderLayout Roo.LayoutDialog builder
56944  * A basic ContentPanel element.
56945  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56946  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56947  * @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
56948  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56949  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56950  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56951  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56952  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56953  * @cfg {String} title          The title for this panel
56954  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56955  * @cfg {String} url            Calls {@link #setUrl} with this value
56956  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
56957  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56958  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56959  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56960  * @cfg {String}    style  Extra style to add to the content panel
56961  * @cfg {Roo.menu.Menu} menu  popup menu
56962
56963  * @constructor
56964  * Create a new ContentPanel.
56965  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56966  * @param {String/Object} config A string to set only the title or a config object
56967  * @param {String} content (optional) Set the HTML content for this panel
56968  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56969  */
56970 Roo.ContentPanel = function(el, config, content){
56971     
56972      
56973     /*
56974     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56975         config = el;
56976         el = Roo.id();
56977     }
56978     if (config && config.parentLayout) { 
56979         el = config.parentLayout.el.createChild(); 
56980     }
56981     */
56982     if(el.autoCreate){ // xtype is available if this is called from factory
56983         config = el;
56984         el = Roo.id();
56985     }
56986     this.el = Roo.get(el);
56987     if(!this.el && config && config.autoCreate){
56988         if(typeof config.autoCreate == "object"){
56989             if(!config.autoCreate.id){
56990                 config.autoCreate.id = config.id||el;
56991             }
56992             this.el = Roo.DomHelper.append(document.body,
56993                         config.autoCreate, true);
56994         }else{
56995             this.el = Roo.DomHelper.append(document.body,
56996                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56997         }
56998     }
56999     
57000     
57001     this.closable = false;
57002     this.loaded = false;
57003     this.active = false;
57004     if(typeof config == "string"){
57005         this.title = config;
57006     }else{
57007         Roo.apply(this, config);
57008     }
57009     
57010     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57011         this.wrapEl = this.el.wrap();
57012         this.toolbar.container = this.el.insertSibling(false, 'before');
57013         this.toolbar = new Roo.Toolbar(this.toolbar);
57014     }
57015     
57016     // xtype created footer. - not sure if will work as we normally have to render first..
57017     if (this.footer && !this.footer.el && this.footer.xtype) {
57018         if (!this.wrapEl) {
57019             this.wrapEl = this.el.wrap();
57020         }
57021     
57022         this.footer.container = this.wrapEl.createChild();
57023          
57024         this.footer = Roo.factory(this.footer, Roo);
57025         
57026     }
57027     
57028     if(this.resizeEl){
57029         this.resizeEl = Roo.get(this.resizeEl, true);
57030     }else{
57031         this.resizeEl = this.el;
57032     }
57033     // handle view.xtype
57034     
57035  
57036     
57037     
57038     this.addEvents({
57039         /**
57040          * @event activate
57041          * Fires when this panel is activated. 
57042          * @param {Roo.ContentPanel} this
57043          */
57044         "activate" : true,
57045         /**
57046          * @event deactivate
57047          * Fires when this panel is activated. 
57048          * @param {Roo.ContentPanel} this
57049          */
57050         "deactivate" : true,
57051
57052         /**
57053          * @event resize
57054          * Fires when this panel is resized if fitToFrame is true.
57055          * @param {Roo.ContentPanel} this
57056          * @param {Number} width The width after any component adjustments
57057          * @param {Number} height The height after any component adjustments
57058          */
57059         "resize" : true,
57060         
57061          /**
57062          * @event render
57063          * Fires when this tab is created
57064          * @param {Roo.ContentPanel} this
57065          */
57066         "render" : true
57067          
57068         
57069     });
57070     
57071
57072     
57073     
57074     if(this.autoScroll){
57075         this.resizeEl.setStyle("overflow", "auto");
57076     } else {
57077         // fix randome scrolling
57078         this.el.on('scroll', function() {
57079             Roo.log('fix random scolling');
57080             this.scrollTo('top',0); 
57081         });
57082     }
57083     content = content || this.content;
57084     if(content){
57085         this.setContent(content);
57086     }
57087     if(config && config.url){
57088         this.setUrl(this.url, this.params, this.loadOnce);
57089     }
57090     
57091     
57092     
57093     Roo.ContentPanel.superclass.constructor.call(this);
57094     
57095     if (this.view && typeof(this.view.xtype) != 'undefined') {
57096         this.view.el = this.el.appendChild(document.createElement("div"));
57097         this.view = Roo.factory(this.view); 
57098         this.view.render  &&  this.view.render(false, '');  
57099     }
57100     
57101     
57102     this.fireEvent('render', this);
57103 };
57104
57105 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57106     tabTip:'',
57107     setRegion : function(region){
57108         this.region = region;
57109         if(region){
57110            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57111         }else{
57112            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57113         } 
57114     },
57115     
57116     /**
57117      * Returns the toolbar for this Panel if one was configured. 
57118      * @return {Roo.Toolbar} 
57119      */
57120     getToolbar : function(){
57121         return this.toolbar;
57122     },
57123     
57124     setActiveState : function(active){
57125         this.active = active;
57126         if(!active){
57127             this.fireEvent("deactivate", this);
57128         }else{
57129             this.fireEvent("activate", this);
57130         }
57131     },
57132     /**
57133      * Updates this panel's element
57134      * @param {String} content The new content
57135      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57136     */
57137     setContent : function(content, loadScripts){
57138         this.el.update(content, loadScripts);
57139     },
57140
57141     ignoreResize : function(w, h){
57142         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57143             return true;
57144         }else{
57145             this.lastSize = {width: w, height: h};
57146             return false;
57147         }
57148     },
57149     /**
57150      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57151      * @return {Roo.UpdateManager} The UpdateManager
57152      */
57153     getUpdateManager : function(){
57154         return this.el.getUpdateManager();
57155     },
57156      /**
57157      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57158      * @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:
57159 <pre><code>
57160 panel.load({
57161     url: "your-url.php",
57162     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57163     callback: yourFunction,
57164     scope: yourObject, //(optional scope)
57165     discardUrl: false,
57166     nocache: false,
57167     text: "Loading...",
57168     timeout: 30,
57169     scripts: false
57170 });
57171 </code></pre>
57172      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57173      * 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.
57174      * @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}
57175      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57176      * @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.
57177      * @return {Roo.ContentPanel} this
57178      */
57179     load : function(){
57180         var um = this.el.getUpdateManager();
57181         um.update.apply(um, arguments);
57182         return this;
57183     },
57184
57185
57186     /**
57187      * 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.
57188      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57189      * @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)
57190      * @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)
57191      * @return {Roo.UpdateManager} The UpdateManager
57192      */
57193     setUrl : function(url, params, loadOnce){
57194         if(this.refreshDelegate){
57195             this.removeListener("activate", this.refreshDelegate);
57196         }
57197         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57198         this.on("activate", this.refreshDelegate);
57199         return this.el.getUpdateManager();
57200     },
57201     
57202     _handleRefresh : function(url, params, loadOnce){
57203         if(!loadOnce || !this.loaded){
57204             var updater = this.el.getUpdateManager();
57205             updater.update(url, params, this._setLoaded.createDelegate(this));
57206         }
57207     },
57208     
57209     _setLoaded : function(){
57210         this.loaded = true;
57211     }, 
57212     
57213     /**
57214      * Returns this panel's id
57215      * @return {String} 
57216      */
57217     getId : function(){
57218         return this.el.id;
57219     },
57220     
57221     /** 
57222      * Returns this panel's element - used by regiosn to add.
57223      * @return {Roo.Element} 
57224      */
57225     getEl : function(){
57226         return this.wrapEl || this.el;
57227     },
57228     
57229     adjustForComponents : function(width, height)
57230     {
57231         //Roo.log('adjustForComponents ');
57232         if(this.resizeEl != this.el){
57233             width -= this.el.getFrameWidth('lr');
57234             height -= this.el.getFrameWidth('tb');
57235         }
57236         if(this.toolbar){
57237             var te = this.toolbar.getEl();
57238             height -= te.getHeight();
57239             te.setWidth(width);
57240         }
57241         if(this.footer){
57242             var te = this.footer.getEl();
57243             //Roo.log("footer:" + te.getHeight());
57244             
57245             height -= te.getHeight();
57246             te.setWidth(width);
57247         }
57248         
57249         
57250         if(this.adjustments){
57251             width += this.adjustments[0];
57252             height += this.adjustments[1];
57253         }
57254         return {"width": width, "height": height};
57255     },
57256     
57257     setSize : function(width, height){
57258         if(this.fitToFrame && !this.ignoreResize(width, height)){
57259             if(this.fitContainer && this.resizeEl != this.el){
57260                 this.el.setSize(width, height);
57261             }
57262             var size = this.adjustForComponents(width, height);
57263             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57264             this.fireEvent('resize', this, size.width, size.height);
57265         }
57266     },
57267     
57268     /**
57269      * Returns this panel's title
57270      * @return {String} 
57271      */
57272     getTitle : function(){
57273         return this.title;
57274     },
57275     
57276     /**
57277      * Set this panel's title
57278      * @param {String} title
57279      */
57280     setTitle : function(title){
57281         this.title = title;
57282         if(this.region){
57283             this.region.updatePanelTitle(this, title);
57284         }
57285     },
57286     
57287     /**
57288      * Returns true is this panel was configured to be closable
57289      * @return {Boolean} 
57290      */
57291     isClosable : function(){
57292         return this.closable;
57293     },
57294     
57295     beforeSlide : function(){
57296         this.el.clip();
57297         this.resizeEl.clip();
57298     },
57299     
57300     afterSlide : function(){
57301         this.el.unclip();
57302         this.resizeEl.unclip();
57303     },
57304     
57305     /**
57306      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57307      *   Will fail silently if the {@link #setUrl} method has not been called.
57308      *   This does not activate the panel, just updates its content.
57309      */
57310     refresh : function(){
57311         if(this.refreshDelegate){
57312            this.loaded = false;
57313            this.refreshDelegate();
57314         }
57315     },
57316     
57317     /**
57318      * Destroys this panel
57319      */
57320     destroy : function(){
57321         this.el.removeAllListeners();
57322         var tempEl = document.createElement("span");
57323         tempEl.appendChild(this.el.dom);
57324         tempEl.innerHTML = "";
57325         this.el.remove();
57326         this.el = null;
57327     },
57328     
57329     /**
57330      * form - if the content panel contains a form - this is a reference to it.
57331      * @type {Roo.form.Form}
57332      */
57333     form : false,
57334     /**
57335      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57336      *    This contains a reference to it.
57337      * @type {Roo.View}
57338      */
57339     view : false,
57340     
57341       /**
57342      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57343      * <pre><code>
57344
57345 layout.addxtype({
57346        xtype : 'Form',
57347        items: [ .... ]
57348    }
57349 );
57350
57351 </code></pre>
57352      * @param {Object} cfg Xtype definition of item to add.
57353      */
57354     
57355     addxtype : function(cfg) {
57356         // add form..
57357         if (cfg.xtype.match(/^Form$/)) {
57358             
57359             var el;
57360             //if (this.footer) {
57361             //    el = this.footer.container.insertSibling(false, 'before');
57362             //} else {
57363                 el = this.el.createChild();
57364             //}
57365
57366             this.form = new  Roo.form.Form(cfg);
57367             
57368             
57369             if ( this.form.allItems.length) {
57370                 this.form.render(el.dom);
57371             }
57372             return this.form;
57373         }
57374         // should only have one of theses..
57375         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57376             // views.. should not be just added - used named prop 'view''
57377             
57378             cfg.el = this.el.appendChild(document.createElement("div"));
57379             // factory?
57380             
57381             var ret = new Roo.factory(cfg);
57382              
57383              ret.render && ret.render(false, ''); // render blank..
57384             this.view = ret;
57385             return ret;
57386         }
57387         return false;
57388     }
57389 });
57390
57391 /**
57392  * @class Roo.GridPanel
57393  * @extends Roo.ContentPanel
57394  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57395  * @constructor
57396  * Create a new GridPanel.
57397  * @cfg {Roo.grid.Grid} grid The grid for this panel
57398  */
57399 Roo.GridPanel = function(grid, config){
57400     
57401     // universal ctor...
57402     if (typeof(grid.grid) != 'undefined') {
57403         config = grid;
57404         grid = config.grid;
57405     }
57406     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57407         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57408         
57409     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57410     
57411     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57412     
57413     if(this.toolbar){
57414         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57415     }
57416     // xtype created footer. - not sure if will work as we normally have to render first..
57417     if (this.footer && !this.footer.el && this.footer.xtype) {
57418         
57419         this.footer.container = this.grid.getView().getFooterPanel(true);
57420         this.footer.dataSource = this.grid.dataSource;
57421         this.footer = Roo.factory(this.footer, Roo);
57422         
57423     }
57424     
57425     grid.monitorWindowResize = false; // turn off autosizing
57426     grid.autoHeight = false;
57427     grid.autoWidth = false;
57428     this.grid = grid;
57429     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57430 };
57431
57432 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57433     getId : function(){
57434         return this.grid.id;
57435     },
57436     
57437     /**
57438      * Returns the grid for this panel
57439      * @return {Roo.grid.Grid} 
57440      */
57441     getGrid : function(){
57442         return this.grid;    
57443     },
57444     
57445     setSize : function(width, height){
57446         if(!this.ignoreResize(width, height)){
57447             var grid = this.grid;
57448             var size = this.adjustForComponents(width, height);
57449             grid.getGridEl().setSize(size.width, size.height);
57450             grid.autoSize();
57451         }
57452     },
57453     
57454     beforeSlide : function(){
57455         this.grid.getView().scroller.clip();
57456     },
57457     
57458     afterSlide : function(){
57459         this.grid.getView().scroller.unclip();
57460     },
57461     
57462     destroy : function(){
57463         this.grid.destroy();
57464         delete this.grid;
57465         Roo.GridPanel.superclass.destroy.call(this); 
57466     }
57467 });
57468
57469
57470 /**
57471  * @class Roo.NestedLayoutPanel
57472  * @extends Roo.ContentPanel
57473  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57474  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57475  *
57476  * 
57477  * @constructor
57478  * Create a new NestedLayoutPanel.
57479  * 
57480  * 
57481  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57482  * @param {String/Object} config A string to set only the title or a config object
57483  */
57484 Roo.NestedLayoutPanel = function(layout, config)
57485 {
57486     // construct with only one argument..
57487     /* FIXME - implement nicer consturctors
57488     if (layout.layout) {
57489         config = layout;
57490         layout = config.layout;
57491         delete config.layout;
57492     }
57493     if (layout.xtype && !layout.getEl) {
57494         // then layout needs constructing..
57495         layout = Roo.factory(layout, Roo);
57496     }
57497     */
57498     
57499     
57500     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57501     
57502     layout.monitorWindowResize = false; // turn off autosizing
57503     this.layout = layout;
57504     this.layout.getEl().addClass("x-layout-nested-layout");
57505     
57506     
57507     
57508     
57509 };
57510
57511 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57512
57513     setSize : function(width, height){
57514         if(!this.ignoreResize(width, height)){
57515             var size = this.adjustForComponents(width, height);
57516             var el = this.layout.getEl();
57517             el.setSize(size.width, size.height);
57518             var touch = el.dom.offsetWidth;
57519             this.layout.layout();
57520             // ie requires a double layout on the first pass
57521             if(Roo.isIE && !this.initialized){
57522                 this.initialized = true;
57523                 this.layout.layout();
57524             }
57525         }
57526     },
57527     
57528     // activate all subpanels if not currently active..
57529     
57530     setActiveState : function(active){
57531         this.active = active;
57532         if(!active){
57533             this.fireEvent("deactivate", this);
57534             return;
57535         }
57536         
57537         this.fireEvent("activate", this);
57538         // not sure if this should happen before or after..
57539         if (!this.layout) {
57540             return; // should not happen..
57541         }
57542         var reg = false;
57543         for (var r in this.layout.regions) {
57544             reg = this.layout.getRegion(r);
57545             if (reg.getActivePanel()) {
57546                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57547                 reg.setActivePanel(reg.getActivePanel());
57548                 continue;
57549             }
57550             if (!reg.panels.length) {
57551                 continue;
57552             }
57553             reg.showPanel(reg.getPanel(0));
57554         }
57555         
57556         
57557         
57558         
57559     },
57560     
57561     /**
57562      * Returns the nested BorderLayout for this panel
57563      * @return {Roo.BorderLayout} 
57564      */
57565     getLayout : function(){
57566         return this.layout;
57567     },
57568     
57569      /**
57570      * Adds a xtype elements to the layout of the nested panel
57571      * <pre><code>
57572
57573 panel.addxtype({
57574        xtype : 'ContentPanel',
57575        region: 'west',
57576        items: [ .... ]
57577    }
57578 );
57579
57580 panel.addxtype({
57581         xtype : 'NestedLayoutPanel',
57582         region: 'west',
57583         layout: {
57584            center: { },
57585            west: { }   
57586         },
57587         items : [ ... list of content panels or nested layout panels.. ]
57588    }
57589 );
57590 </code></pre>
57591      * @param {Object} cfg Xtype definition of item to add.
57592      */
57593     addxtype : function(cfg) {
57594         return this.layout.addxtype(cfg);
57595     
57596     }
57597 });
57598
57599 Roo.ScrollPanel = function(el, config, content){
57600     config = config || {};
57601     config.fitToFrame = true;
57602     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57603     
57604     this.el.dom.style.overflow = "hidden";
57605     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57606     this.el.removeClass("x-layout-inactive-content");
57607     this.el.on("mousewheel", this.onWheel, this);
57608
57609     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57610     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57611     up.unselectable(); down.unselectable();
57612     up.on("click", this.scrollUp, this);
57613     down.on("click", this.scrollDown, this);
57614     up.addClassOnOver("x-scroller-btn-over");
57615     down.addClassOnOver("x-scroller-btn-over");
57616     up.addClassOnClick("x-scroller-btn-click");
57617     down.addClassOnClick("x-scroller-btn-click");
57618     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57619
57620     this.resizeEl = this.el;
57621     this.el = wrap; this.up = up; this.down = down;
57622 };
57623
57624 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57625     increment : 100,
57626     wheelIncrement : 5,
57627     scrollUp : function(){
57628         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57629     },
57630
57631     scrollDown : function(){
57632         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57633     },
57634
57635     afterScroll : function(){
57636         var el = this.resizeEl;
57637         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57638         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57639         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57640     },
57641
57642     setSize : function(){
57643         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57644         this.afterScroll();
57645     },
57646
57647     onWheel : function(e){
57648         var d = e.getWheelDelta();
57649         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57650         this.afterScroll();
57651         e.stopEvent();
57652     },
57653
57654     setContent : function(content, loadScripts){
57655         this.resizeEl.update(content, loadScripts);
57656     }
57657
57658 });
57659
57660
57661
57662 /**
57663  * @class Roo.TreePanel
57664  * @extends Roo.ContentPanel
57665  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57666  * Treepanel component
57667  * 
57668  * @constructor
57669  * Create a new TreePanel. - defaults to fit/scoll contents.
57670  * @param {String/Object} config A string to set only the panel's title, or a config object
57671  */
57672 Roo.TreePanel = function(config){
57673     var el = config.el;
57674     var tree = config.tree;
57675     delete config.tree; 
57676     delete config.el; // hopefull!
57677     
57678     // wrapper for IE7 strict & safari scroll issue
57679     
57680     var treeEl = el.createChild();
57681     config.resizeEl = treeEl;
57682     
57683     
57684     
57685     Roo.TreePanel.superclass.constructor.call(this, el, config);
57686  
57687  
57688     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57689     //console.log(tree);
57690     this.on('activate', function()
57691     {
57692         if (this.tree.rendered) {
57693             return;
57694         }
57695         //console.log('render tree');
57696         this.tree.render();
57697     });
57698     // this should not be needed.. - it's actually the 'el' that resizes?
57699     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57700     
57701     //this.on('resize',  function (cp, w, h) {
57702     //        this.tree.innerCt.setWidth(w);
57703     //        this.tree.innerCt.setHeight(h);
57704     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57705     //});
57706
57707         
57708     
57709 };
57710
57711 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57712     fitToFrame : true,
57713     autoScroll : true,
57714     /*
57715      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57716      */
57717     tree : false
57718
57719 });
57720
57721
57722
57723
57724
57725
57726
57727
57728
57729
57730
57731 /*
57732  * Based on:
57733  * Ext JS Library 1.1.1
57734  * Copyright(c) 2006-2007, Ext JS, LLC.
57735  *
57736  * Originally Released Under LGPL - original licence link has changed is not relivant.
57737  *
57738  * Fork - LGPL
57739  * <script type="text/javascript">
57740  */
57741  
57742
57743 /**
57744  * @class Roo.ReaderLayout
57745  * @extends Roo.BorderLayout
57746  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57747  * center region containing two nested regions (a top one for a list view and one for item preview below),
57748  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57749  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57750  * expedites the setup of the overall layout and regions for this common application style.
57751  * Example:
57752  <pre><code>
57753 var reader = new Roo.ReaderLayout();
57754 var CP = Roo.ContentPanel;  // shortcut for adding
57755
57756 reader.beginUpdate();
57757 reader.add("north", new CP("north", "North"));
57758 reader.add("west", new CP("west", {title: "West"}));
57759 reader.add("east", new CP("east", {title: "East"}));
57760
57761 reader.regions.listView.add(new CP("listView", "List"));
57762 reader.regions.preview.add(new CP("preview", "Preview"));
57763 reader.endUpdate();
57764 </code></pre>
57765 * @constructor
57766 * Create a new ReaderLayout
57767 * @param {Object} config Configuration options
57768 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57769 * document.body if omitted)
57770 */
57771 Roo.ReaderLayout = function(config, renderTo){
57772     var c = config || {size:{}};
57773     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57774         north: c.north !== false ? Roo.apply({
57775             split:false,
57776             initialSize: 32,
57777             titlebar: false
57778         }, c.north) : false,
57779         west: c.west !== false ? Roo.apply({
57780             split:true,
57781             initialSize: 200,
57782             minSize: 175,
57783             maxSize: 400,
57784             titlebar: true,
57785             collapsible: true,
57786             animate: true,
57787             margins:{left:5,right:0,bottom:5,top:5},
57788             cmargins:{left:5,right:5,bottom:5,top:5}
57789         }, c.west) : false,
57790         east: c.east !== false ? Roo.apply({
57791             split:true,
57792             initialSize: 200,
57793             minSize: 175,
57794             maxSize: 400,
57795             titlebar: true,
57796             collapsible: true,
57797             animate: true,
57798             margins:{left:0,right:5,bottom:5,top:5},
57799             cmargins:{left:5,right:5,bottom:5,top:5}
57800         }, c.east) : false,
57801         center: Roo.apply({
57802             tabPosition: 'top',
57803             autoScroll:false,
57804             closeOnTab: true,
57805             titlebar:false,
57806             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57807         }, c.center)
57808     });
57809
57810     this.el.addClass('x-reader');
57811
57812     this.beginUpdate();
57813
57814     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57815         south: c.preview !== false ? Roo.apply({
57816             split:true,
57817             initialSize: 200,
57818             minSize: 100,
57819             autoScroll:true,
57820             collapsible:true,
57821             titlebar: true,
57822             cmargins:{top:5,left:0, right:0, bottom:0}
57823         }, c.preview) : false,
57824         center: Roo.apply({
57825             autoScroll:false,
57826             titlebar:false,
57827             minHeight:200
57828         }, c.listView)
57829     });
57830     this.add('center', new Roo.NestedLayoutPanel(inner,
57831             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57832
57833     this.endUpdate();
57834
57835     this.regions.preview = inner.getRegion('south');
57836     this.regions.listView = inner.getRegion('center');
57837 };
57838
57839 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57840  * Based on:
57841  * Ext JS Library 1.1.1
57842  * Copyright(c) 2006-2007, Ext JS, LLC.
57843  *
57844  * Originally Released Under LGPL - original licence link has changed is not relivant.
57845  *
57846  * Fork - LGPL
57847  * <script type="text/javascript">
57848  */
57849  
57850 /**
57851  * @class Roo.grid.Grid
57852  * @extends Roo.util.Observable
57853  * This class represents the primary interface of a component based grid control.
57854  * <br><br>Usage:<pre><code>
57855  var grid = new Roo.grid.Grid("my-container-id", {
57856      ds: myDataStore,
57857      cm: myColModel,
57858      selModel: mySelectionModel,
57859      autoSizeColumns: true,
57860      monitorWindowResize: false,
57861      trackMouseOver: true
57862  });
57863  // set any options
57864  grid.render();
57865  * </code></pre>
57866  * <b>Common Problems:</b><br/>
57867  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57868  * element will correct this<br/>
57869  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57870  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57871  * are unpredictable.<br/>
57872  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57873  * grid to calculate dimensions/offsets.<br/>
57874   * @constructor
57875  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57876  * The container MUST have some type of size defined for the grid to fill. The container will be
57877  * automatically set to position relative if it isn't already.
57878  * @param {Object} config A config object that sets properties on this grid.
57879  */
57880 Roo.grid.Grid = function(container, config){
57881         // initialize the container
57882         this.container = Roo.get(container);
57883         this.container.update("");
57884         this.container.setStyle("overflow", "hidden");
57885     this.container.addClass('x-grid-container');
57886
57887     this.id = this.container.id;
57888
57889     Roo.apply(this, config);
57890     // check and correct shorthanded configs
57891     if(this.ds){
57892         this.dataSource = this.ds;
57893         delete this.ds;
57894     }
57895     if(this.cm){
57896         this.colModel = this.cm;
57897         delete this.cm;
57898     }
57899     if(this.sm){
57900         this.selModel = this.sm;
57901         delete this.sm;
57902     }
57903
57904     if (this.selModel) {
57905         this.selModel = Roo.factory(this.selModel, Roo.grid);
57906         this.sm = this.selModel;
57907         this.sm.xmodule = this.xmodule || false;
57908     }
57909     if (typeof(this.colModel.config) == 'undefined') {
57910         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57911         this.cm = this.colModel;
57912         this.cm.xmodule = this.xmodule || false;
57913     }
57914     if (this.dataSource) {
57915         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57916         this.ds = this.dataSource;
57917         this.ds.xmodule = this.xmodule || false;
57918          
57919     }
57920     
57921     
57922     
57923     if(this.width){
57924         this.container.setWidth(this.width);
57925     }
57926
57927     if(this.height){
57928         this.container.setHeight(this.height);
57929     }
57930     /** @private */
57931         this.addEvents({
57932         // raw events
57933         /**
57934          * @event click
57935          * The raw click event for the entire grid.
57936          * @param {Roo.EventObject} e
57937          */
57938         "click" : true,
57939         /**
57940          * @event dblclick
57941          * The raw dblclick event for the entire grid.
57942          * @param {Roo.EventObject} e
57943          */
57944         "dblclick" : true,
57945         /**
57946          * @event contextmenu
57947          * The raw contextmenu event for the entire grid.
57948          * @param {Roo.EventObject} e
57949          */
57950         "contextmenu" : true,
57951         /**
57952          * @event mousedown
57953          * The raw mousedown event for the entire grid.
57954          * @param {Roo.EventObject} e
57955          */
57956         "mousedown" : true,
57957         /**
57958          * @event mouseup
57959          * The raw mouseup event for the entire grid.
57960          * @param {Roo.EventObject} e
57961          */
57962         "mouseup" : true,
57963         /**
57964          * @event mouseover
57965          * The raw mouseover event for the entire grid.
57966          * @param {Roo.EventObject} e
57967          */
57968         "mouseover" : true,
57969         /**
57970          * @event mouseout
57971          * The raw mouseout event for the entire grid.
57972          * @param {Roo.EventObject} e
57973          */
57974         "mouseout" : true,
57975         /**
57976          * @event keypress
57977          * The raw keypress event for the entire grid.
57978          * @param {Roo.EventObject} e
57979          */
57980         "keypress" : true,
57981         /**
57982          * @event keydown
57983          * The raw keydown event for the entire grid.
57984          * @param {Roo.EventObject} e
57985          */
57986         "keydown" : true,
57987
57988         // custom events
57989
57990         /**
57991          * @event cellclick
57992          * Fires when a cell is clicked
57993          * @param {Grid} this
57994          * @param {Number} rowIndex
57995          * @param {Number} columnIndex
57996          * @param {Roo.EventObject} e
57997          */
57998         "cellclick" : true,
57999         /**
58000          * @event celldblclick
58001          * Fires when a cell is double clicked
58002          * @param {Grid} this
58003          * @param {Number} rowIndex
58004          * @param {Number} columnIndex
58005          * @param {Roo.EventObject} e
58006          */
58007         "celldblclick" : true,
58008         /**
58009          * @event rowclick
58010          * Fires when a row is clicked
58011          * @param {Grid} this
58012          * @param {Number} rowIndex
58013          * @param {Roo.EventObject} e
58014          */
58015         "rowclick" : true,
58016         /**
58017          * @event rowdblclick
58018          * Fires when a row is double clicked
58019          * @param {Grid} this
58020          * @param {Number} rowIndex
58021          * @param {Roo.EventObject} e
58022          */
58023         "rowdblclick" : true,
58024         /**
58025          * @event headerclick
58026          * Fires when a header is clicked
58027          * @param {Grid} this
58028          * @param {Number} columnIndex
58029          * @param {Roo.EventObject} e
58030          */
58031         "headerclick" : true,
58032         /**
58033          * @event headerdblclick
58034          * Fires when a header cell is double clicked
58035          * @param {Grid} this
58036          * @param {Number} columnIndex
58037          * @param {Roo.EventObject} e
58038          */
58039         "headerdblclick" : true,
58040         /**
58041          * @event rowcontextmenu
58042          * Fires when a row is right clicked
58043          * @param {Grid} this
58044          * @param {Number} rowIndex
58045          * @param {Roo.EventObject} e
58046          */
58047         "rowcontextmenu" : true,
58048         /**
58049          * @event cellcontextmenu
58050          * Fires when a cell is right clicked
58051          * @param {Grid} this
58052          * @param {Number} rowIndex
58053          * @param {Number} cellIndex
58054          * @param {Roo.EventObject} e
58055          */
58056          "cellcontextmenu" : true,
58057         /**
58058          * @event headercontextmenu
58059          * Fires when a header is right clicked
58060          * @param {Grid} this
58061          * @param {Number} columnIndex
58062          * @param {Roo.EventObject} e
58063          */
58064         "headercontextmenu" : true,
58065         /**
58066          * @event bodyscroll
58067          * Fires when the body element is scrolled
58068          * @param {Number} scrollLeft
58069          * @param {Number} scrollTop
58070          */
58071         "bodyscroll" : true,
58072         /**
58073          * @event columnresize
58074          * Fires when the user resizes a column
58075          * @param {Number} columnIndex
58076          * @param {Number} newSize
58077          */
58078         "columnresize" : true,
58079         /**
58080          * @event columnmove
58081          * Fires when the user moves a column
58082          * @param {Number} oldIndex
58083          * @param {Number} newIndex
58084          */
58085         "columnmove" : true,
58086         /**
58087          * @event startdrag
58088          * Fires when row(s) start being dragged
58089          * @param {Grid} this
58090          * @param {Roo.GridDD} dd The drag drop object
58091          * @param {event} e The raw browser event
58092          */
58093         "startdrag" : true,
58094         /**
58095          * @event enddrag
58096          * Fires when a drag operation is complete
58097          * @param {Grid} this
58098          * @param {Roo.GridDD} dd The drag drop object
58099          * @param {event} e The raw browser event
58100          */
58101         "enddrag" : true,
58102         /**
58103          * @event dragdrop
58104          * Fires when dragged row(s) are dropped on a valid DD target
58105          * @param {Grid} this
58106          * @param {Roo.GridDD} dd The drag drop object
58107          * @param {String} targetId The target drag drop object
58108          * @param {event} e The raw browser event
58109          */
58110         "dragdrop" : true,
58111         /**
58112          * @event dragover
58113          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58114          * @param {Grid} this
58115          * @param {Roo.GridDD} dd The drag drop object
58116          * @param {String} targetId The target drag drop object
58117          * @param {event} e The raw browser event
58118          */
58119         "dragover" : true,
58120         /**
58121          * @event dragenter
58122          *  Fires when the dragged row(s) first cross another DD target while being dragged
58123          * @param {Grid} this
58124          * @param {Roo.GridDD} dd The drag drop object
58125          * @param {String} targetId The target drag drop object
58126          * @param {event} e The raw browser event
58127          */
58128         "dragenter" : true,
58129         /**
58130          * @event dragout
58131          * Fires when the dragged row(s) leave another DD target while being dragged
58132          * @param {Grid} this
58133          * @param {Roo.GridDD} dd The drag drop object
58134          * @param {String} targetId The target drag drop object
58135          * @param {event} e The raw browser event
58136          */
58137         "dragout" : true,
58138         /**
58139          * @event rowclass
58140          * Fires when a row is rendered, so you can change add a style to it.
58141          * @param {GridView} gridview   The grid view
58142          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58143          */
58144         'rowclass' : true,
58145
58146         /**
58147          * @event render
58148          * Fires when the grid is rendered
58149          * @param {Grid} grid
58150          */
58151         'render' : true
58152     });
58153
58154     Roo.grid.Grid.superclass.constructor.call(this);
58155 };
58156 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58157     
58158     /**
58159          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58160          */
58161         /**
58162          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58163          */
58164         /**
58165          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58166          */
58167         /**
58168          * @cfg {Roo.grid.Store} ds The data store for the grid
58169          */
58170         /**
58171          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58172          */
58173         /**
58174      * @cfg {String} ddGroup - drag drop group.
58175      */
58176       /**
58177      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58178      */
58179
58180     /**
58181      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58182      */
58183     minColumnWidth : 25,
58184
58185     /**
58186      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58187      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58188      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58189      */
58190     autoSizeColumns : false,
58191
58192     /**
58193      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58194      */
58195     autoSizeHeaders : true,
58196
58197     /**
58198      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58199      */
58200     monitorWindowResize : true,
58201
58202     /**
58203      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58204      * rows measured to get a columns size. Default is 0 (all rows).
58205      */
58206     maxRowsToMeasure : 0,
58207
58208     /**
58209      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58210      */
58211     trackMouseOver : true,
58212
58213     /**
58214     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58215     */
58216       /**
58217     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58218     */
58219     
58220     /**
58221     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58222     */
58223     enableDragDrop : false,
58224     
58225     /**
58226     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58227     */
58228     enableColumnMove : true,
58229     
58230     /**
58231     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58232     */
58233     enableColumnHide : true,
58234     
58235     /**
58236     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58237     */
58238     enableRowHeightSync : false,
58239     
58240     /**
58241     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58242     */
58243     stripeRows : true,
58244     
58245     /**
58246     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58247     */
58248     autoHeight : false,
58249
58250     /**
58251      * @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.
58252      */
58253     autoExpandColumn : false,
58254
58255     /**
58256     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58257     * Default is 50.
58258     */
58259     autoExpandMin : 50,
58260
58261     /**
58262     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58263     */
58264     autoExpandMax : 1000,
58265
58266     /**
58267     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58268     */
58269     view : null,
58270
58271     /**
58272     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58273     */
58274     loadMask : false,
58275     /**
58276     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58277     */
58278     dropTarget: false,
58279      /**
58280     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58281     */ 
58282     sortColMenu : false,
58283     
58284     // private
58285     rendered : false,
58286
58287     /**
58288     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58289     * of a fixed width. Default is false.
58290     */
58291     /**
58292     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58293     */
58294     
58295     
58296     /**
58297     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58298     * %0 is replaced with the number of selected rows.
58299     */
58300     ddText : "{0} selected row{1}",
58301     
58302     
58303     /**
58304      * Called once after all setup has been completed and the grid is ready to be rendered.
58305      * @return {Roo.grid.Grid} this
58306      */
58307     render : function()
58308     {
58309         var c = this.container;
58310         // try to detect autoHeight/width mode
58311         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58312             this.autoHeight = true;
58313         }
58314         var view = this.getView();
58315         view.init(this);
58316
58317         c.on("click", this.onClick, this);
58318         c.on("dblclick", this.onDblClick, this);
58319         c.on("contextmenu", this.onContextMenu, this);
58320         c.on("keydown", this.onKeyDown, this);
58321         if (Roo.isTouch) {
58322             c.on("touchstart", this.onTouchStart, this);
58323         }
58324
58325         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58326
58327         this.getSelectionModel().init(this);
58328
58329         view.render();
58330
58331         if(this.loadMask){
58332             this.loadMask = new Roo.LoadMask(this.container,
58333                     Roo.apply({store:this.dataSource}, this.loadMask));
58334         }
58335         
58336         
58337         if (this.toolbar && this.toolbar.xtype) {
58338             this.toolbar.container = this.getView().getHeaderPanel(true);
58339             this.toolbar = new Roo.Toolbar(this.toolbar);
58340         }
58341         if (this.footer && this.footer.xtype) {
58342             this.footer.dataSource = this.getDataSource();
58343             this.footer.container = this.getView().getFooterPanel(true);
58344             this.footer = Roo.factory(this.footer, Roo);
58345         }
58346         if (this.dropTarget && this.dropTarget.xtype) {
58347             delete this.dropTarget.xtype;
58348             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58349         }
58350         
58351         
58352         this.rendered = true;
58353         this.fireEvent('render', this);
58354         return this;
58355     },
58356
58357     /**
58358      * Reconfigures the grid to use a different Store and Column Model.
58359      * The View will be bound to the new objects and refreshed.
58360      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58361      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58362      */
58363     reconfigure : function(dataSource, colModel){
58364         if(this.loadMask){
58365             this.loadMask.destroy();
58366             this.loadMask = new Roo.LoadMask(this.container,
58367                     Roo.apply({store:dataSource}, this.loadMask));
58368         }
58369         this.view.bind(dataSource, colModel);
58370         this.dataSource = dataSource;
58371         this.colModel = colModel;
58372         this.view.refresh(true);
58373     },
58374     /**
58375      * addColumns
58376      * Add's a column, default at the end..
58377      
58378      * @param {int} position to add (default end)
58379      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58380      */
58381     addColumns : function(pos, ar)
58382     {
58383         
58384         for (var i =0;i< ar.length;i++) {
58385             var cfg = ar[i];
58386             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58387             this.cm.lookup[cfg.id] = cfg;
58388         }
58389         
58390         
58391         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58392             pos = this.cm.config.length; //this.cm.config.push(cfg);
58393         } 
58394         pos = Math.max(0,pos);
58395         ar.unshift(0);
58396         ar.unshift(pos);
58397         this.cm.config.splice.apply(this.cm.config, ar);
58398         
58399         
58400         
58401         this.view.generateRules(this.cm);
58402         this.view.refresh(true);
58403         
58404     },
58405     
58406     
58407     
58408     
58409     // private
58410     onKeyDown : function(e){
58411         this.fireEvent("keydown", e);
58412     },
58413
58414     /**
58415      * Destroy this grid.
58416      * @param {Boolean} removeEl True to remove the element
58417      */
58418     destroy : function(removeEl, keepListeners){
58419         if(this.loadMask){
58420             this.loadMask.destroy();
58421         }
58422         var c = this.container;
58423         c.removeAllListeners();
58424         this.view.destroy();
58425         this.colModel.purgeListeners();
58426         if(!keepListeners){
58427             this.purgeListeners();
58428         }
58429         c.update("");
58430         if(removeEl === true){
58431             c.remove();
58432         }
58433     },
58434
58435     // private
58436     processEvent : function(name, e){
58437         // does this fire select???
58438         //Roo.log('grid:processEvent '  + name);
58439         
58440         if (name != 'touchstart' ) {
58441             this.fireEvent(name, e);    
58442         }
58443         
58444         var t = e.getTarget();
58445         var v = this.view;
58446         var header = v.findHeaderIndex(t);
58447         if(header !== false){
58448             var ename = name == 'touchstart' ? 'click' : name;
58449              
58450             this.fireEvent("header" + ename, this, header, e);
58451         }else{
58452             var row = v.findRowIndex(t);
58453             var cell = v.findCellIndex(t);
58454             if (name == 'touchstart') {
58455                 // first touch is always a click.
58456                 // hopefull this happens after selection is updated.?
58457                 name = false;
58458                 
58459                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58460                     var cs = this.selModel.getSelectedCell();
58461                     if (row == cs[0] && cell == cs[1]){
58462                         name = 'dblclick';
58463                     }
58464                 }
58465                 if (typeof(this.selModel.getSelections) != 'undefined') {
58466                     var cs = this.selModel.getSelections();
58467                     var ds = this.dataSource;
58468                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58469                         name = 'dblclick';
58470                     }
58471                 }
58472                 if (!name) {
58473                     return;
58474                 }
58475             }
58476             
58477             
58478             if(row !== false){
58479                 this.fireEvent("row" + name, this, row, e);
58480                 if(cell !== false){
58481                     this.fireEvent("cell" + name, this, row, cell, e);
58482                 }
58483             }
58484         }
58485     },
58486
58487     // private
58488     onClick : function(e){
58489         this.processEvent("click", e);
58490     },
58491    // private
58492     onTouchStart : function(e){
58493         this.processEvent("touchstart", e);
58494     },
58495
58496     // private
58497     onContextMenu : function(e, t){
58498         this.processEvent("contextmenu", e);
58499     },
58500
58501     // private
58502     onDblClick : function(e){
58503         this.processEvent("dblclick", e);
58504     },
58505
58506     // private
58507     walkCells : function(row, col, step, fn, scope){
58508         var cm = this.colModel, clen = cm.getColumnCount();
58509         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58510         if(step < 0){
58511             if(col < 0){
58512                 row--;
58513                 first = false;
58514             }
58515             while(row >= 0){
58516                 if(!first){
58517                     col = clen-1;
58518                 }
58519                 first = false;
58520                 while(col >= 0){
58521                     if(fn.call(scope || this, row, col, cm) === true){
58522                         return [row, col];
58523                     }
58524                     col--;
58525                 }
58526                 row--;
58527             }
58528         } else {
58529             if(col >= clen){
58530                 row++;
58531                 first = false;
58532             }
58533             while(row < rlen){
58534                 if(!first){
58535                     col = 0;
58536                 }
58537                 first = false;
58538                 while(col < clen){
58539                     if(fn.call(scope || this, row, col, cm) === true){
58540                         return [row, col];
58541                     }
58542                     col++;
58543                 }
58544                 row++;
58545             }
58546         }
58547         return null;
58548     },
58549
58550     // private
58551     getSelections : function(){
58552         return this.selModel.getSelections();
58553     },
58554
58555     /**
58556      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58557      * but if manual update is required this method will initiate it.
58558      */
58559     autoSize : function(){
58560         if(this.rendered){
58561             this.view.layout();
58562             if(this.view.adjustForScroll){
58563                 this.view.adjustForScroll();
58564             }
58565         }
58566     },
58567
58568     /**
58569      * Returns the grid's underlying element.
58570      * @return {Element} The element
58571      */
58572     getGridEl : function(){
58573         return this.container;
58574     },
58575
58576     // private for compatibility, overridden by editor grid
58577     stopEditing : function(){},
58578
58579     /**
58580      * Returns the grid's SelectionModel.
58581      * @return {SelectionModel}
58582      */
58583     getSelectionModel : function(){
58584         if(!this.selModel){
58585             this.selModel = new Roo.grid.RowSelectionModel();
58586         }
58587         return this.selModel;
58588     },
58589
58590     /**
58591      * Returns the grid's DataSource.
58592      * @return {DataSource}
58593      */
58594     getDataSource : function(){
58595         return this.dataSource;
58596     },
58597
58598     /**
58599      * Returns the grid's ColumnModel.
58600      * @return {ColumnModel}
58601      */
58602     getColumnModel : function(){
58603         return this.colModel;
58604     },
58605
58606     /**
58607      * Returns the grid's GridView object.
58608      * @return {GridView}
58609      */
58610     getView : function(){
58611         if(!this.view){
58612             this.view = new Roo.grid.GridView(this.viewConfig);
58613             this.relayEvents(this.view, [
58614                 "beforerowremoved", "beforerowsinserted",
58615                 "beforerefresh", "rowremoved",
58616                 "rowsinserted", "rowupdated" ,"refresh"
58617             ]);
58618         }
58619         return this.view;
58620     },
58621     /**
58622      * Called to get grid's drag proxy text, by default returns this.ddText.
58623      * Override this to put something different in the dragged text.
58624      * @return {String}
58625      */
58626     getDragDropText : function(){
58627         var count = this.selModel.getCount();
58628         return String.format(this.ddText, count, count == 1 ? '' : 's');
58629     }
58630 });
58631 /*
58632  * Based on:
58633  * Ext JS Library 1.1.1
58634  * Copyright(c) 2006-2007, Ext JS, LLC.
58635  *
58636  * Originally Released Under LGPL - original licence link has changed is not relivant.
58637  *
58638  * Fork - LGPL
58639  * <script type="text/javascript">
58640  */
58641  /**
58642  * @class Roo.grid.AbstractGridView
58643  * @extends Roo.util.Observable
58644  * @abstract
58645  * Abstract base class for grid Views
58646  * @constructor
58647  */
58648 Roo.grid.AbstractGridView = function(){
58649         this.grid = null;
58650         
58651         this.events = {
58652             "beforerowremoved" : true,
58653             "beforerowsinserted" : true,
58654             "beforerefresh" : true,
58655             "rowremoved" : true,
58656             "rowsinserted" : true,
58657             "rowupdated" : true,
58658             "refresh" : true
58659         };
58660     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58661 };
58662
58663 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58664     rowClass : "x-grid-row",
58665     cellClass : "x-grid-cell",
58666     tdClass : "x-grid-td",
58667     hdClass : "x-grid-hd",
58668     splitClass : "x-grid-hd-split",
58669     
58670     init: function(grid){
58671         this.grid = grid;
58672                 var cid = this.grid.getGridEl().id;
58673         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58674         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58675         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58676         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58677         },
58678         
58679     getColumnRenderers : function(){
58680         var renderers = [];
58681         var cm = this.grid.colModel;
58682         var colCount = cm.getColumnCount();
58683         for(var i = 0; i < colCount; i++){
58684             renderers[i] = cm.getRenderer(i);
58685         }
58686         return renderers;
58687     },
58688     
58689     getColumnIds : function(){
58690         var ids = [];
58691         var cm = this.grid.colModel;
58692         var colCount = cm.getColumnCount();
58693         for(var i = 0; i < colCount; i++){
58694             ids[i] = cm.getColumnId(i);
58695         }
58696         return ids;
58697     },
58698     
58699     getDataIndexes : function(){
58700         if(!this.indexMap){
58701             this.indexMap = this.buildIndexMap();
58702         }
58703         return this.indexMap.colToData;
58704     },
58705     
58706     getColumnIndexByDataIndex : function(dataIndex){
58707         if(!this.indexMap){
58708             this.indexMap = this.buildIndexMap();
58709         }
58710         return this.indexMap.dataToCol[dataIndex];
58711     },
58712     
58713     /**
58714      * Set a css style for a column dynamically. 
58715      * @param {Number} colIndex The index of the column
58716      * @param {String} name The css property name
58717      * @param {String} value The css value
58718      */
58719     setCSSStyle : function(colIndex, name, value){
58720         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58721         Roo.util.CSS.updateRule(selector, name, value);
58722     },
58723     
58724     generateRules : function(cm){
58725         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58726         Roo.util.CSS.removeStyleSheet(rulesId);
58727         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58728             var cid = cm.getColumnId(i);
58729             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58730                          this.tdSelector, cid, " {\n}\n",
58731                          this.hdSelector, cid, " {\n}\n",
58732                          this.splitSelector, cid, " {\n}\n");
58733         }
58734         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58735     }
58736 });/*
58737  * Based on:
58738  * Ext JS Library 1.1.1
58739  * Copyright(c) 2006-2007, Ext JS, LLC.
58740  *
58741  * Originally Released Under LGPL - original licence link has changed is not relivant.
58742  *
58743  * Fork - LGPL
58744  * <script type="text/javascript">
58745  */
58746
58747 // private
58748 // This is a support class used internally by the Grid components
58749 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58750     this.grid = grid;
58751     this.view = grid.getView();
58752     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58753     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58754     if(hd2){
58755         this.setHandleElId(Roo.id(hd));
58756         this.setOuterHandleElId(Roo.id(hd2));
58757     }
58758     this.scroll = false;
58759 };
58760 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58761     maxDragWidth: 120,
58762     getDragData : function(e){
58763         var t = Roo.lib.Event.getTarget(e);
58764         var h = this.view.findHeaderCell(t);
58765         if(h){
58766             return {ddel: h.firstChild, header:h};
58767         }
58768         return false;
58769     },
58770
58771     onInitDrag : function(e){
58772         this.view.headersDisabled = true;
58773         var clone = this.dragData.ddel.cloneNode(true);
58774         clone.id = Roo.id();
58775         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58776         this.proxy.update(clone);
58777         return true;
58778     },
58779
58780     afterValidDrop : function(){
58781         var v = this.view;
58782         setTimeout(function(){
58783             v.headersDisabled = false;
58784         }, 50);
58785     },
58786
58787     afterInvalidDrop : function(){
58788         var v = this.view;
58789         setTimeout(function(){
58790             v.headersDisabled = false;
58791         }, 50);
58792     }
58793 });
58794 /*
58795  * Based on:
58796  * Ext JS Library 1.1.1
58797  * Copyright(c) 2006-2007, Ext JS, LLC.
58798  *
58799  * Originally Released Under LGPL - original licence link has changed is not relivant.
58800  *
58801  * Fork - LGPL
58802  * <script type="text/javascript">
58803  */
58804 // private
58805 // This is a support class used internally by the Grid components
58806 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58807     this.grid = grid;
58808     this.view = grid.getView();
58809     // split the proxies so they don't interfere with mouse events
58810     this.proxyTop = Roo.DomHelper.append(document.body, {
58811         cls:"col-move-top", html:"&#160;"
58812     }, true);
58813     this.proxyBottom = Roo.DomHelper.append(document.body, {
58814         cls:"col-move-bottom", html:"&#160;"
58815     }, true);
58816     this.proxyTop.hide = this.proxyBottom.hide = function(){
58817         this.setLeftTop(-100,-100);
58818         this.setStyle("visibility", "hidden");
58819     };
58820     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58821     // temporarily disabled
58822     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58823     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58824 };
58825 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58826     proxyOffsets : [-4, -9],
58827     fly: Roo.Element.fly,
58828
58829     getTargetFromEvent : function(e){
58830         var t = Roo.lib.Event.getTarget(e);
58831         var cindex = this.view.findCellIndex(t);
58832         if(cindex !== false){
58833             return this.view.getHeaderCell(cindex);
58834         }
58835         return null;
58836     },
58837
58838     nextVisible : function(h){
58839         var v = this.view, cm = this.grid.colModel;
58840         h = h.nextSibling;
58841         while(h){
58842             if(!cm.isHidden(v.getCellIndex(h))){
58843                 return h;
58844             }
58845             h = h.nextSibling;
58846         }
58847         return null;
58848     },
58849
58850     prevVisible : function(h){
58851         var v = this.view, cm = this.grid.colModel;
58852         h = h.prevSibling;
58853         while(h){
58854             if(!cm.isHidden(v.getCellIndex(h))){
58855                 return h;
58856             }
58857             h = h.prevSibling;
58858         }
58859         return null;
58860     },
58861
58862     positionIndicator : function(h, n, e){
58863         var x = Roo.lib.Event.getPageX(e);
58864         var r = Roo.lib.Dom.getRegion(n.firstChild);
58865         var px, pt, py = r.top + this.proxyOffsets[1];
58866         if((r.right - x) <= (r.right-r.left)/2){
58867             px = r.right+this.view.borderWidth;
58868             pt = "after";
58869         }else{
58870             px = r.left;
58871             pt = "before";
58872         }
58873         var oldIndex = this.view.getCellIndex(h);
58874         var newIndex = this.view.getCellIndex(n);
58875
58876         if(this.grid.colModel.isFixed(newIndex)){
58877             return false;
58878         }
58879
58880         var locked = this.grid.colModel.isLocked(newIndex);
58881
58882         if(pt == "after"){
58883             newIndex++;
58884         }
58885         if(oldIndex < newIndex){
58886             newIndex--;
58887         }
58888         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58889             return false;
58890         }
58891         px +=  this.proxyOffsets[0];
58892         this.proxyTop.setLeftTop(px, py);
58893         this.proxyTop.show();
58894         if(!this.bottomOffset){
58895             this.bottomOffset = this.view.mainHd.getHeight();
58896         }
58897         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58898         this.proxyBottom.show();
58899         return pt;
58900     },
58901
58902     onNodeEnter : function(n, dd, e, data){
58903         if(data.header != n){
58904             this.positionIndicator(data.header, n, e);
58905         }
58906     },
58907
58908     onNodeOver : function(n, dd, e, data){
58909         var result = false;
58910         if(data.header != n){
58911             result = this.positionIndicator(data.header, n, e);
58912         }
58913         if(!result){
58914             this.proxyTop.hide();
58915             this.proxyBottom.hide();
58916         }
58917         return result ? this.dropAllowed : this.dropNotAllowed;
58918     },
58919
58920     onNodeOut : function(n, dd, e, data){
58921         this.proxyTop.hide();
58922         this.proxyBottom.hide();
58923     },
58924
58925     onNodeDrop : function(n, dd, e, data){
58926         var h = data.header;
58927         if(h != n){
58928             var cm = this.grid.colModel;
58929             var x = Roo.lib.Event.getPageX(e);
58930             var r = Roo.lib.Dom.getRegion(n.firstChild);
58931             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58932             var oldIndex = this.view.getCellIndex(h);
58933             var newIndex = this.view.getCellIndex(n);
58934             var locked = cm.isLocked(newIndex);
58935             if(pt == "after"){
58936                 newIndex++;
58937             }
58938             if(oldIndex < newIndex){
58939                 newIndex--;
58940             }
58941             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58942                 return false;
58943             }
58944             cm.setLocked(oldIndex, locked, true);
58945             cm.moveColumn(oldIndex, newIndex);
58946             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58947             return true;
58948         }
58949         return false;
58950     }
58951 });
58952 /*
58953  * Based on:
58954  * Ext JS Library 1.1.1
58955  * Copyright(c) 2006-2007, Ext JS, LLC.
58956  *
58957  * Originally Released Under LGPL - original licence link has changed is not relivant.
58958  *
58959  * Fork - LGPL
58960  * <script type="text/javascript">
58961  */
58962   
58963 /**
58964  * @class Roo.grid.GridView
58965  * @extends Roo.util.Observable
58966  *
58967  * @constructor
58968  * @param {Object} config
58969  */
58970 Roo.grid.GridView = function(config){
58971     Roo.grid.GridView.superclass.constructor.call(this);
58972     this.el = null;
58973
58974     Roo.apply(this, config);
58975 };
58976
58977 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58978
58979     unselectable :  'unselectable="on"',
58980     unselectableCls :  'x-unselectable',
58981     
58982     
58983     rowClass : "x-grid-row",
58984
58985     cellClass : "x-grid-col",
58986
58987     tdClass : "x-grid-td",
58988
58989     hdClass : "x-grid-hd",
58990
58991     splitClass : "x-grid-split",
58992
58993     sortClasses : ["sort-asc", "sort-desc"],
58994
58995     enableMoveAnim : false,
58996
58997     hlColor: "C3DAF9",
58998
58999     dh : Roo.DomHelper,
59000
59001     fly : Roo.Element.fly,
59002
59003     css : Roo.util.CSS,
59004
59005     borderWidth: 1,
59006
59007     splitOffset: 3,
59008
59009     scrollIncrement : 22,
59010
59011     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59012
59013     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59014
59015     bind : function(ds, cm){
59016         if(this.ds){
59017             this.ds.un("load", this.onLoad, this);
59018             this.ds.un("datachanged", this.onDataChange, this);
59019             this.ds.un("add", this.onAdd, this);
59020             this.ds.un("remove", this.onRemove, this);
59021             this.ds.un("update", this.onUpdate, this);
59022             this.ds.un("clear", this.onClear, this);
59023         }
59024         if(ds){
59025             ds.on("load", this.onLoad, this);
59026             ds.on("datachanged", this.onDataChange, this);
59027             ds.on("add", this.onAdd, this);
59028             ds.on("remove", this.onRemove, this);
59029             ds.on("update", this.onUpdate, this);
59030             ds.on("clear", this.onClear, this);
59031         }
59032         this.ds = ds;
59033
59034         if(this.cm){
59035             this.cm.un("widthchange", this.onColWidthChange, this);
59036             this.cm.un("headerchange", this.onHeaderChange, this);
59037             this.cm.un("hiddenchange", this.onHiddenChange, this);
59038             this.cm.un("columnmoved", this.onColumnMove, this);
59039             this.cm.un("columnlockchange", this.onColumnLock, this);
59040         }
59041         if(cm){
59042             this.generateRules(cm);
59043             cm.on("widthchange", this.onColWidthChange, this);
59044             cm.on("headerchange", this.onHeaderChange, this);
59045             cm.on("hiddenchange", this.onHiddenChange, this);
59046             cm.on("columnmoved", this.onColumnMove, this);
59047             cm.on("columnlockchange", this.onColumnLock, this);
59048         }
59049         this.cm = cm;
59050     },
59051
59052     init: function(grid){
59053         Roo.grid.GridView.superclass.init.call(this, grid);
59054
59055         this.bind(grid.dataSource, grid.colModel);
59056
59057         grid.on("headerclick", this.handleHeaderClick, this);
59058
59059         if(grid.trackMouseOver){
59060             grid.on("mouseover", this.onRowOver, this);
59061             grid.on("mouseout", this.onRowOut, this);
59062         }
59063         grid.cancelTextSelection = function(){};
59064         this.gridId = grid.id;
59065
59066         var tpls = this.templates || {};
59067
59068         if(!tpls.master){
59069             tpls.master = new Roo.Template(
59070                '<div class="x-grid" hidefocus="true">',
59071                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59072                   '<div class="x-grid-topbar"></div>',
59073                   '<div class="x-grid-scroller"><div></div></div>',
59074                   '<div class="x-grid-locked">',
59075                       '<div class="x-grid-header">{lockedHeader}</div>',
59076                       '<div class="x-grid-body">{lockedBody}</div>',
59077                   "</div>",
59078                   '<div class="x-grid-viewport">',
59079                       '<div class="x-grid-header">{header}</div>',
59080                       '<div class="x-grid-body">{body}</div>',
59081                   "</div>",
59082                   '<div class="x-grid-bottombar"></div>',
59083                  
59084                   '<div class="x-grid-resize-proxy">&#160;</div>',
59085                "</div>"
59086             );
59087             tpls.master.disableformats = true;
59088         }
59089
59090         if(!tpls.header){
59091             tpls.header = new Roo.Template(
59092                '<table border="0" cellspacing="0" cellpadding="0">',
59093                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59094                "</table>{splits}"
59095             );
59096             tpls.header.disableformats = true;
59097         }
59098         tpls.header.compile();
59099
59100         if(!tpls.hcell){
59101             tpls.hcell = new Roo.Template(
59102                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59103                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59104                 "</div></td>"
59105              );
59106              tpls.hcell.disableFormats = true;
59107         }
59108         tpls.hcell.compile();
59109
59110         if(!tpls.hsplit){
59111             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59112                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59113             tpls.hsplit.disableFormats = true;
59114         }
59115         tpls.hsplit.compile();
59116
59117         if(!tpls.body){
59118             tpls.body = new Roo.Template(
59119                '<table border="0" cellspacing="0" cellpadding="0">',
59120                "<tbody>{rows}</tbody>",
59121                "</table>"
59122             );
59123             tpls.body.disableFormats = true;
59124         }
59125         tpls.body.compile();
59126
59127         if(!tpls.row){
59128             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59129             tpls.row.disableFormats = true;
59130         }
59131         tpls.row.compile();
59132
59133         if(!tpls.cell){
59134             tpls.cell = new Roo.Template(
59135                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59136                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59137                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59138                 "</td>"
59139             );
59140             tpls.cell.disableFormats = true;
59141         }
59142         tpls.cell.compile();
59143
59144         this.templates = tpls;
59145     },
59146
59147     // remap these for backwards compat
59148     onColWidthChange : function(){
59149         this.updateColumns.apply(this, arguments);
59150     },
59151     onHeaderChange : function(){
59152         this.updateHeaders.apply(this, arguments);
59153     }, 
59154     onHiddenChange : function(){
59155         this.handleHiddenChange.apply(this, arguments);
59156     },
59157     onColumnMove : function(){
59158         this.handleColumnMove.apply(this, arguments);
59159     },
59160     onColumnLock : function(){
59161         this.handleLockChange.apply(this, arguments);
59162     },
59163
59164     onDataChange : function(){
59165         this.refresh();
59166         this.updateHeaderSortState();
59167     },
59168
59169     onClear : function(){
59170         this.refresh();
59171     },
59172
59173     onUpdate : function(ds, record){
59174         this.refreshRow(record);
59175     },
59176
59177     refreshRow : function(record){
59178         var ds = this.ds, index;
59179         if(typeof record == 'number'){
59180             index = record;
59181             record = ds.getAt(index);
59182         }else{
59183             index = ds.indexOf(record);
59184         }
59185         this.insertRows(ds, index, index, true);
59186         this.onRemove(ds, record, index+1, true);
59187         this.syncRowHeights(index, index);
59188         this.layout();
59189         this.fireEvent("rowupdated", this, index, record);
59190     },
59191
59192     onAdd : function(ds, records, index){
59193         this.insertRows(ds, index, index + (records.length-1));
59194     },
59195
59196     onRemove : function(ds, record, index, isUpdate){
59197         if(isUpdate !== true){
59198             this.fireEvent("beforerowremoved", this, index, record);
59199         }
59200         var bt = this.getBodyTable(), lt = this.getLockedTable();
59201         if(bt.rows[index]){
59202             bt.firstChild.removeChild(bt.rows[index]);
59203         }
59204         if(lt.rows[index]){
59205             lt.firstChild.removeChild(lt.rows[index]);
59206         }
59207         if(isUpdate !== true){
59208             this.stripeRows(index);
59209             this.syncRowHeights(index, index);
59210             this.layout();
59211             this.fireEvent("rowremoved", this, index, record);
59212         }
59213     },
59214
59215     onLoad : function(){
59216         this.scrollToTop();
59217     },
59218
59219     /**
59220      * Scrolls the grid to the top
59221      */
59222     scrollToTop : function(){
59223         if(this.scroller){
59224             this.scroller.dom.scrollTop = 0;
59225             this.syncScroll();
59226         }
59227     },
59228
59229     /**
59230      * Gets a panel in the header of the grid that can be used for toolbars etc.
59231      * After modifying the contents of this panel a call to grid.autoSize() may be
59232      * required to register any changes in size.
59233      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59234      * @return Roo.Element
59235      */
59236     getHeaderPanel : function(doShow){
59237         if(doShow){
59238             this.headerPanel.show();
59239         }
59240         return this.headerPanel;
59241     },
59242
59243     /**
59244      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59245      * After modifying the contents of this panel a call to grid.autoSize() may be
59246      * required to register any changes in size.
59247      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59248      * @return Roo.Element
59249      */
59250     getFooterPanel : function(doShow){
59251         if(doShow){
59252             this.footerPanel.show();
59253         }
59254         return this.footerPanel;
59255     },
59256
59257     initElements : function(){
59258         var E = Roo.Element;
59259         var el = this.grid.getGridEl().dom.firstChild;
59260         var cs = el.childNodes;
59261
59262         this.el = new E(el);
59263         
59264          this.focusEl = new E(el.firstChild);
59265         this.focusEl.swallowEvent("click", true);
59266         
59267         this.headerPanel = new E(cs[1]);
59268         this.headerPanel.enableDisplayMode("block");
59269
59270         this.scroller = new E(cs[2]);
59271         this.scrollSizer = new E(this.scroller.dom.firstChild);
59272
59273         this.lockedWrap = new E(cs[3]);
59274         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59275         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59276
59277         this.mainWrap = new E(cs[4]);
59278         this.mainHd = new E(this.mainWrap.dom.firstChild);
59279         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59280
59281         this.footerPanel = new E(cs[5]);
59282         this.footerPanel.enableDisplayMode("block");
59283
59284         this.resizeProxy = new E(cs[6]);
59285
59286         this.headerSelector = String.format(
59287            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59288            this.lockedHd.id, this.mainHd.id
59289         );
59290
59291         this.splitterSelector = String.format(
59292            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59293            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59294         );
59295     },
59296     idToCssName : function(s)
59297     {
59298         return s.replace(/[^a-z0-9]+/ig, '-');
59299     },
59300
59301     getHeaderCell : function(index){
59302         return Roo.DomQuery.select(this.headerSelector)[index];
59303     },
59304
59305     getHeaderCellMeasure : function(index){
59306         return this.getHeaderCell(index).firstChild;
59307     },
59308
59309     getHeaderCellText : function(index){
59310         return this.getHeaderCell(index).firstChild.firstChild;
59311     },
59312
59313     getLockedTable : function(){
59314         return this.lockedBody.dom.firstChild;
59315     },
59316
59317     getBodyTable : function(){
59318         return this.mainBody.dom.firstChild;
59319     },
59320
59321     getLockedRow : function(index){
59322         return this.getLockedTable().rows[index];
59323     },
59324
59325     getRow : function(index){
59326         return this.getBodyTable().rows[index];
59327     },
59328
59329     getRowComposite : function(index){
59330         if(!this.rowEl){
59331             this.rowEl = new Roo.CompositeElementLite();
59332         }
59333         var els = [], lrow, mrow;
59334         if(lrow = this.getLockedRow(index)){
59335             els.push(lrow);
59336         }
59337         if(mrow = this.getRow(index)){
59338             els.push(mrow);
59339         }
59340         this.rowEl.elements = els;
59341         return this.rowEl;
59342     },
59343     /**
59344      * Gets the 'td' of the cell
59345      * 
59346      * @param {Integer} rowIndex row to select
59347      * @param {Integer} colIndex column to select
59348      * 
59349      * @return {Object} 
59350      */
59351     getCell : function(rowIndex, colIndex){
59352         var locked = this.cm.getLockedCount();
59353         var source;
59354         if(colIndex < locked){
59355             source = this.lockedBody.dom.firstChild;
59356         }else{
59357             source = this.mainBody.dom.firstChild;
59358             colIndex -= locked;
59359         }
59360         return source.rows[rowIndex].childNodes[colIndex];
59361     },
59362
59363     getCellText : function(rowIndex, colIndex){
59364         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59365     },
59366
59367     getCellBox : function(cell){
59368         var b = this.fly(cell).getBox();
59369         if(Roo.isOpera){ // opera fails to report the Y
59370             b.y = cell.offsetTop + this.mainBody.getY();
59371         }
59372         return b;
59373     },
59374
59375     getCellIndex : function(cell){
59376         var id = String(cell.className).match(this.cellRE);
59377         if(id){
59378             return parseInt(id[1], 10);
59379         }
59380         return 0;
59381     },
59382
59383     findHeaderIndex : function(n){
59384         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59385         return r ? this.getCellIndex(r) : false;
59386     },
59387
59388     findHeaderCell : function(n){
59389         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59390         return r ? r : false;
59391     },
59392
59393     findRowIndex : function(n){
59394         if(!n){
59395             return false;
59396         }
59397         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59398         return r ? r.rowIndex : false;
59399     },
59400
59401     findCellIndex : function(node){
59402         var stop = this.el.dom;
59403         while(node && node != stop){
59404             if(this.findRE.test(node.className)){
59405                 return this.getCellIndex(node);
59406             }
59407             node = node.parentNode;
59408         }
59409         return false;
59410     },
59411
59412     getColumnId : function(index){
59413         return this.cm.getColumnId(index);
59414     },
59415
59416     getSplitters : function()
59417     {
59418         if(this.splitterSelector){
59419            return Roo.DomQuery.select(this.splitterSelector);
59420         }else{
59421             return null;
59422       }
59423     },
59424
59425     getSplitter : function(index){
59426         return this.getSplitters()[index];
59427     },
59428
59429     onRowOver : function(e, t){
59430         var row;
59431         if((row = this.findRowIndex(t)) !== false){
59432             this.getRowComposite(row).addClass("x-grid-row-over");
59433         }
59434     },
59435
59436     onRowOut : function(e, t){
59437         var row;
59438         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59439             this.getRowComposite(row).removeClass("x-grid-row-over");
59440         }
59441     },
59442
59443     renderHeaders : function(){
59444         var cm = this.cm;
59445         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59446         var cb = [], lb = [], sb = [], lsb = [], p = {};
59447         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59448             p.cellId = "x-grid-hd-0-" + i;
59449             p.splitId = "x-grid-csplit-0-" + i;
59450             p.id = cm.getColumnId(i);
59451             p.value = cm.getColumnHeader(i) || "";
59452             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59453             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59454             if(!cm.isLocked(i)){
59455                 cb[cb.length] = ct.apply(p);
59456                 sb[sb.length] = st.apply(p);
59457             }else{
59458                 lb[lb.length] = ct.apply(p);
59459                 lsb[lsb.length] = st.apply(p);
59460             }
59461         }
59462         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59463                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59464     },
59465
59466     updateHeaders : function(){
59467         var html = this.renderHeaders();
59468         this.lockedHd.update(html[0]);
59469         this.mainHd.update(html[1]);
59470     },
59471
59472     /**
59473      * Focuses the specified row.
59474      * @param {Number} row The row index
59475      */
59476     focusRow : function(row)
59477     {
59478         //Roo.log('GridView.focusRow');
59479         var x = this.scroller.dom.scrollLeft;
59480         this.focusCell(row, 0, false);
59481         this.scroller.dom.scrollLeft = x;
59482     },
59483
59484     /**
59485      * Focuses the specified cell.
59486      * @param {Number} row The row index
59487      * @param {Number} col The column index
59488      * @param {Boolean} hscroll false to disable horizontal scrolling
59489      */
59490     focusCell : function(row, col, hscroll)
59491     {
59492         //Roo.log('GridView.focusCell');
59493         var el = this.ensureVisible(row, col, hscroll);
59494         this.focusEl.alignTo(el, "tl-tl");
59495         if(Roo.isGecko){
59496             this.focusEl.focus();
59497         }else{
59498             this.focusEl.focus.defer(1, this.focusEl);
59499         }
59500     },
59501
59502     /**
59503      * Scrolls the specified cell into view
59504      * @param {Number} row The row index
59505      * @param {Number} col The column index
59506      * @param {Boolean} hscroll false to disable horizontal scrolling
59507      */
59508     ensureVisible : function(row, col, hscroll)
59509     {
59510         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59511         //return null; //disable for testing.
59512         if(typeof row != "number"){
59513             row = row.rowIndex;
59514         }
59515         if(row < 0 && row >= this.ds.getCount()){
59516             return  null;
59517         }
59518         col = (col !== undefined ? col : 0);
59519         var cm = this.grid.colModel;
59520         while(cm.isHidden(col)){
59521             col++;
59522         }
59523
59524         var el = this.getCell(row, col);
59525         if(!el){
59526             return null;
59527         }
59528         var c = this.scroller.dom;
59529
59530         var ctop = parseInt(el.offsetTop, 10);
59531         var cleft = parseInt(el.offsetLeft, 10);
59532         var cbot = ctop + el.offsetHeight;
59533         var cright = cleft + el.offsetWidth;
59534         
59535         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59536         var stop = parseInt(c.scrollTop, 10);
59537         var sleft = parseInt(c.scrollLeft, 10);
59538         var sbot = stop + ch;
59539         var sright = sleft + c.clientWidth;
59540         /*
59541         Roo.log('GridView.ensureVisible:' +
59542                 ' ctop:' + ctop +
59543                 ' c.clientHeight:' + c.clientHeight +
59544                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59545                 ' stop:' + stop +
59546                 ' cbot:' + cbot +
59547                 ' sbot:' + sbot +
59548                 ' ch:' + ch  
59549                 );
59550         */
59551         if(ctop < stop){
59552             c.scrollTop = ctop;
59553             //Roo.log("set scrolltop to ctop DISABLE?");
59554         }else if(cbot > sbot){
59555             //Roo.log("set scrolltop to cbot-ch");
59556             c.scrollTop = cbot-ch;
59557         }
59558         
59559         if(hscroll !== false){
59560             if(cleft < sleft){
59561                 c.scrollLeft = cleft;
59562             }else if(cright > sright){
59563                 c.scrollLeft = cright-c.clientWidth;
59564             }
59565         }
59566          
59567         return el;
59568     },
59569
59570     updateColumns : function(){
59571         this.grid.stopEditing();
59572         var cm = this.grid.colModel, colIds = this.getColumnIds();
59573         //var totalWidth = cm.getTotalWidth();
59574         var pos = 0;
59575         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59576             //if(cm.isHidden(i)) continue;
59577             var w = cm.getColumnWidth(i);
59578             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59579             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59580         }
59581         this.updateSplitters();
59582     },
59583
59584     generateRules : function(cm){
59585         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59586         Roo.util.CSS.removeStyleSheet(rulesId);
59587         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59588             var cid = cm.getColumnId(i);
59589             var align = '';
59590             if(cm.config[i].align){
59591                 align = 'text-align:'+cm.config[i].align+';';
59592             }
59593             var hidden = '';
59594             if(cm.isHidden(i)){
59595                 hidden = 'display:none;';
59596             }
59597             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59598             ruleBuf.push(
59599                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59600                     this.hdSelector, cid, " {\n", align, width, "}\n",
59601                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59602                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59603         }
59604         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59605     },
59606
59607     updateSplitters : function(){
59608         var cm = this.cm, s = this.getSplitters();
59609         if(s){ // splitters not created yet
59610             var pos = 0, locked = true;
59611             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59612                 if(cm.isHidden(i)) {
59613                     continue;
59614                 }
59615                 var w = cm.getColumnWidth(i); // make sure it's a number
59616                 if(!cm.isLocked(i) && locked){
59617                     pos = 0;
59618                     locked = false;
59619                 }
59620                 pos += w;
59621                 s[i].style.left = (pos-this.splitOffset) + "px";
59622             }
59623         }
59624     },
59625
59626     handleHiddenChange : function(colModel, colIndex, hidden){
59627         if(hidden){
59628             this.hideColumn(colIndex);
59629         }else{
59630             this.unhideColumn(colIndex);
59631         }
59632     },
59633
59634     hideColumn : function(colIndex){
59635         var cid = this.getColumnId(colIndex);
59636         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59637         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59638         if(Roo.isSafari){
59639             this.updateHeaders();
59640         }
59641         this.updateSplitters();
59642         this.layout();
59643     },
59644
59645     unhideColumn : function(colIndex){
59646         var cid = this.getColumnId(colIndex);
59647         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59648         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59649
59650         if(Roo.isSafari){
59651             this.updateHeaders();
59652         }
59653         this.updateSplitters();
59654         this.layout();
59655     },
59656
59657     insertRows : function(dm, firstRow, lastRow, isUpdate){
59658         if(firstRow == 0 && lastRow == dm.getCount()-1){
59659             this.refresh();
59660         }else{
59661             if(!isUpdate){
59662                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59663             }
59664             var s = this.getScrollState();
59665             var markup = this.renderRows(firstRow, lastRow);
59666             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59667             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59668             this.restoreScroll(s);
59669             if(!isUpdate){
59670                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59671                 this.syncRowHeights(firstRow, lastRow);
59672                 this.stripeRows(firstRow);
59673                 this.layout();
59674             }
59675         }
59676     },
59677
59678     bufferRows : function(markup, target, index){
59679         var before = null, trows = target.rows, tbody = target.tBodies[0];
59680         if(index < trows.length){
59681             before = trows[index];
59682         }
59683         var b = document.createElement("div");
59684         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59685         var rows = b.firstChild.rows;
59686         for(var i = 0, len = rows.length; i < len; i++){
59687             if(before){
59688                 tbody.insertBefore(rows[0], before);
59689             }else{
59690                 tbody.appendChild(rows[0]);
59691             }
59692         }
59693         b.innerHTML = "";
59694         b = null;
59695     },
59696
59697     deleteRows : function(dm, firstRow, lastRow){
59698         if(dm.getRowCount()<1){
59699             this.fireEvent("beforerefresh", this);
59700             this.mainBody.update("");
59701             this.lockedBody.update("");
59702             this.fireEvent("refresh", this);
59703         }else{
59704             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59705             var bt = this.getBodyTable();
59706             var tbody = bt.firstChild;
59707             var rows = bt.rows;
59708             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59709                 tbody.removeChild(rows[firstRow]);
59710             }
59711             this.stripeRows(firstRow);
59712             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59713         }
59714     },
59715
59716     updateRows : function(dataSource, firstRow, lastRow){
59717         var s = this.getScrollState();
59718         this.refresh();
59719         this.restoreScroll(s);
59720     },
59721
59722     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59723         if(!noRefresh){
59724            this.refresh();
59725         }
59726         this.updateHeaderSortState();
59727     },
59728
59729     getScrollState : function(){
59730         
59731         var sb = this.scroller.dom;
59732         return {left: sb.scrollLeft, top: sb.scrollTop};
59733     },
59734
59735     stripeRows : function(startRow){
59736         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59737             return;
59738         }
59739         startRow = startRow || 0;
59740         var rows = this.getBodyTable().rows;
59741         var lrows = this.getLockedTable().rows;
59742         var cls = ' x-grid-row-alt ';
59743         for(var i = startRow, len = rows.length; i < len; i++){
59744             var row = rows[i], lrow = lrows[i];
59745             var isAlt = ((i+1) % 2 == 0);
59746             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59747             if(isAlt == hasAlt){
59748                 continue;
59749             }
59750             if(isAlt){
59751                 row.className += " x-grid-row-alt";
59752             }else{
59753                 row.className = row.className.replace("x-grid-row-alt", "");
59754             }
59755             if(lrow){
59756                 lrow.className = row.className;
59757             }
59758         }
59759     },
59760
59761     restoreScroll : function(state){
59762         //Roo.log('GridView.restoreScroll');
59763         var sb = this.scroller.dom;
59764         sb.scrollLeft = state.left;
59765         sb.scrollTop = state.top;
59766         this.syncScroll();
59767     },
59768
59769     syncScroll : function(){
59770         //Roo.log('GridView.syncScroll');
59771         var sb = this.scroller.dom;
59772         var sh = this.mainHd.dom;
59773         var bs = this.mainBody.dom;
59774         var lv = this.lockedBody.dom;
59775         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59776         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59777     },
59778
59779     handleScroll : function(e){
59780         this.syncScroll();
59781         var sb = this.scroller.dom;
59782         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59783         e.stopEvent();
59784     },
59785
59786     handleWheel : function(e){
59787         var d = e.getWheelDelta();
59788         this.scroller.dom.scrollTop -= d*22;
59789         // set this here to prevent jumpy scrolling on large tables
59790         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59791         e.stopEvent();
59792     },
59793
59794     renderRows : function(startRow, endRow){
59795         // pull in all the crap needed to render rows
59796         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59797         var colCount = cm.getColumnCount();
59798
59799         if(ds.getCount() < 1){
59800             return ["", ""];
59801         }
59802
59803         // build a map for all the columns
59804         var cs = [];
59805         for(var i = 0; i < colCount; i++){
59806             var name = cm.getDataIndex(i);
59807             cs[i] = {
59808                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59809                 renderer : cm.getRenderer(i),
59810                 id : cm.getColumnId(i),
59811                 locked : cm.isLocked(i),
59812                 has_editor : cm.isCellEditable(i)
59813             };
59814         }
59815
59816         startRow = startRow || 0;
59817         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59818
59819         // records to render
59820         var rs = ds.getRange(startRow, endRow);
59821
59822         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59823     },
59824
59825     // As much as I hate to duplicate code, this was branched because FireFox really hates
59826     // [].join("") on strings. The performance difference was substantial enough to
59827     // branch this function
59828     doRender : Roo.isGecko ?
59829             function(cs, rs, ds, startRow, colCount, stripe){
59830                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59831                 // buffers
59832                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59833                 
59834                 var hasListener = this.grid.hasListener('rowclass');
59835                 var rowcfg = {};
59836                 for(var j = 0, len = rs.length; j < len; j++){
59837                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59838                     for(var i = 0; i < colCount; i++){
59839                         c = cs[i];
59840                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59841                         p.id = c.id;
59842                         p.css = p.attr = "";
59843                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59844                         if(p.value == undefined || p.value === "") {
59845                             p.value = "&#160;";
59846                         }
59847                         if(c.has_editor){
59848                             p.css += ' x-grid-editable-cell';
59849                         }
59850                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59851                             p.css +=  ' x-grid-dirty-cell';
59852                         }
59853                         var markup = ct.apply(p);
59854                         if(!c.locked){
59855                             cb+= markup;
59856                         }else{
59857                             lcb+= markup;
59858                         }
59859                     }
59860                     var alt = [];
59861                     if(stripe && ((rowIndex+1) % 2 == 0)){
59862                         alt.push("x-grid-row-alt")
59863                     }
59864                     if(r.dirty){
59865                         alt.push(  " x-grid-dirty-row");
59866                     }
59867                     rp.cells = lcb;
59868                     if(this.getRowClass){
59869                         alt.push(this.getRowClass(r, rowIndex));
59870                     }
59871                     if (hasListener) {
59872                         rowcfg = {
59873                              
59874                             record: r,
59875                             rowIndex : rowIndex,
59876                             rowClass : ''
59877                         };
59878                         this.grid.fireEvent('rowclass', this, rowcfg);
59879                         alt.push(rowcfg.rowClass);
59880                     }
59881                     rp.alt = alt.join(" ");
59882                     lbuf+= rt.apply(rp);
59883                     rp.cells = cb;
59884                     buf+=  rt.apply(rp);
59885                 }
59886                 return [lbuf, buf];
59887             } :
59888             function(cs, rs, ds, startRow, colCount, stripe){
59889                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59890                 // buffers
59891                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59892                 var hasListener = this.grid.hasListener('rowclass');
59893  
59894                 var rowcfg = {};
59895                 for(var j = 0, len = rs.length; j < len; j++){
59896                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59897                     for(var i = 0; i < colCount; i++){
59898                         c = cs[i];
59899                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59900                         p.id = c.id;
59901                         p.css = p.attr = "";
59902                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59903                         if(p.value == undefined || p.value === "") {
59904                             p.value = "&#160;";
59905                         }
59906                         //Roo.log(c);
59907                          if(c.has_editor){
59908                             p.css += ' x-grid-editable-cell';
59909                         }
59910                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59911                             p.css += ' x-grid-dirty-cell' 
59912                         }
59913                         
59914                         var markup = ct.apply(p);
59915                         if(!c.locked){
59916                             cb[cb.length] = markup;
59917                         }else{
59918                             lcb[lcb.length] = markup;
59919                         }
59920                     }
59921                     var alt = [];
59922                     if(stripe && ((rowIndex+1) % 2 == 0)){
59923                         alt.push( "x-grid-row-alt");
59924                     }
59925                     if(r.dirty){
59926                         alt.push(" x-grid-dirty-row");
59927                     }
59928                     rp.cells = lcb;
59929                     if(this.getRowClass){
59930                         alt.push( this.getRowClass(r, rowIndex));
59931                     }
59932                     if (hasListener) {
59933                         rowcfg = {
59934                              
59935                             record: r,
59936                             rowIndex : rowIndex,
59937                             rowClass : ''
59938                         };
59939                         this.grid.fireEvent('rowclass', this, rowcfg);
59940                         alt.push(rowcfg.rowClass);
59941                     }
59942                     
59943                     rp.alt = alt.join(" ");
59944                     rp.cells = lcb.join("");
59945                     lbuf[lbuf.length] = rt.apply(rp);
59946                     rp.cells = cb.join("");
59947                     buf[buf.length] =  rt.apply(rp);
59948                 }
59949                 return [lbuf.join(""), buf.join("")];
59950             },
59951
59952     renderBody : function(){
59953         var markup = this.renderRows();
59954         var bt = this.templates.body;
59955         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59956     },
59957
59958     /**
59959      * Refreshes the grid
59960      * @param {Boolean} headersToo
59961      */
59962     refresh : function(headersToo){
59963         this.fireEvent("beforerefresh", this);
59964         this.grid.stopEditing();
59965         var result = this.renderBody();
59966         this.lockedBody.update(result[0]);
59967         this.mainBody.update(result[1]);
59968         if(headersToo === true){
59969             this.updateHeaders();
59970             this.updateColumns();
59971             this.updateSplitters();
59972             this.updateHeaderSortState();
59973         }
59974         this.syncRowHeights();
59975         this.layout();
59976         this.fireEvent("refresh", this);
59977     },
59978
59979     handleColumnMove : function(cm, oldIndex, newIndex){
59980         this.indexMap = null;
59981         var s = this.getScrollState();
59982         this.refresh(true);
59983         this.restoreScroll(s);
59984         this.afterMove(newIndex);
59985     },
59986
59987     afterMove : function(colIndex){
59988         if(this.enableMoveAnim && Roo.enableFx){
59989             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59990         }
59991         // if multisort - fix sortOrder, and reload..
59992         if (this.grid.dataSource.multiSort) {
59993             // the we can call sort again..
59994             var dm = this.grid.dataSource;
59995             var cm = this.grid.colModel;
59996             var so = [];
59997             for(var i = 0; i < cm.config.length; i++ ) {
59998                 
59999                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60000                     continue; // dont' bother, it's not in sort list or being set.
60001                 }
60002                 
60003                 so.push(cm.config[i].dataIndex);
60004             };
60005             dm.sortOrder = so;
60006             dm.load(dm.lastOptions);
60007             
60008             
60009         }
60010         
60011     },
60012
60013     updateCell : function(dm, rowIndex, dataIndex){
60014         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60015         if(typeof colIndex == "undefined"){ // not present in grid
60016             return;
60017         }
60018         var cm = this.grid.colModel;
60019         var cell = this.getCell(rowIndex, colIndex);
60020         var cellText = this.getCellText(rowIndex, colIndex);
60021
60022         var p = {
60023             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60024             id : cm.getColumnId(colIndex),
60025             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60026         };
60027         var renderer = cm.getRenderer(colIndex);
60028         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60029         if(typeof val == "undefined" || val === "") {
60030             val = "&#160;";
60031         }
60032         cellText.innerHTML = val;
60033         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60034         this.syncRowHeights(rowIndex, rowIndex);
60035     },
60036
60037     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60038         var maxWidth = 0;
60039         if(this.grid.autoSizeHeaders){
60040             var h = this.getHeaderCellMeasure(colIndex);
60041             maxWidth = Math.max(maxWidth, h.scrollWidth);
60042         }
60043         var tb, index;
60044         if(this.cm.isLocked(colIndex)){
60045             tb = this.getLockedTable();
60046             index = colIndex;
60047         }else{
60048             tb = this.getBodyTable();
60049             index = colIndex - this.cm.getLockedCount();
60050         }
60051         if(tb && tb.rows){
60052             var rows = tb.rows;
60053             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60054             for(var i = 0; i < stopIndex; i++){
60055                 var cell = rows[i].childNodes[index].firstChild;
60056                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60057             }
60058         }
60059         return maxWidth + /*margin for error in IE*/ 5;
60060     },
60061     /**
60062      * Autofit a column to its content.
60063      * @param {Number} colIndex
60064      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60065      */
60066      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60067          if(this.cm.isHidden(colIndex)){
60068              return; // can't calc a hidden column
60069          }
60070         if(forceMinSize){
60071             var cid = this.cm.getColumnId(colIndex);
60072             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60073            if(this.grid.autoSizeHeaders){
60074                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60075            }
60076         }
60077         var newWidth = this.calcColumnWidth(colIndex);
60078         this.cm.setColumnWidth(colIndex,
60079             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60080         if(!suppressEvent){
60081             this.grid.fireEvent("columnresize", colIndex, newWidth);
60082         }
60083     },
60084
60085     /**
60086      * Autofits all columns to their content and then expands to fit any extra space in the grid
60087      */
60088      autoSizeColumns : function(){
60089         var cm = this.grid.colModel;
60090         var colCount = cm.getColumnCount();
60091         for(var i = 0; i < colCount; i++){
60092             this.autoSizeColumn(i, true, true);
60093         }
60094         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60095             this.fitColumns();
60096         }else{
60097             this.updateColumns();
60098             this.layout();
60099         }
60100     },
60101
60102     /**
60103      * Autofits all columns to the grid's width proportionate with their current size
60104      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60105      */
60106     fitColumns : function(reserveScrollSpace){
60107         var cm = this.grid.colModel;
60108         var colCount = cm.getColumnCount();
60109         var cols = [];
60110         var width = 0;
60111         var i, w;
60112         for (i = 0; i < colCount; i++){
60113             if(!cm.isHidden(i) && !cm.isFixed(i)){
60114                 w = cm.getColumnWidth(i);
60115                 cols.push(i);
60116                 cols.push(w);
60117                 width += w;
60118             }
60119         }
60120         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60121         if(reserveScrollSpace){
60122             avail -= 17;
60123         }
60124         var frac = (avail - cm.getTotalWidth())/width;
60125         while (cols.length){
60126             w = cols.pop();
60127             i = cols.pop();
60128             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60129         }
60130         this.updateColumns();
60131         this.layout();
60132     },
60133
60134     onRowSelect : function(rowIndex){
60135         var row = this.getRowComposite(rowIndex);
60136         row.addClass("x-grid-row-selected");
60137     },
60138
60139     onRowDeselect : function(rowIndex){
60140         var row = this.getRowComposite(rowIndex);
60141         row.removeClass("x-grid-row-selected");
60142     },
60143
60144     onCellSelect : function(row, col){
60145         var cell = this.getCell(row, col);
60146         if(cell){
60147             Roo.fly(cell).addClass("x-grid-cell-selected");
60148         }
60149     },
60150
60151     onCellDeselect : function(row, col){
60152         var cell = this.getCell(row, col);
60153         if(cell){
60154             Roo.fly(cell).removeClass("x-grid-cell-selected");
60155         }
60156     },
60157
60158     updateHeaderSortState : function(){
60159         
60160         // sort state can be single { field: xxx, direction : yyy}
60161         // or   { xxx=>ASC , yyy : DESC ..... }
60162         
60163         var mstate = {};
60164         if (!this.ds.multiSort) { 
60165             var state = this.ds.getSortState();
60166             if(!state){
60167                 return;
60168             }
60169             mstate[state.field] = state.direction;
60170             // FIXME... - this is not used here.. but might be elsewhere..
60171             this.sortState = state;
60172             
60173         } else {
60174             mstate = this.ds.sortToggle;
60175         }
60176         //remove existing sort classes..
60177         
60178         var sc = this.sortClasses;
60179         var hds = this.el.select(this.headerSelector).removeClass(sc);
60180         
60181         for(var f in mstate) {
60182         
60183             var sortColumn = this.cm.findColumnIndex(f);
60184             
60185             if(sortColumn != -1){
60186                 var sortDir = mstate[f];        
60187                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60188             }
60189         }
60190         
60191          
60192         
60193     },
60194
60195
60196     handleHeaderClick : function(g, index,e){
60197         
60198         Roo.log("header click");
60199         
60200         if (Roo.isTouch) {
60201             // touch events on header are handled by context
60202             this.handleHdCtx(g,index,e);
60203             return;
60204         }
60205         
60206         
60207         if(this.headersDisabled){
60208             return;
60209         }
60210         var dm = g.dataSource, cm = g.colModel;
60211         if(!cm.isSortable(index)){
60212             return;
60213         }
60214         g.stopEditing();
60215         
60216         if (dm.multiSort) {
60217             // update the sortOrder
60218             var so = [];
60219             for(var i = 0; i < cm.config.length; i++ ) {
60220                 
60221                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60222                     continue; // dont' bother, it's not in sort list or being set.
60223                 }
60224                 
60225                 so.push(cm.config[i].dataIndex);
60226             };
60227             dm.sortOrder = so;
60228         }
60229         
60230         
60231         dm.sort(cm.getDataIndex(index));
60232     },
60233
60234
60235     destroy : function(){
60236         if(this.colMenu){
60237             this.colMenu.removeAll();
60238             Roo.menu.MenuMgr.unregister(this.colMenu);
60239             this.colMenu.getEl().remove();
60240             delete this.colMenu;
60241         }
60242         if(this.hmenu){
60243             this.hmenu.removeAll();
60244             Roo.menu.MenuMgr.unregister(this.hmenu);
60245             this.hmenu.getEl().remove();
60246             delete this.hmenu;
60247         }
60248         if(this.grid.enableColumnMove){
60249             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60250             if(dds){
60251                 for(var dd in dds){
60252                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60253                         var elid = dds[dd].dragElId;
60254                         dds[dd].unreg();
60255                         Roo.get(elid).remove();
60256                     } else if(dds[dd].config.isTarget){
60257                         dds[dd].proxyTop.remove();
60258                         dds[dd].proxyBottom.remove();
60259                         dds[dd].unreg();
60260                     }
60261                     if(Roo.dd.DDM.locationCache[dd]){
60262                         delete Roo.dd.DDM.locationCache[dd];
60263                     }
60264                 }
60265                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60266             }
60267         }
60268         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60269         this.bind(null, null);
60270         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60271     },
60272
60273     handleLockChange : function(){
60274         this.refresh(true);
60275     },
60276
60277     onDenyColumnLock : function(){
60278
60279     },
60280
60281     onDenyColumnHide : function(){
60282
60283     },
60284
60285     handleHdMenuClick : function(item){
60286         var index = this.hdCtxIndex;
60287         var cm = this.cm, ds = this.ds;
60288         switch(item.id){
60289             case "asc":
60290                 ds.sort(cm.getDataIndex(index), "ASC");
60291                 break;
60292             case "desc":
60293                 ds.sort(cm.getDataIndex(index), "DESC");
60294                 break;
60295             case "lock":
60296                 var lc = cm.getLockedCount();
60297                 if(cm.getColumnCount(true) <= lc+1){
60298                     this.onDenyColumnLock();
60299                     return;
60300                 }
60301                 if(lc != index){
60302                     cm.setLocked(index, true, true);
60303                     cm.moveColumn(index, lc);
60304                     this.grid.fireEvent("columnmove", index, lc);
60305                 }else{
60306                     cm.setLocked(index, true);
60307                 }
60308             break;
60309             case "unlock":
60310                 var lc = cm.getLockedCount();
60311                 if((lc-1) != index){
60312                     cm.setLocked(index, false, true);
60313                     cm.moveColumn(index, lc-1);
60314                     this.grid.fireEvent("columnmove", index, lc-1);
60315                 }else{
60316                     cm.setLocked(index, false);
60317                 }
60318             break;
60319             case 'wider': // used to expand cols on touch..
60320             case 'narrow':
60321                 var cw = cm.getColumnWidth(index);
60322                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60323                 cw = Math.max(0, cw);
60324                 cw = Math.min(cw,4000);
60325                 cm.setColumnWidth(index, cw);
60326                 break;
60327                 
60328             default:
60329                 index = cm.getIndexById(item.id.substr(4));
60330                 if(index != -1){
60331                     if(item.checked && cm.getColumnCount(true) <= 1){
60332                         this.onDenyColumnHide();
60333                         return false;
60334                     }
60335                     cm.setHidden(index, item.checked);
60336                 }
60337         }
60338         return true;
60339     },
60340
60341     beforeColMenuShow : function(){
60342         var cm = this.cm,  colCount = cm.getColumnCount();
60343         this.colMenu.removeAll();
60344         
60345         var items = [];
60346         for(var i = 0; i < colCount; i++){
60347             items.push({
60348                 id: "col-"+cm.getColumnId(i),
60349                 text: cm.getColumnHeader(i),
60350                 checked: !cm.isHidden(i),
60351                 hideOnClick:false
60352             });
60353         }
60354         
60355         if (this.grid.sortColMenu) {
60356             items.sort(function(a,b) {
60357                 if (a.text == b.text) {
60358                     return 0;
60359                 }
60360                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60361             });
60362         }
60363         
60364         for(var i = 0; i < colCount; i++){
60365             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60366         }
60367     },
60368
60369     handleHdCtx : function(g, index, e){
60370         e.stopEvent();
60371         var hd = this.getHeaderCell(index);
60372         this.hdCtxIndex = index;
60373         var ms = this.hmenu.items, cm = this.cm;
60374         ms.get("asc").setDisabled(!cm.isSortable(index));
60375         ms.get("desc").setDisabled(!cm.isSortable(index));
60376         if(this.grid.enableColLock !== false){
60377             ms.get("lock").setDisabled(cm.isLocked(index));
60378             ms.get("unlock").setDisabled(!cm.isLocked(index));
60379         }
60380         this.hmenu.show(hd, "tl-bl");
60381     },
60382
60383     handleHdOver : function(e){
60384         var hd = this.findHeaderCell(e.getTarget());
60385         if(hd && !this.headersDisabled){
60386             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60387                this.fly(hd).addClass("x-grid-hd-over");
60388             }
60389         }
60390     },
60391
60392     handleHdOut : function(e){
60393         var hd = this.findHeaderCell(e.getTarget());
60394         if(hd){
60395             this.fly(hd).removeClass("x-grid-hd-over");
60396         }
60397     },
60398
60399     handleSplitDblClick : function(e, t){
60400         var i = this.getCellIndex(t);
60401         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60402             this.autoSizeColumn(i, true);
60403             this.layout();
60404         }
60405     },
60406
60407     render : function(){
60408
60409         var cm = this.cm;
60410         var colCount = cm.getColumnCount();
60411
60412         if(this.grid.monitorWindowResize === true){
60413             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60414         }
60415         var header = this.renderHeaders();
60416         var body = this.templates.body.apply({rows:""});
60417         var html = this.templates.master.apply({
60418             lockedBody: body,
60419             body: body,
60420             lockedHeader: header[0],
60421             header: header[1]
60422         });
60423
60424         //this.updateColumns();
60425
60426         this.grid.getGridEl().dom.innerHTML = html;
60427
60428         this.initElements();
60429         
60430         // a kludge to fix the random scolling effect in webkit
60431         this.el.on("scroll", function() {
60432             this.el.dom.scrollTop=0; // hopefully not recursive..
60433         },this);
60434
60435         this.scroller.on("scroll", this.handleScroll, this);
60436         this.lockedBody.on("mousewheel", this.handleWheel, this);
60437         this.mainBody.on("mousewheel", this.handleWheel, this);
60438
60439         this.mainHd.on("mouseover", this.handleHdOver, this);
60440         this.mainHd.on("mouseout", this.handleHdOut, this);
60441         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60442                 {delegate: "."+this.splitClass});
60443
60444         this.lockedHd.on("mouseover", this.handleHdOver, this);
60445         this.lockedHd.on("mouseout", this.handleHdOut, this);
60446         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60447                 {delegate: "."+this.splitClass});
60448
60449         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60450             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60451         }
60452
60453         this.updateSplitters();
60454
60455         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60456             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60457             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60458         }
60459
60460         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60461             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60462             this.hmenu.add(
60463                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60464                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60465             );
60466             if(this.grid.enableColLock !== false){
60467                 this.hmenu.add('-',
60468                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60469                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60470                 );
60471             }
60472             if (Roo.isTouch) {
60473                  this.hmenu.add('-',
60474                     {id:"wider", text: this.columnsWiderText},
60475                     {id:"narrow", text: this.columnsNarrowText }
60476                 );
60477                 
60478                  
60479             }
60480             
60481             if(this.grid.enableColumnHide !== false){
60482
60483                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60484                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60485                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60486
60487                 this.hmenu.add('-',
60488                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60489                 );
60490             }
60491             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60492
60493             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60494         }
60495
60496         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60497             this.dd = new Roo.grid.GridDragZone(this.grid, {
60498                 ddGroup : this.grid.ddGroup || 'GridDD'
60499             });
60500             
60501         }
60502
60503         /*
60504         for(var i = 0; i < colCount; i++){
60505             if(cm.isHidden(i)){
60506                 this.hideColumn(i);
60507             }
60508             if(cm.config[i].align){
60509                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60510                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60511             }
60512         }*/
60513         
60514         this.updateHeaderSortState();
60515
60516         this.beforeInitialResize();
60517         this.layout(true);
60518
60519         // two part rendering gives faster view to the user
60520         this.renderPhase2.defer(1, this);
60521     },
60522
60523     renderPhase2 : function(){
60524         // render the rows now
60525         this.refresh();
60526         if(this.grid.autoSizeColumns){
60527             this.autoSizeColumns();
60528         }
60529     },
60530
60531     beforeInitialResize : function(){
60532
60533     },
60534
60535     onColumnSplitterMoved : function(i, w){
60536         this.userResized = true;
60537         var cm = this.grid.colModel;
60538         cm.setColumnWidth(i, w, true);
60539         var cid = cm.getColumnId(i);
60540         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60541         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60542         this.updateSplitters();
60543         this.layout();
60544         this.grid.fireEvent("columnresize", i, w);
60545     },
60546
60547     syncRowHeights : function(startIndex, endIndex){
60548         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60549             startIndex = startIndex || 0;
60550             var mrows = this.getBodyTable().rows;
60551             var lrows = this.getLockedTable().rows;
60552             var len = mrows.length-1;
60553             endIndex = Math.min(endIndex || len, len);
60554             for(var i = startIndex; i <= endIndex; i++){
60555                 var m = mrows[i], l = lrows[i];
60556                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60557                 m.style.height = l.style.height = h + "px";
60558             }
60559         }
60560     },
60561
60562     layout : function(initialRender, is2ndPass)
60563     {
60564         var g = this.grid;
60565         var auto = g.autoHeight;
60566         var scrollOffset = 16;
60567         var c = g.getGridEl(), cm = this.cm,
60568                 expandCol = g.autoExpandColumn,
60569                 gv = this;
60570         //c.beginMeasure();
60571
60572         if(!c.dom.offsetWidth){ // display:none?
60573             if(initialRender){
60574                 this.lockedWrap.show();
60575                 this.mainWrap.show();
60576             }
60577             return;
60578         }
60579
60580         var hasLock = this.cm.isLocked(0);
60581
60582         var tbh = this.headerPanel.getHeight();
60583         var bbh = this.footerPanel.getHeight();
60584
60585         if(auto){
60586             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60587             var newHeight = ch + c.getBorderWidth("tb");
60588             if(g.maxHeight){
60589                 newHeight = Math.min(g.maxHeight, newHeight);
60590             }
60591             c.setHeight(newHeight);
60592         }
60593
60594         if(g.autoWidth){
60595             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60596         }
60597
60598         var s = this.scroller;
60599
60600         var csize = c.getSize(true);
60601
60602         this.el.setSize(csize.width, csize.height);
60603
60604         this.headerPanel.setWidth(csize.width);
60605         this.footerPanel.setWidth(csize.width);
60606
60607         var hdHeight = this.mainHd.getHeight();
60608         var vw = csize.width;
60609         var vh = csize.height - (tbh + bbh);
60610
60611         s.setSize(vw, vh);
60612
60613         var bt = this.getBodyTable();
60614         
60615         if(cm.getLockedCount() == cm.config.length){
60616             bt = this.getLockedTable();
60617         }
60618         
60619         var ltWidth = hasLock ?
60620                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60621
60622         var scrollHeight = bt.offsetHeight;
60623         var scrollWidth = ltWidth + bt.offsetWidth;
60624         var vscroll = false, hscroll = false;
60625
60626         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60627
60628         var lw = this.lockedWrap, mw = this.mainWrap;
60629         var lb = this.lockedBody, mb = this.mainBody;
60630
60631         setTimeout(function(){
60632             var t = s.dom.offsetTop;
60633             var w = s.dom.clientWidth,
60634                 h = s.dom.clientHeight;
60635
60636             lw.setTop(t);
60637             lw.setSize(ltWidth, h);
60638
60639             mw.setLeftTop(ltWidth, t);
60640             mw.setSize(w-ltWidth, h);
60641
60642             lb.setHeight(h-hdHeight);
60643             mb.setHeight(h-hdHeight);
60644
60645             if(is2ndPass !== true && !gv.userResized && expandCol){
60646                 // high speed resize without full column calculation
60647                 
60648                 var ci = cm.getIndexById(expandCol);
60649                 if (ci < 0) {
60650                     ci = cm.findColumnIndex(expandCol);
60651                 }
60652                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60653                 var expandId = cm.getColumnId(ci);
60654                 var  tw = cm.getTotalWidth(false);
60655                 var currentWidth = cm.getColumnWidth(ci);
60656                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60657                 if(currentWidth != cw){
60658                     cm.setColumnWidth(ci, cw, true);
60659                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60660                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60661                     gv.updateSplitters();
60662                     gv.layout(false, true);
60663                 }
60664             }
60665
60666             if(initialRender){
60667                 lw.show();
60668                 mw.show();
60669             }
60670             //c.endMeasure();
60671         }, 10);
60672     },
60673
60674     onWindowResize : function(){
60675         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60676             return;
60677         }
60678         this.layout();
60679     },
60680
60681     appendFooter : function(parentEl){
60682         return null;
60683     },
60684
60685     sortAscText : "Sort Ascending",
60686     sortDescText : "Sort Descending",
60687     lockText : "Lock Column",
60688     unlockText : "Unlock Column",
60689     columnsText : "Columns",
60690  
60691     columnsWiderText : "Wider",
60692     columnsNarrowText : "Thinner"
60693 });
60694
60695
60696 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60697     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60698     this.proxy.el.addClass('x-grid3-col-dd');
60699 };
60700
60701 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60702     handleMouseDown : function(e){
60703
60704     },
60705
60706     callHandleMouseDown : function(e){
60707         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60708     }
60709 });
60710 /*
60711  * Based on:
60712  * Ext JS Library 1.1.1
60713  * Copyright(c) 2006-2007, Ext JS, LLC.
60714  *
60715  * Originally Released Under LGPL - original licence link has changed is not relivant.
60716  *
60717  * Fork - LGPL
60718  * <script type="text/javascript">
60719  */
60720  /**
60721  * @extends Roo.dd.DDProxy
60722  * @class Roo.grid.SplitDragZone
60723  * Support for Column Header resizing
60724  * @constructor
60725  * @param {Object} config
60726  */
60727 // private
60728 // This is a support class used internally by the Grid components
60729 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60730     this.grid = grid;
60731     this.view = grid.getView();
60732     this.proxy = this.view.resizeProxy;
60733     Roo.grid.SplitDragZone.superclass.constructor.call(
60734         this,
60735         hd, // ID
60736         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60737         {  // CONFIG
60738             dragElId : Roo.id(this.proxy.dom),
60739             resizeFrame:false
60740         }
60741     );
60742     
60743     this.setHandleElId(Roo.id(hd));
60744     if (hd2 !== false) {
60745         this.setOuterHandleElId(Roo.id(hd2));
60746     }
60747     
60748     this.scroll = false;
60749 };
60750 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60751     fly: Roo.Element.fly,
60752
60753     b4StartDrag : function(x, y){
60754         this.view.headersDisabled = true;
60755         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60756                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60757         );
60758         this.proxy.setHeight(h);
60759         
60760         // for old system colWidth really stored the actual width?
60761         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60762         // which in reality did not work.. - it worked only for fixed sizes
60763         // for resizable we need to use actual sizes.
60764         var w = this.cm.getColumnWidth(this.cellIndex);
60765         if (!this.view.mainWrap) {
60766             // bootstrap.
60767             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60768         }
60769         
60770         
60771         
60772         // this was w-this.grid.minColumnWidth;
60773         // doesnt really make sense? - w = thie curren width or the rendered one?
60774         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60775         this.resetConstraints();
60776         this.setXConstraint(minw, 1000);
60777         this.setYConstraint(0, 0);
60778         this.minX = x - minw;
60779         this.maxX = x + 1000;
60780         this.startPos = x;
60781         if (!this.view.mainWrap) { // this is Bootstrap code..
60782             this.getDragEl().style.display='block';
60783         }
60784         
60785         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60786     },
60787
60788
60789     handleMouseDown : function(e){
60790         ev = Roo.EventObject.setEvent(e);
60791         var t = this.fly(ev.getTarget());
60792         if(t.hasClass("x-grid-split")){
60793             this.cellIndex = this.view.getCellIndex(t.dom);
60794             this.split = t.dom;
60795             this.cm = this.grid.colModel;
60796             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60797                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60798             }
60799         }
60800     },
60801
60802     endDrag : function(e){
60803         this.view.headersDisabled = false;
60804         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60805         var diff = endX - this.startPos;
60806         // 
60807         var w = this.cm.getColumnWidth(this.cellIndex);
60808         if (!this.view.mainWrap) {
60809             w = 0;
60810         }
60811         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60812     },
60813
60814     autoOffset : function(){
60815         this.setDelta(0,0);
60816     }
60817 });/*
60818  * Based on:
60819  * Ext JS Library 1.1.1
60820  * Copyright(c) 2006-2007, Ext JS, LLC.
60821  *
60822  * Originally Released Under LGPL - original licence link has changed is not relivant.
60823  *
60824  * Fork - LGPL
60825  * <script type="text/javascript">
60826  */
60827  
60828 // private
60829 // This is a support class used internally by the Grid components
60830 Roo.grid.GridDragZone = function(grid, config){
60831     this.view = grid.getView();
60832     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60833     if(this.view.lockedBody){
60834         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60835         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60836     }
60837     this.scroll = false;
60838     this.grid = grid;
60839     this.ddel = document.createElement('div');
60840     this.ddel.className = 'x-grid-dd-wrap';
60841 };
60842
60843 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60844     ddGroup : "GridDD",
60845
60846     getDragData : function(e){
60847         var t = Roo.lib.Event.getTarget(e);
60848         var rowIndex = this.view.findRowIndex(t);
60849         var sm = this.grid.selModel;
60850             
60851         //Roo.log(rowIndex);
60852         
60853         if (sm.getSelectedCell) {
60854             // cell selection..
60855             if (!sm.getSelectedCell()) {
60856                 return false;
60857             }
60858             if (rowIndex != sm.getSelectedCell()[0]) {
60859                 return false;
60860             }
60861         
60862         }
60863         if (sm.getSelections && sm.getSelections().length < 1) {
60864             return false;
60865         }
60866         
60867         
60868         // before it used to all dragging of unseleted... - now we dont do that.
60869         if(rowIndex !== false){
60870             
60871             // if editorgrid.. 
60872             
60873             
60874             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60875                
60876             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60877               //  
60878             //}
60879             if (e.hasModifier()){
60880                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60881             }
60882             
60883             Roo.log("getDragData");
60884             
60885             return {
60886                 grid: this.grid,
60887                 ddel: this.ddel,
60888                 rowIndex: rowIndex,
60889                 selections: sm.getSelections ? sm.getSelections() : (
60890                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60891             };
60892         }
60893         return false;
60894     },
60895     
60896     
60897     onInitDrag : function(e){
60898         var data = this.dragData;
60899         this.ddel.innerHTML = this.grid.getDragDropText();
60900         this.proxy.update(this.ddel);
60901         // fire start drag?
60902     },
60903
60904     afterRepair : function(){
60905         this.dragging = false;
60906     },
60907
60908     getRepairXY : function(e, data){
60909         return false;
60910     },
60911
60912     onEndDrag : function(data, e){
60913         // fire end drag?
60914     },
60915
60916     onValidDrop : function(dd, e, id){
60917         // fire drag drop?
60918         this.hideProxy();
60919     },
60920
60921     beforeInvalidDrop : function(e, id){
60922
60923     }
60924 });/*
60925  * Based on:
60926  * Ext JS Library 1.1.1
60927  * Copyright(c) 2006-2007, Ext JS, LLC.
60928  *
60929  * Originally Released Under LGPL - original licence link has changed is not relivant.
60930  *
60931  * Fork - LGPL
60932  * <script type="text/javascript">
60933  */
60934  
60935
60936 /**
60937  * @class Roo.grid.ColumnModel
60938  * @extends Roo.util.Observable
60939  * This is the default implementation of a ColumnModel used by the Grid. It defines
60940  * the columns in the grid.
60941  * <br>Usage:<br>
60942  <pre><code>
60943  var colModel = new Roo.grid.ColumnModel([
60944         {header: "Ticker", width: 60, sortable: true, locked: true},
60945         {header: "Company Name", width: 150, sortable: true},
60946         {header: "Market Cap.", width: 100, sortable: true},
60947         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60948         {header: "Employees", width: 100, sortable: true, resizable: false}
60949  ]);
60950  </code></pre>
60951  * <p>
60952  
60953  * The config options listed for this class are options which may appear in each
60954  * individual column definition.
60955  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60956  * @constructor
60957  * @param {Object} config An Array of column config objects. See this class's
60958  * config objects for details.
60959 */
60960 Roo.grid.ColumnModel = function(config){
60961         /**
60962      * The config passed into the constructor
60963      */
60964     this.config = []; //config;
60965     this.lookup = {};
60966
60967     // if no id, create one
60968     // if the column does not have a dataIndex mapping,
60969     // map it to the order it is in the config
60970     for(var i = 0, len = config.length; i < len; i++){
60971         this.addColumn(config[i]);
60972         
60973     }
60974
60975     /**
60976      * The width of columns which have no width specified (defaults to 100)
60977      * @type Number
60978      */
60979     this.defaultWidth = 100;
60980
60981     /**
60982      * Default sortable of columns which have no sortable specified (defaults to false)
60983      * @type Boolean
60984      */
60985     this.defaultSortable = false;
60986
60987     this.addEvents({
60988         /**
60989              * @event widthchange
60990              * Fires when the width of a column changes.
60991              * @param {ColumnModel} this
60992              * @param {Number} columnIndex The column index
60993              * @param {Number} newWidth The new width
60994              */
60995             "widthchange": true,
60996         /**
60997              * @event headerchange
60998              * Fires when the text of a header changes.
60999              * @param {ColumnModel} this
61000              * @param {Number} columnIndex The column index
61001              * @param {Number} newText The new header text
61002              */
61003             "headerchange": true,
61004         /**
61005              * @event hiddenchange
61006              * Fires when a column is hidden or "unhidden".
61007              * @param {ColumnModel} this
61008              * @param {Number} columnIndex The column index
61009              * @param {Boolean} hidden true if hidden, false otherwise
61010              */
61011             "hiddenchange": true,
61012             /**
61013          * @event columnmoved
61014          * Fires when a column is moved.
61015          * @param {ColumnModel} this
61016          * @param {Number} oldIndex
61017          * @param {Number} newIndex
61018          */
61019         "columnmoved" : true,
61020         /**
61021          * @event columlockchange
61022          * Fires when a column's locked state is changed
61023          * @param {ColumnModel} this
61024          * @param {Number} colIndex
61025          * @param {Boolean} locked true if locked
61026          */
61027         "columnlockchange" : true
61028     });
61029     Roo.grid.ColumnModel.superclass.constructor.call(this);
61030 };
61031 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61032     /**
61033      * @cfg {String} header The header text to display in the Grid view.
61034      */
61035         /**
61036      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61037      */
61038         /**
61039      * @cfg {String} smHeader Header at Bootsrap Small width
61040      */
61041         /**
61042      * @cfg {String} mdHeader Header at Bootsrap Medium width
61043      */
61044         /**
61045      * @cfg {String} lgHeader Header at Bootsrap Large width
61046      */
61047         /**
61048      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61049      */
61050     /**
61051      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61052      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61053      * specified, the column's index is used as an index into the Record's data Array.
61054      */
61055     /**
61056      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61057      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61058      */
61059     /**
61060      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61061      * Defaults to the value of the {@link #defaultSortable} property.
61062      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61063      */
61064     /**
61065      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61066      */
61067     /**
61068      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61069      */
61070     /**
61071      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61072      */
61073     /**
61074      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61075      */
61076     /**
61077      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61078      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61079      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61080      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61081      */
61082        /**
61083      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61084      */
61085     /**
61086      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61087      */
61088     /**
61089      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61090      */
61091     /**
61092      * @cfg {String} cursor (Optional)
61093      */
61094     /**
61095      * @cfg {String} tooltip (Optional)
61096      */
61097     /**
61098      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61099      */
61100     /**
61101      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61102      */
61103     /**
61104      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61105      */
61106     /**
61107      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61108      */
61109         /**
61110      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61111      */
61112     /**
61113      * Returns the id of the column at the specified index.
61114      * @param {Number} index The column index
61115      * @return {String} the id
61116      */
61117     getColumnId : function(index){
61118         return this.config[index].id;
61119     },
61120
61121     /**
61122      * Returns the column for a specified id.
61123      * @param {String} id The column id
61124      * @return {Object} the column
61125      */
61126     getColumnById : function(id){
61127         return this.lookup[id];
61128     },
61129
61130     
61131     /**
61132      * Returns the column Object for a specified dataIndex.
61133      * @param {String} dataIndex The column dataIndex
61134      * @return {Object|Boolean} the column or false if not found
61135      */
61136     getColumnByDataIndex: function(dataIndex){
61137         var index = this.findColumnIndex(dataIndex);
61138         return index > -1 ? this.config[index] : false;
61139     },
61140     
61141     /**
61142      * Returns the index for a specified column id.
61143      * @param {String} id The column id
61144      * @return {Number} the index, or -1 if not found
61145      */
61146     getIndexById : function(id){
61147         for(var i = 0, len = this.config.length; i < len; i++){
61148             if(this.config[i].id == id){
61149                 return i;
61150             }
61151         }
61152         return -1;
61153     },
61154     
61155     /**
61156      * Returns the index for a specified column dataIndex.
61157      * @param {String} dataIndex The column dataIndex
61158      * @return {Number} the index, or -1 if not found
61159      */
61160     
61161     findColumnIndex : function(dataIndex){
61162         for(var i = 0, len = this.config.length; i < len; i++){
61163             if(this.config[i].dataIndex == dataIndex){
61164                 return i;
61165             }
61166         }
61167         return -1;
61168     },
61169     
61170     
61171     moveColumn : function(oldIndex, newIndex){
61172         var c = this.config[oldIndex];
61173         this.config.splice(oldIndex, 1);
61174         this.config.splice(newIndex, 0, c);
61175         this.dataMap = null;
61176         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61177     },
61178
61179     isLocked : function(colIndex){
61180         return this.config[colIndex].locked === true;
61181     },
61182
61183     setLocked : function(colIndex, value, suppressEvent){
61184         if(this.isLocked(colIndex) == value){
61185             return;
61186         }
61187         this.config[colIndex].locked = value;
61188         if(!suppressEvent){
61189             this.fireEvent("columnlockchange", this, colIndex, value);
61190         }
61191     },
61192
61193     getTotalLockedWidth : function(){
61194         var totalWidth = 0;
61195         for(var i = 0; i < this.config.length; i++){
61196             if(this.isLocked(i) && !this.isHidden(i)){
61197                 this.totalWidth += this.getColumnWidth(i);
61198             }
61199         }
61200         return totalWidth;
61201     },
61202
61203     getLockedCount : function(){
61204         for(var i = 0, len = this.config.length; i < len; i++){
61205             if(!this.isLocked(i)){
61206                 return i;
61207             }
61208         }
61209         
61210         return this.config.length;
61211     },
61212
61213     /**
61214      * Returns the number of columns.
61215      * @return {Number}
61216      */
61217     getColumnCount : function(visibleOnly){
61218         if(visibleOnly === true){
61219             var c = 0;
61220             for(var i = 0, len = this.config.length; i < len; i++){
61221                 if(!this.isHidden(i)){
61222                     c++;
61223                 }
61224             }
61225             return c;
61226         }
61227         return this.config.length;
61228     },
61229
61230     /**
61231      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61232      * @param {Function} fn
61233      * @param {Object} scope (optional)
61234      * @return {Array} result
61235      */
61236     getColumnsBy : function(fn, scope){
61237         var r = [];
61238         for(var i = 0, len = this.config.length; i < len; i++){
61239             var c = this.config[i];
61240             if(fn.call(scope||this, c, i) === true){
61241                 r[r.length] = c;
61242             }
61243         }
61244         return r;
61245     },
61246
61247     /**
61248      * Returns true if the specified column is sortable.
61249      * @param {Number} col The column index
61250      * @return {Boolean}
61251      */
61252     isSortable : function(col){
61253         if(typeof this.config[col].sortable == "undefined"){
61254             return this.defaultSortable;
61255         }
61256         return this.config[col].sortable;
61257     },
61258
61259     /**
61260      * Returns the rendering (formatting) function defined for the column.
61261      * @param {Number} col The column index.
61262      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61263      */
61264     getRenderer : function(col){
61265         if(!this.config[col].renderer){
61266             return Roo.grid.ColumnModel.defaultRenderer;
61267         }
61268         return this.config[col].renderer;
61269     },
61270
61271     /**
61272      * Sets the rendering (formatting) function for a column.
61273      * @param {Number} col The column index
61274      * @param {Function} fn The function to use to process the cell's raw data
61275      * to return HTML markup for the grid view. The render function is called with
61276      * the following parameters:<ul>
61277      * <li>Data value.</li>
61278      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61279      * <li>css A CSS style string to apply to the table cell.</li>
61280      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61281      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61282      * <li>Row index</li>
61283      * <li>Column index</li>
61284      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61285      */
61286     setRenderer : function(col, fn){
61287         this.config[col].renderer = fn;
61288     },
61289
61290     /**
61291      * Returns the width for the specified column.
61292      * @param {Number} col The column index
61293      * @param (optional) {String} gridSize bootstrap width size.
61294      * @return {Number}
61295      */
61296     getColumnWidth : function(col, gridSize)
61297         {
61298                 var cfg = this.config[col];
61299                 
61300                 if (typeof(gridSize) == 'undefined') {
61301                         return cfg.width * 1 || this.defaultWidth;
61302                 }
61303                 if (gridSize === false) { // if we set it..
61304                         return cfg.width || false;
61305                 }
61306                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61307                 
61308                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61309                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61310                                 continue;
61311                         }
61312                         return cfg[ sizes[i] ];
61313                 }
61314                 return 1;
61315                 
61316     },
61317
61318     /**
61319      * Sets the width for a column.
61320      * @param {Number} col The column index
61321      * @param {Number} width The new width
61322      */
61323     setColumnWidth : function(col, width, suppressEvent){
61324         this.config[col].width = width;
61325         this.totalWidth = null;
61326         if(!suppressEvent){
61327              this.fireEvent("widthchange", this, col, width);
61328         }
61329     },
61330
61331     /**
61332      * Returns the total width of all columns.
61333      * @param {Boolean} includeHidden True to include hidden column widths
61334      * @return {Number}
61335      */
61336     getTotalWidth : function(includeHidden){
61337         if(!this.totalWidth){
61338             this.totalWidth = 0;
61339             for(var i = 0, len = this.config.length; i < len; i++){
61340                 if(includeHidden || !this.isHidden(i)){
61341                     this.totalWidth += this.getColumnWidth(i);
61342                 }
61343             }
61344         }
61345         return this.totalWidth;
61346     },
61347
61348     /**
61349      * Returns the header for the specified column.
61350      * @param {Number} col The column index
61351      * @return {String}
61352      */
61353     getColumnHeader : function(col){
61354         return this.config[col].header;
61355     },
61356
61357     /**
61358      * Sets the header for a column.
61359      * @param {Number} col The column index
61360      * @param {String} header The new header
61361      */
61362     setColumnHeader : function(col, header){
61363         this.config[col].header = header;
61364         this.fireEvent("headerchange", this, col, header);
61365     },
61366
61367     /**
61368      * Returns the tooltip for the specified column.
61369      * @param {Number} col The column index
61370      * @return {String}
61371      */
61372     getColumnTooltip : function(col){
61373             return this.config[col].tooltip;
61374     },
61375     /**
61376      * Sets the tooltip for a column.
61377      * @param {Number} col The column index
61378      * @param {String} tooltip The new tooltip
61379      */
61380     setColumnTooltip : function(col, tooltip){
61381             this.config[col].tooltip = tooltip;
61382     },
61383
61384     /**
61385      * Returns the dataIndex for the specified column.
61386      * @param {Number} col The column index
61387      * @return {Number}
61388      */
61389     getDataIndex : function(col){
61390         return this.config[col].dataIndex;
61391     },
61392
61393     /**
61394      * Sets the dataIndex for a column.
61395      * @param {Number} col The column index
61396      * @param {Number} dataIndex The new dataIndex
61397      */
61398     setDataIndex : function(col, dataIndex){
61399         this.config[col].dataIndex = dataIndex;
61400     },
61401
61402     
61403     
61404     /**
61405      * Returns true if the cell is editable.
61406      * @param {Number} colIndex The column index
61407      * @param {Number} rowIndex The row index - this is nto actually used..?
61408      * @return {Boolean}
61409      */
61410     isCellEditable : function(colIndex, rowIndex){
61411         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61412     },
61413
61414     /**
61415      * Returns the editor defined for the cell/column.
61416      * return false or null to disable editing.
61417      * @param {Number} colIndex The column index
61418      * @param {Number} rowIndex The row index
61419      * @return {Object}
61420      */
61421     getCellEditor : function(colIndex, rowIndex){
61422         return this.config[colIndex].editor;
61423     },
61424
61425     /**
61426      * Sets if a column is editable.
61427      * @param {Number} col The column index
61428      * @param {Boolean} editable True if the column is editable
61429      */
61430     setEditable : function(col, editable){
61431         this.config[col].editable = editable;
61432     },
61433
61434
61435     /**
61436      * Returns true if the column is hidden.
61437      * @param {Number} colIndex The column index
61438      * @return {Boolean}
61439      */
61440     isHidden : function(colIndex){
61441         return this.config[colIndex].hidden;
61442     },
61443
61444
61445     /**
61446      * Returns true if the column width cannot be changed
61447      */
61448     isFixed : function(colIndex){
61449         return this.config[colIndex].fixed;
61450     },
61451
61452     /**
61453      * Returns true if the column can be resized
61454      * @return {Boolean}
61455      */
61456     isResizable : function(colIndex){
61457         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61458     },
61459     /**
61460      * Sets if a column is hidden.
61461      * @param {Number} colIndex The column index
61462      * @param {Boolean} hidden True if the column is hidden
61463      */
61464     setHidden : function(colIndex, hidden){
61465         this.config[colIndex].hidden = hidden;
61466         this.totalWidth = null;
61467         this.fireEvent("hiddenchange", this, colIndex, hidden);
61468     },
61469
61470     /**
61471      * Sets the editor for a column.
61472      * @param {Number} col The column index
61473      * @param {Object} editor The editor object
61474      */
61475     setEditor : function(col, editor){
61476         this.config[col].editor = editor;
61477     },
61478     /**
61479      * Add a column (experimental...) - defaults to adding to the end..
61480      * @param {Object} config 
61481     */
61482     addColumn : function(c)
61483     {
61484     
61485         var i = this.config.length;
61486         this.config[i] = c;
61487         
61488         if(typeof c.dataIndex == "undefined"){
61489             c.dataIndex = i;
61490         }
61491         if(typeof c.renderer == "string"){
61492             c.renderer = Roo.util.Format[c.renderer];
61493         }
61494         if(typeof c.id == "undefined"){
61495             c.id = Roo.id();
61496         }
61497         if(c.editor && c.editor.xtype){
61498             c.editor  = Roo.factory(c.editor, Roo.grid);
61499         }
61500         if(c.editor && c.editor.isFormField){
61501             c.editor = new Roo.grid.GridEditor(c.editor);
61502         }
61503         this.lookup[c.id] = c;
61504     }
61505     
61506 });
61507
61508 Roo.grid.ColumnModel.defaultRenderer = function(value)
61509 {
61510     if(typeof value == "object") {
61511         return value;
61512     }
61513         if(typeof value == "string" && value.length < 1){
61514             return "&#160;";
61515         }
61516     
61517         return String.format("{0}", value);
61518 };
61519
61520 // Alias for backwards compatibility
61521 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61522 /*
61523  * Based on:
61524  * Ext JS Library 1.1.1
61525  * Copyright(c) 2006-2007, Ext JS, LLC.
61526  *
61527  * Originally Released Under LGPL - original licence link has changed is not relivant.
61528  *
61529  * Fork - LGPL
61530  * <script type="text/javascript">
61531  */
61532
61533 /**
61534  * @class Roo.grid.AbstractSelectionModel
61535  * @extends Roo.util.Observable
61536  * @abstract
61537  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61538  * implemented by descendant classes.  This class should not be directly instantiated.
61539  * @constructor
61540  */
61541 Roo.grid.AbstractSelectionModel = function(){
61542     this.locked = false;
61543     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61544 };
61545
61546 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61547     /** @ignore Called by the grid automatically. Do not call directly. */
61548     init : function(grid){
61549         this.grid = grid;
61550         this.initEvents();
61551     },
61552
61553     /**
61554      * Locks the selections.
61555      */
61556     lock : function(){
61557         this.locked = true;
61558     },
61559
61560     /**
61561      * Unlocks the selections.
61562      */
61563     unlock : function(){
61564         this.locked = false;
61565     },
61566
61567     /**
61568      * Returns true if the selections are locked.
61569      * @return {Boolean}
61570      */
61571     isLocked : function(){
61572         return this.locked;
61573     }
61574 });/*
61575  * Based on:
61576  * Ext JS Library 1.1.1
61577  * Copyright(c) 2006-2007, Ext JS, LLC.
61578  *
61579  * Originally Released Under LGPL - original licence link has changed is not relivant.
61580  *
61581  * Fork - LGPL
61582  * <script type="text/javascript">
61583  */
61584 /**
61585  * @extends Roo.grid.AbstractSelectionModel
61586  * @class Roo.grid.RowSelectionModel
61587  * The default SelectionModel used by {@link Roo.grid.Grid}.
61588  * It supports multiple selections and keyboard selection/navigation. 
61589  * @constructor
61590  * @param {Object} config
61591  */
61592 Roo.grid.RowSelectionModel = function(config){
61593     Roo.apply(this, config);
61594     this.selections = new Roo.util.MixedCollection(false, function(o){
61595         return o.id;
61596     });
61597
61598     this.last = false;
61599     this.lastActive = false;
61600
61601     this.addEvents({
61602         /**
61603         * @event selectionchange
61604         * Fires when the selection changes
61605         * @param {SelectionModel} this
61606         */
61607        "selectionchange" : true,
61608        /**
61609         * @event afterselectionchange
61610         * Fires after the selection changes (eg. by key press or clicking)
61611         * @param {SelectionModel} this
61612         */
61613        "afterselectionchange" : true,
61614        /**
61615         * @event beforerowselect
61616         * Fires when a row is selected being selected, return false to cancel.
61617         * @param {SelectionModel} this
61618         * @param {Number} rowIndex The selected index
61619         * @param {Boolean} keepExisting False if other selections will be cleared
61620         */
61621        "beforerowselect" : true,
61622        /**
61623         * @event rowselect
61624         * Fires when a row is selected.
61625         * @param {SelectionModel} this
61626         * @param {Number} rowIndex The selected index
61627         * @param {Roo.data.Record} r The record
61628         */
61629        "rowselect" : true,
61630        /**
61631         * @event rowdeselect
61632         * Fires when a row is deselected.
61633         * @param {SelectionModel} this
61634         * @param {Number} rowIndex The selected index
61635         */
61636         "rowdeselect" : true
61637     });
61638     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61639     this.locked = false;
61640 };
61641
61642 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61643     /**
61644      * @cfg {Boolean} singleSelect
61645      * True to allow selection of only one row at a time (defaults to false)
61646      */
61647     singleSelect : false,
61648
61649     // private
61650     initEvents : function(){
61651
61652         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61653             this.grid.on("mousedown", this.handleMouseDown, this);
61654         }else{ // allow click to work like normal
61655             this.grid.on("rowclick", this.handleDragableRowClick, this);
61656         }
61657         // bootstrap does not have a view..
61658         var view = this.grid.view ? this.grid.view : this.grid;
61659         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61660             "up" : function(e){
61661                 if(!e.shiftKey){
61662                     this.selectPrevious(e.shiftKey);
61663                 }else if(this.last !== false && this.lastActive !== false){
61664                     var last = this.last;
61665                     this.selectRange(this.last,  this.lastActive-1);
61666                     view.focusRow(this.lastActive);
61667                     if(last !== false){
61668                         this.last = last;
61669                     }
61670                 }else{
61671                     this.selectFirstRow();
61672                 }
61673                 this.fireEvent("afterselectionchange", this);
61674             },
61675             "down" : function(e){
61676                 if(!e.shiftKey){
61677                     this.selectNext(e.shiftKey);
61678                 }else if(this.last !== false && this.lastActive !== false){
61679                     var last = this.last;
61680                     this.selectRange(this.last,  this.lastActive+1);
61681                     view.focusRow(this.lastActive);
61682                     if(last !== false){
61683                         this.last = last;
61684                     }
61685                 }else{
61686                     this.selectFirstRow();
61687                 }
61688                 this.fireEvent("afterselectionchange", this);
61689             },
61690             scope: this
61691         });
61692
61693          
61694         view.on("refresh", this.onRefresh, this);
61695         view.on("rowupdated", this.onRowUpdated, this);
61696         view.on("rowremoved", this.onRemove, this);
61697     },
61698
61699     // private
61700     onRefresh : function(){
61701         var ds = this.grid.ds, i, v = this.grid.view;
61702         var s = this.selections;
61703         s.each(function(r){
61704             if((i = ds.indexOfId(r.id)) != -1){
61705                 v.onRowSelect(i);
61706                 s.add(ds.getAt(i)); // updating the selection relate data
61707             }else{
61708                 s.remove(r);
61709             }
61710         });
61711     },
61712
61713     // private
61714     onRemove : function(v, index, r){
61715         this.selections.remove(r);
61716     },
61717
61718     // private
61719     onRowUpdated : function(v, index, r){
61720         if(this.isSelected(r)){
61721             v.onRowSelect(index);
61722         }
61723     },
61724
61725     /**
61726      * Select records.
61727      * @param {Array} records The records to select
61728      * @param {Boolean} keepExisting (optional) True to keep existing selections
61729      */
61730     selectRecords : function(records, keepExisting){
61731         if(!keepExisting){
61732             this.clearSelections();
61733         }
61734         var ds = this.grid.ds;
61735         for(var i = 0, len = records.length; i < len; i++){
61736             this.selectRow(ds.indexOf(records[i]), true);
61737         }
61738     },
61739
61740     /**
61741      * Gets the number of selected rows.
61742      * @return {Number}
61743      */
61744     getCount : function(){
61745         return this.selections.length;
61746     },
61747
61748     /**
61749      * Selects the first row in the grid.
61750      */
61751     selectFirstRow : function(){
61752         this.selectRow(0);
61753     },
61754
61755     /**
61756      * Select the last row.
61757      * @param {Boolean} keepExisting (optional) True to keep existing selections
61758      */
61759     selectLastRow : function(keepExisting){
61760         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61761     },
61762
61763     /**
61764      * Selects the row immediately following the last selected row.
61765      * @param {Boolean} keepExisting (optional) True to keep existing selections
61766      */
61767     selectNext : function(keepExisting){
61768         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61769             this.selectRow(this.last+1, keepExisting);
61770             var view = this.grid.view ? this.grid.view : this.grid;
61771             view.focusRow(this.last);
61772         }
61773     },
61774
61775     /**
61776      * Selects the row that precedes the last selected row.
61777      * @param {Boolean} keepExisting (optional) True to keep existing selections
61778      */
61779     selectPrevious : function(keepExisting){
61780         if(this.last){
61781             this.selectRow(this.last-1, keepExisting);
61782             var view = this.grid.view ? this.grid.view : this.grid;
61783             view.focusRow(this.last);
61784         }
61785     },
61786
61787     /**
61788      * Returns the selected records
61789      * @return {Array} Array of selected records
61790      */
61791     getSelections : function(){
61792         return [].concat(this.selections.items);
61793     },
61794
61795     /**
61796      * Returns the first selected record.
61797      * @return {Record}
61798      */
61799     getSelected : function(){
61800         return this.selections.itemAt(0);
61801     },
61802
61803
61804     /**
61805      * Clears all selections.
61806      */
61807     clearSelections : function(fast){
61808         if(this.locked) {
61809             return;
61810         }
61811         if(fast !== true){
61812             var ds = this.grid.ds;
61813             var s = this.selections;
61814             s.each(function(r){
61815                 this.deselectRow(ds.indexOfId(r.id));
61816             }, this);
61817             s.clear();
61818         }else{
61819             this.selections.clear();
61820         }
61821         this.last = false;
61822     },
61823
61824
61825     /**
61826      * Selects all rows.
61827      */
61828     selectAll : function(){
61829         if(this.locked) {
61830             return;
61831         }
61832         this.selections.clear();
61833         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61834             this.selectRow(i, true);
61835         }
61836     },
61837
61838     /**
61839      * Returns True if there is a selection.
61840      * @return {Boolean}
61841      */
61842     hasSelection : function(){
61843         return this.selections.length > 0;
61844     },
61845
61846     /**
61847      * Returns True if the specified row is selected.
61848      * @param {Number/Record} record The record or index of the record to check
61849      * @return {Boolean}
61850      */
61851     isSelected : function(index){
61852         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61853         return (r && this.selections.key(r.id) ? true : false);
61854     },
61855
61856     /**
61857      * Returns True if the specified record id is selected.
61858      * @param {String} id The id of record to check
61859      * @return {Boolean}
61860      */
61861     isIdSelected : function(id){
61862         return (this.selections.key(id) ? true : false);
61863     },
61864
61865     // private
61866     handleMouseDown : function(e, t)
61867     {
61868         var view = this.grid.view ? this.grid.view : this.grid;
61869         var rowIndex;
61870         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61871             return;
61872         };
61873         if(e.shiftKey && this.last !== false){
61874             var last = this.last;
61875             this.selectRange(last, rowIndex, e.ctrlKey);
61876             this.last = last; // reset the last
61877             view.focusRow(rowIndex);
61878         }else{
61879             var isSelected = this.isSelected(rowIndex);
61880             if(e.button !== 0 && isSelected){
61881                 view.focusRow(rowIndex);
61882             }else if(e.ctrlKey && isSelected){
61883                 this.deselectRow(rowIndex);
61884             }else if(!isSelected){
61885                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61886                 view.focusRow(rowIndex);
61887             }
61888         }
61889         this.fireEvent("afterselectionchange", this);
61890     },
61891     // private
61892     handleDragableRowClick :  function(grid, rowIndex, e) 
61893     {
61894         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61895             this.selectRow(rowIndex, false);
61896             var view = this.grid.view ? this.grid.view : this.grid;
61897             view.focusRow(rowIndex);
61898              this.fireEvent("afterselectionchange", this);
61899         }
61900     },
61901     
61902     /**
61903      * Selects multiple rows.
61904      * @param {Array} rows Array of the indexes of the row to select
61905      * @param {Boolean} keepExisting (optional) True to keep existing selections
61906      */
61907     selectRows : function(rows, keepExisting){
61908         if(!keepExisting){
61909             this.clearSelections();
61910         }
61911         for(var i = 0, len = rows.length; i < len; i++){
61912             this.selectRow(rows[i], true);
61913         }
61914     },
61915
61916     /**
61917      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61918      * @param {Number} startRow The index of the first row in the range
61919      * @param {Number} endRow The index of the last row in the range
61920      * @param {Boolean} keepExisting (optional) True to retain existing selections
61921      */
61922     selectRange : function(startRow, endRow, keepExisting){
61923         if(this.locked) {
61924             return;
61925         }
61926         if(!keepExisting){
61927             this.clearSelections();
61928         }
61929         if(startRow <= endRow){
61930             for(var i = startRow; i <= endRow; i++){
61931                 this.selectRow(i, true);
61932             }
61933         }else{
61934             for(var i = startRow; i >= endRow; i--){
61935                 this.selectRow(i, true);
61936             }
61937         }
61938     },
61939
61940     /**
61941      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61942      * @param {Number} startRow The index of the first row in the range
61943      * @param {Number} endRow The index of the last row in the range
61944      */
61945     deselectRange : function(startRow, endRow, preventViewNotify){
61946         if(this.locked) {
61947             return;
61948         }
61949         for(var i = startRow; i <= endRow; i++){
61950             this.deselectRow(i, preventViewNotify);
61951         }
61952     },
61953
61954     /**
61955      * Selects a row.
61956      * @param {Number} row The index of the row to select
61957      * @param {Boolean} keepExisting (optional) True to keep existing selections
61958      */
61959     selectRow : function(index, keepExisting, preventViewNotify){
61960         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61961             return;
61962         }
61963         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61964             if(!keepExisting || this.singleSelect){
61965                 this.clearSelections();
61966             }
61967             var r = this.grid.ds.getAt(index);
61968             this.selections.add(r);
61969             this.last = this.lastActive = index;
61970             if(!preventViewNotify){
61971                 var view = this.grid.view ? this.grid.view : this.grid;
61972                 view.onRowSelect(index);
61973             }
61974             this.fireEvent("rowselect", this, index, r);
61975             this.fireEvent("selectionchange", this);
61976         }
61977     },
61978
61979     /**
61980      * Deselects a row.
61981      * @param {Number} row The index of the row to deselect
61982      */
61983     deselectRow : function(index, preventViewNotify){
61984         if(this.locked) {
61985             return;
61986         }
61987         if(this.last == index){
61988             this.last = false;
61989         }
61990         if(this.lastActive == index){
61991             this.lastActive = false;
61992         }
61993         var r = this.grid.ds.getAt(index);
61994         this.selections.remove(r);
61995         if(!preventViewNotify){
61996             var view = this.grid.view ? this.grid.view : this.grid;
61997             view.onRowDeselect(index);
61998         }
61999         this.fireEvent("rowdeselect", this, index);
62000         this.fireEvent("selectionchange", this);
62001     },
62002
62003     // private
62004     restoreLast : function(){
62005         if(this._last){
62006             this.last = this._last;
62007         }
62008     },
62009
62010     // private
62011     acceptsNav : function(row, col, cm){
62012         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62013     },
62014
62015     // private
62016     onEditorKey : function(field, e){
62017         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62018         if(k == e.TAB){
62019             e.stopEvent();
62020             ed.completeEdit();
62021             if(e.shiftKey){
62022                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62023             }else{
62024                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62025             }
62026         }else if(k == e.ENTER && !e.ctrlKey){
62027             e.stopEvent();
62028             ed.completeEdit();
62029             if(e.shiftKey){
62030                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62031             }else{
62032                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62033             }
62034         }else if(k == e.ESC){
62035             ed.cancelEdit();
62036         }
62037         if(newCell){
62038             g.startEditing(newCell[0], newCell[1]);
62039         }
62040     }
62041 });/*
62042  * Based on:
62043  * Ext JS Library 1.1.1
62044  * Copyright(c) 2006-2007, Ext JS, LLC.
62045  *
62046  * Originally Released Under LGPL - original licence link has changed is not relivant.
62047  *
62048  * Fork - LGPL
62049  * <script type="text/javascript">
62050  */
62051 /**
62052  * @class Roo.grid.CellSelectionModel
62053  * @extends Roo.grid.AbstractSelectionModel
62054  * This class provides the basic implementation for cell selection in a grid.
62055  * @constructor
62056  * @param {Object} config The object containing the configuration of this model.
62057  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62058  */
62059 Roo.grid.CellSelectionModel = function(config){
62060     Roo.apply(this, config);
62061
62062     this.selection = null;
62063
62064     this.addEvents({
62065         /**
62066              * @event beforerowselect
62067              * Fires before a cell is selected.
62068              * @param {SelectionModel} this
62069              * @param {Number} rowIndex The selected row index
62070              * @param {Number} colIndex The selected cell index
62071              */
62072             "beforecellselect" : true,
62073         /**
62074              * @event cellselect
62075              * Fires when a cell is selected.
62076              * @param {SelectionModel} this
62077              * @param {Number} rowIndex The selected row index
62078              * @param {Number} colIndex The selected cell index
62079              */
62080             "cellselect" : true,
62081         /**
62082              * @event selectionchange
62083              * Fires when the active selection changes.
62084              * @param {SelectionModel} this
62085              * @param {Object} selection null for no selection or an object (o) with two properties
62086                 <ul>
62087                 <li>o.record: the record object for the row the selection is in</li>
62088                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62089                 </ul>
62090              */
62091             "selectionchange" : true,
62092         /**
62093              * @event tabend
62094              * Fires when the tab (or enter) was pressed on the last editable cell
62095              * You can use this to trigger add new row.
62096              * @param {SelectionModel} this
62097              */
62098             "tabend" : true,
62099          /**
62100              * @event beforeeditnext
62101              * Fires before the next editable sell is made active
62102              * You can use this to skip to another cell or fire the tabend
62103              *    if you set cell to false
62104              * @param {Object} eventdata object : { cell : [ row, col ] } 
62105              */
62106             "beforeeditnext" : true
62107     });
62108     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62109 };
62110
62111 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62112     
62113     enter_is_tab: false,
62114
62115     /** @ignore */
62116     initEvents : function(){
62117         this.grid.on("mousedown", this.handleMouseDown, this);
62118         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62119         var view = this.grid.view;
62120         view.on("refresh", this.onViewChange, this);
62121         view.on("rowupdated", this.onRowUpdated, this);
62122         view.on("beforerowremoved", this.clearSelections, this);
62123         view.on("beforerowsinserted", this.clearSelections, this);
62124         if(this.grid.isEditor){
62125             this.grid.on("beforeedit", this.beforeEdit,  this);
62126         }
62127     },
62128
62129         //private
62130     beforeEdit : function(e){
62131         this.select(e.row, e.column, false, true, e.record);
62132     },
62133
62134         //private
62135     onRowUpdated : function(v, index, r){
62136         if(this.selection && this.selection.record == r){
62137             v.onCellSelect(index, this.selection.cell[1]);
62138         }
62139     },
62140
62141         //private
62142     onViewChange : function(){
62143         this.clearSelections(true);
62144     },
62145
62146         /**
62147          * Returns the currently selected cell,.
62148          * @return {Array} The selected cell (row, column) or null if none selected.
62149          */
62150     getSelectedCell : function(){
62151         return this.selection ? this.selection.cell : null;
62152     },
62153
62154     /**
62155      * Clears all selections.
62156      * @param {Boolean} true to prevent the gridview from being notified about the change.
62157      */
62158     clearSelections : function(preventNotify){
62159         var s = this.selection;
62160         if(s){
62161             if(preventNotify !== true){
62162                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62163             }
62164             this.selection = null;
62165             this.fireEvent("selectionchange", this, null);
62166         }
62167     },
62168
62169     /**
62170      * Returns true if there is a selection.
62171      * @return {Boolean}
62172      */
62173     hasSelection : function(){
62174         return this.selection ? true : false;
62175     },
62176
62177     /** @ignore */
62178     handleMouseDown : function(e, t){
62179         var v = this.grid.getView();
62180         if(this.isLocked()){
62181             return;
62182         };
62183         var row = v.findRowIndex(t);
62184         var cell = v.findCellIndex(t);
62185         if(row !== false && cell !== false){
62186             this.select(row, cell);
62187         }
62188     },
62189
62190     /**
62191      * Selects a cell.
62192      * @param {Number} rowIndex
62193      * @param {Number} collIndex
62194      */
62195     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62196         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62197             this.clearSelections();
62198             r = r || this.grid.dataSource.getAt(rowIndex);
62199             this.selection = {
62200                 record : r,
62201                 cell : [rowIndex, colIndex]
62202             };
62203             if(!preventViewNotify){
62204                 var v = this.grid.getView();
62205                 v.onCellSelect(rowIndex, colIndex);
62206                 if(preventFocus !== true){
62207                     v.focusCell(rowIndex, colIndex);
62208                 }
62209             }
62210             this.fireEvent("cellselect", this, rowIndex, colIndex);
62211             this.fireEvent("selectionchange", this, this.selection);
62212         }
62213     },
62214
62215         //private
62216     isSelectable : function(rowIndex, colIndex, cm){
62217         return !cm.isHidden(colIndex);
62218     },
62219
62220     /** @ignore */
62221     handleKeyDown : function(e){
62222         //Roo.log('Cell Sel Model handleKeyDown');
62223         if(!e.isNavKeyPress()){
62224             return;
62225         }
62226         var g = this.grid, s = this.selection;
62227         if(!s){
62228             e.stopEvent();
62229             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62230             if(cell){
62231                 this.select(cell[0], cell[1]);
62232             }
62233             return;
62234         }
62235         var sm = this;
62236         var walk = function(row, col, step){
62237             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62238         };
62239         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62240         var newCell;
62241
62242       
62243
62244         switch(k){
62245             case e.TAB:
62246                 // handled by onEditorKey
62247                 if (g.isEditor && g.editing) {
62248                     return;
62249                 }
62250                 if(e.shiftKey) {
62251                     newCell = walk(r, c-1, -1);
62252                 } else {
62253                     newCell = walk(r, c+1, 1);
62254                 }
62255                 break;
62256             
62257             case e.DOWN:
62258                newCell = walk(r+1, c, 1);
62259                 break;
62260             
62261             case e.UP:
62262                 newCell = walk(r-1, c, -1);
62263                 break;
62264             
62265             case e.RIGHT:
62266                 newCell = walk(r, c+1, 1);
62267                 break;
62268             
62269             case e.LEFT:
62270                 newCell = walk(r, c-1, -1);
62271                 break;
62272             
62273             case e.ENTER:
62274                 
62275                 if(g.isEditor && !g.editing){
62276                    g.startEditing(r, c);
62277                    e.stopEvent();
62278                    return;
62279                 }
62280                 
62281                 
62282              break;
62283         };
62284         if(newCell){
62285             this.select(newCell[0], newCell[1]);
62286             e.stopEvent();
62287             
62288         }
62289     },
62290
62291     acceptsNav : function(row, col, cm){
62292         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62293     },
62294     /**
62295      * Selects a cell.
62296      * @param {Number} field (not used) - as it's normally used as a listener
62297      * @param {Number} e - event - fake it by using
62298      *
62299      * var e = Roo.EventObjectImpl.prototype;
62300      * e.keyCode = e.TAB
62301      *
62302      * 
62303      */
62304     onEditorKey : function(field, e){
62305         
62306         var k = e.getKey(),
62307             newCell,
62308             g = this.grid,
62309             ed = g.activeEditor,
62310             forward = false;
62311         ///Roo.log('onEditorKey' + k);
62312         
62313         
62314         if (this.enter_is_tab && k == e.ENTER) {
62315             k = e.TAB;
62316         }
62317         
62318         if(k == e.TAB){
62319             if(e.shiftKey){
62320                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62321             }else{
62322                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62323                 forward = true;
62324             }
62325             
62326             e.stopEvent();
62327             
62328         } else if(k == e.ENTER &&  !e.ctrlKey){
62329             ed.completeEdit();
62330             e.stopEvent();
62331             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62332         
62333                 } else if(k == e.ESC){
62334             ed.cancelEdit();
62335         }
62336                 
62337         if (newCell) {
62338             var ecall = { cell : newCell, forward : forward };
62339             this.fireEvent('beforeeditnext', ecall );
62340             newCell = ecall.cell;
62341                         forward = ecall.forward;
62342         }
62343                 
62344         if(newCell){
62345             //Roo.log('next cell after edit');
62346             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62347         } else if (forward) {
62348             // tabbed past last
62349             this.fireEvent.defer(100, this, ['tabend',this]);
62350         }
62351     }
62352 });/*
62353  * Based on:
62354  * Ext JS Library 1.1.1
62355  * Copyright(c) 2006-2007, Ext JS, LLC.
62356  *
62357  * Originally Released Under LGPL - original licence link has changed is not relivant.
62358  *
62359  * Fork - LGPL
62360  * <script type="text/javascript">
62361  */
62362  
62363 /**
62364  * @class Roo.grid.EditorGrid
62365  * @extends Roo.grid.Grid
62366  * Class for creating and editable grid.
62367  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62368  * The container MUST have some type of size defined for the grid to fill. The container will be 
62369  * automatically set to position relative if it isn't already.
62370  * @param {Object} dataSource The data model to bind to
62371  * @param {Object} colModel The column model with info about this grid's columns
62372  */
62373 Roo.grid.EditorGrid = function(container, config){
62374     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62375     this.getGridEl().addClass("xedit-grid");
62376
62377     if(!this.selModel){
62378         this.selModel = new Roo.grid.CellSelectionModel();
62379     }
62380
62381     this.activeEditor = null;
62382
62383         this.addEvents({
62384             /**
62385              * @event beforeedit
62386              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62387              * <ul style="padding:5px;padding-left:16px;">
62388              * <li>grid - This grid</li>
62389              * <li>record - The record being edited</li>
62390              * <li>field - The field name being edited</li>
62391              * <li>value - The value for the field being edited.</li>
62392              * <li>row - The grid row index</li>
62393              * <li>column - The grid column index</li>
62394              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62395              * </ul>
62396              * @param {Object} e An edit event (see above for description)
62397              */
62398             "beforeedit" : true,
62399             /**
62400              * @event afteredit
62401              * Fires after a cell is edited. <br />
62402              * <ul style="padding:5px;padding-left:16px;">
62403              * <li>grid - This grid</li>
62404              * <li>record - The record being edited</li>
62405              * <li>field - The field name being edited</li>
62406              * <li>value - The value being set</li>
62407              * <li>originalValue - The original value for the field, before the edit.</li>
62408              * <li>row - The grid row index</li>
62409              * <li>column - The grid column index</li>
62410              * </ul>
62411              * @param {Object} e An edit event (see above for description)
62412              */
62413             "afteredit" : true,
62414             /**
62415              * @event validateedit
62416              * Fires after a cell is edited, but before the value is set in the record. 
62417          * You can use this to modify the value being set in the field, Return false
62418              * to cancel the change. The edit event object has the following properties <br />
62419              * <ul style="padding:5px;padding-left:16px;">
62420          * <li>editor - This editor</li>
62421              * <li>grid - This grid</li>
62422              * <li>record - The record being edited</li>
62423              * <li>field - The field name being edited</li>
62424              * <li>value - The value being set</li>
62425              * <li>originalValue - The original value for the field, before the edit.</li>
62426              * <li>row - The grid row index</li>
62427              * <li>column - The grid column index</li>
62428              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62429              * </ul>
62430              * @param {Object} e An edit event (see above for description)
62431              */
62432             "validateedit" : true
62433         });
62434     this.on("bodyscroll", this.stopEditing,  this);
62435     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62436 };
62437
62438 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62439     /**
62440      * @cfg {Number} clicksToEdit
62441      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62442      */
62443     clicksToEdit: 2,
62444
62445     // private
62446     isEditor : true,
62447     // private
62448     trackMouseOver: false, // causes very odd FF errors
62449
62450     onCellDblClick : function(g, row, col){
62451         this.startEditing(row, col);
62452     },
62453
62454     onEditComplete : function(ed, value, startValue){
62455         this.editing = false;
62456         this.activeEditor = null;
62457         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62458         var r = ed.record;
62459         var field = this.colModel.getDataIndex(ed.col);
62460         var e = {
62461             grid: this,
62462             record: r,
62463             field: field,
62464             originalValue: startValue,
62465             value: value,
62466             row: ed.row,
62467             column: ed.col,
62468             cancel:false,
62469             editor: ed
62470         };
62471         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62472         cell.show();
62473           
62474         if(String(value) !== String(startValue)){
62475             
62476             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62477                 r.set(field, e.value);
62478                 // if we are dealing with a combo box..
62479                 // then we also set the 'name' colum to be the displayField
62480                 if (ed.field.displayField && ed.field.name) {
62481                     r.set(ed.field.name, ed.field.el.dom.value);
62482                 }
62483                 
62484                 delete e.cancel; //?? why!!!
62485                 this.fireEvent("afteredit", e);
62486             }
62487         } else {
62488             this.fireEvent("afteredit", e); // always fire it!
62489         }
62490         this.view.focusCell(ed.row, ed.col);
62491     },
62492
62493     /**
62494      * Starts editing the specified for the specified row/column
62495      * @param {Number} rowIndex
62496      * @param {Number} colIndex
62497      */
62498     startEditing : function(row, col){
62499         this.stopEditing();
62500         if(this.colModel.isCellEditable(col, row)){
62501             this.view.ensureVisible(row, col, true);
62502           
62503             var r = this.dataSource.getAt(row);
62504             var field = this.colModel.getDataIndex(col);
62505             var cell = Roo.get(this.view.getCell(row,col));
62506             var e = {
62507                 grid: this,
62508                 record: r,
62509                 field: field,
62510                 value: r.data[field],
62511                 row: row,
62512                 column: col,
62513                 cancel:false 
62514             };
62515             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62516                 this.editing = true;
62517                 var ed = this.colModel.getCellEditor(col, row);
62518                 
62519                 if (!ed) {
62520                     return;
62521                 }
62522                 if(!ed.rendered){
62523                     ed.render(ed.parentEl || document.body);
62524                 }
62525                 ed.field.reset();
62526                
62527                 cell.hide();
62528                 
62529                 (function(){ // complex but required for focus issues in safari, ie and opera
62530                     ed.row = row;
62531                     ed.col = col;
62532                     ed.record = r;
62533                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62534                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62535                     this.activeEditor = ed;
62536                     var v = r.data[field];
62537                     ed.startEdit(this.view.getCell(row, col), v);
62538                     // combo's with 'displayField and name set
62539                     if (ed.field.displayField && ed.field.name) {
62540                         ed.field.el.dom.value = r.data[ed.field.name];
62541                     }
62542                     
62543                     
62544                 }).defer(50, this);
62545             }
62546         }
62547     },
62548         
62549     /**
62550      * Stops any active editing
62551      */
62552     stopEditing : function(){
62553         if(this.activeEditor){
62554             this.activeEditor.completeEdit();
62555         }
62556         this.activeEditor = null;
62557     },
62558         
62559          /**
62560      * Called to get grid's drag proxy text, by default returns this.ddText.
62561      * @return {String}
62562      */
62563     getDragDropText : function(){
62564         var count = this.selModel.getSelectedCell() ? 1 : 0;
62565         return String.format(this.ddText, count, count == 1 ? '' : 's');
62566     }
62567         
62568 });/*
62569  * Based on:
62570  * Ext JS Library 1.1.1
62571  * Copyright(c) 2006-2007, Ext JS, LLC.
62572  *
62573  * Originally Released Under LGPL - original licence link has changed is not relivant.
62574  *
62575  * Fork - LGPL
62576  * <script type="text/javascript">
62577  */
62578
62579 // private - not really -- you end up using it !
62580 // This is a support class used internally by the Grid components
62581
62582 /**
62583  * @class Roo.grid.GridEditor
62584  * @extends Roo.Editor
62585  * Class for creating and editable grid elements.
62586  * @param {Object} config any settings (must include field)
62587  */
62588 Roo.grid.GridEditor = function(field, config){
62589     if (!config && field.field) {
62590         config = field;
62591         field = Roo.factory(config.field, Roo.form);
62592     }
62593     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62594     field.monitorTab = false;
62595 };
62596
62597 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62598     
62599     /**
62600      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62601      */
62602     
62603     alignment: "tl-tl",
62604     autoSize: "width",
62605     hideEl : false,
62606     cls: "x-small-editor x-grid-editor",
62607     shim:false,
62608     shadow:"frame"
62609 });/*
62610  * Based on:
62611  * Ext JS Library 1.1.1
62612  * Copyright(c) 2006-2007, Ext JS, LLC.
62613  *
62614  * Originally Released Under LGPL - original licence link has changed is not relivant.
62615  *
62616  * Fork - LGPL
62617  * <script type="text/javascript">
62618  */
62619   
62620
62621   
62622 Roo.grid.PropertyRecord = Roo.data.Record.create([
62623     {name:'name',type:'string'},  'value'
62624 ]);
62625
62626
62627 Roo.grid.PropertyStore = function(grid, source){
62628     this.grid = grid;
62629     this.store = new Roo.data.Store({
62630         recordType : Roo.grid.PropertyRecord
62631     });
62632     this.store.on('update', this.onUpdate,  this);
62633     if(source){
62634         this.setSource(source);
62635     }
62636     Roo.grid.PropertyStore.superclass.constructor.call(this);
62637 };
62638
62639
62640
62641 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62642     setSource : function(o){
62643         this.source = o;
62644         this.store.removeAll();
62645         var data = [];
62646         for(var k in o){
62647             if(this.isEditableValue(o[k])){
62648                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62649             }
62650         }
62651         this.store.loadRecords({records: data}, {}, true);
62652     },
62653
62654     onUpdate : function(ds, record, type){
62655         if(type == Roo.data.Record.EDIT){
62656             var v = record.data['value'];
62657             var oldValue = record.modified['value'];
62658             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62659                 this.source[record.id] = v;
62660                 record.commit();
62661                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62662             }else{
62663                 record.reject();
62664             }
62665         }
62666     },
62667
62668     getProperty : function(row){
62669        return this.store.getAt(row);
62670     },
62671
62672     isEditableValue: function(val){
62673         if(val && val instanceof Date){
62674             return true;
62675         }else if(typeof val == 'object' || typeof val == 'function'){
62676             return false;
62677         }
62678         return true;
62679     },
62680
62681     setValue : function(prop, value){
62682         this.source[prop] = value;
62683         this.store.getById(prop).set('value', value);
62684     },
62685
62686     getSource : function(){
62687         return this.source;
62688     }
62689 });
62690
62691 Roo.grid.PropertyColumnModel = function(grid, store){
62692     this.grid = grid;
62693     var g = Roo.grid;
62694     g.PropertyColumnModel.superclass.constructor.call(this, [
62695         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62696         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62697     ]);
62698     this.store = store;
62699     this.bselect = Roo.DomHelper.append(document.body, {
62700         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62701             {tag: 'option', value: 'true', html: 'true'},
62702             {tag: 'option', value: 'false', html: 'false'}
62703         ]
62704     });
62705     Roo.id(this.bselect);
62706     var f = Roo.form;
62707     this.editors = {
62708         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62709         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62710         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62711         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62712         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62713     };
62714     this.renderCellDelegate = this.renderCell.createDelegate(this);
62715     this.renderPropDelegate = this.renderProp.createDelegate(this);
62716 };
62717
62718 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62719     
62720     
62721     nameText : 'Name',
62722     valueText : 'Value',
62723     
62724     dateFormat : 'm/j/Y',
62725     
62726     
62727     renderDate : function(dateVal){
62728         return dateVal.dateFormat(this.dateFormat);
62729     },
62730
62731     renderBool : function(bVal){
62732         return bVal ? 'true' : 'false';
62733     },
62734
62735     isCellEditable : function(colIndex, rowIndex){
62736         return colIndex == 1;
62737     },
62738
62739     getRenderer : function(col){
62740         return col == 1 ?
62741             this.renderCellDelegate : this.renderPropDelegate;
62742     },
62743
62744     renderProp : function(v){
62745         return this.getPropertyName(v);
62746     },
62747
62748     renderCell : function(val){
62749         var rv = val;
62750         if(val instanceof Date){
62751             rv = this.renderDate(val);
62752         }else if(typeof val == 'boolean'){
62753             rv = this.renderBool(val);
62754         }
62755         return Roo.util.Format.htmlEncode(rv);
62756     },
62757
62758     getPropertyName : function(name){
62759         var pn = this.grid.propertyNames;
62760         return pn && pn[name] ? pn[name] : name;
62761     },
62762
62763     getCellEditor : function(colIndex, rowIndex){
62764         var p = this.store.getProperty(rowIndex);
62765         var n = p.data['name'], val = p.data['value'];
62766         
62767         if(typeof(this.grid.customEditors[n]) == 'string'){
62768             return this.editors[this.grid.customEditors[n]];
62769         }
62770         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62771             return this.grid.customEditors[n];
62772         }
62773         if(val instanceof Date){
62774             return this.editors['date'];
62775         }else if(typeof val == 'number'){
62776             return this.editors['number'];
62777         }else if(typeof val == 'boolean'){
62778             return this.editors['boolean'];
62779         }else{
62780             return this.editors['string'];
62781         }
62782     }
62783 });
62784
62785 /**
62786  * @class Roo.grid.PropertyGrid
62787  * @extends Roo.grid.EditorGrid
62788  * This class represents the  interface of a component based property grid control.
62789  * <br><br>Usage:<pre><code>
62790  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62791       
62792  });
62793  // set any options
62794  grid.render();
62795  * </code></pre>
62796   
62797  * @constructor
62798  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62799  * The container MUST have some type of size defined for the grid to fill. The container will be
62800  * automatically set to position relative if it isn't already.
62801  * @param {Object} config A config object that sets properties on this grid.
62802  */
62803 Roo.grid.PropertyGrid = function(container, config){
62804     config = config || {};
62805     var store = new Roo.grid.PropertyStore(this);
62806     this.store = store;
62807     var cm = new Roo.grid.PropertyColumnModel(this, store);
62808     store.store.sort('name', 'ASC');
62809     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62810         ds: store.store,
62811         cm: cm,
62812         enableColLock:false,
62813         enableColumnMove:false,
62814         stripeRows:false,
62815         trackMouseOver: false,
62816         clicksToEdit:1
62817     }, config));
62818     this.getGridEl().addClass('x-props-grid');
62819     this.lastEditRow = null;
62820     this.on('columnresize', this.onColumnResize, this);
62821     this.addEvents({
62822          /**
62823              * @event beforepropertychange
62824              * Fires before a property changes (return false to stop?)
62825              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62826              * @param {String} id Record Id
62827              * @param {String} newval New Value
62828          * @param {String} oldval Old Value
62829              */
62830         "beforepropertychange": true,
62831         /**
62832              * @event propertychange
62833              * Fires after a property changes
62834              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62835              * @param {String} id Record Id
62836              * @param {String} newval New Value
62837          * @param {String} oldval Old Value
62838              */
62839         "propertychange": true
62840     });
62841     this.customEditors = this.customEditors || {};
62842 };
62843 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62844     
62845      /**
62846      * @cfg {Object} customEditors map of colnames=> custom editors.
62847      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62848      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62849      * false disables editing of the field.
62850          */
62851     
62852       /**
62853      * @cfg {Object} propertyNames map of property Names to their displayed value
62854          */
62855     
62856     render : function(){
62857         Roo.grid.PropertyGrid.superclass.render.call(this);
62858         this.autoSize.defer(100, this);
62859     },
62860
62861     autoSize : function(){
62862         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62863         if(this.view){
62864             this.view.fitColumns();
62865         }
62866     },
62867
62868     onColumnResize : function(){
62869         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62870         this.autoSize();
62871     },
62872     /**
62873      * Sets the data for the Grid
62874      * accepts a Key => Value object of all the elements avaiable.
62875      * @param {Object} data  to appear in grid.
62876      */
62877     setSource : function(source){
62878         this.store.setSource(source);
62879         //this.autoSize();
62880     },
62881     /**
62882      * Gets all the data from the grid.
62883      * @return {Object} data  data stored in grid
62884      */
62885     getSource : function(){
62886         return this.store.getSource();
62887     }
62888 });/*
62889   
62890  * Licence LGPL
62891  
62892  */
62893  
62894 /**
62895  * @class Roo.grid.Calendar
62896  * @extends Roo.grid.Grid
62897  * This class extends the Grid to provide a calendar widget
62898  * <br><br>Usage:<pre><code>
62899  var grid = new Roo.grid.Calendar("my-container-id", {
62900      ds: myDataStore,
62901      cm: myColModel,
62902      selModel: mySelectionModel,
62903      autoSizeColumns: true,
62904      monitorWindowResize: false,
62905      trackMouseOver: true
62906      eventstore : real data store..
62907  });
62908  // set any options
62909  grid.render();
62910   
62911   * @constructor
62912  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62913  * The container MUST have some type of size defined for the grid to fill. The container will be
62914  * automatically set to position relative if it isn't already.
62915  * @param {Object} config A config object that sets properties on this grid.
62916  */
62917 Roo.grid.Calendar = function(container, config){
62918         // initialize the container
62919         this.container = Roo.get(container);
62920         this.container.update("");
62921         this.container.setStyle("overflow", "hidden");
62922     this.container.addClass('x-grid-container');
62923
62924     this.id = this.container.id;
62925
62926     Roo.apply(this, config);
62927     // check and correct shorthanded configs
62928     
62929     var rows = [];
62930     var d =1;
62931     for (var r = 0;r < 6;r++) {
62932         
62933         rows[r]=[];
62934         for (var c =0;c < 7;c++) {
62935             rows[r][c]= '';
62936         }
62937     }
62938     if (this.eventStore) {
62939         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62940         this.eventStore.on('load',this.onLoad, this);
62941         this.eventStore.on('beforeload',this.clearEvents, this);
62942          
62943     }
62944     
62945     this.dataSource = new Roo.data.Store({
62946             proxy: new Roo.data.MemoryProxy(rows),
62947             reader: new Roo.data.ArrayReader({}, [
62948                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62949     });
62950
62951     this.dataSource.load();
62952     this.ds = this.dataSource;
62953     this.ds.xmodule = this.xmodule || false;
62954     
62955     
62956     var cellRender = function(v,x,r)
62957     {
62958         return String.format(
62959             '<div class="fc-day  fc-widget-content"><div>' +
62960                 '<div class="fc-event-container"></div>' +
62961                 '<div class="fc-day-number">{0}</div>'+
62962                 
62963                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62964             '</div></div>', v);
62965     
62966     }
62967     
62968     
62969     this.colModel = new Roo.grid.ColumnModel( [
62970         {
62971             xtype: 'ColumnModel',
62972             xns: Roo.grid,
62973             dataIndex : 'weekday0',
62974             header : 'Sunday',
62975             renderer : cellRender
62976         },
62977         {
62978             xtype: 'ColumnModel',
62979             xns: Roo.grid,
62980             dataIndex : 'weekday1',
62981             header : 'Monday',
62982             renderer : cellRender
62983         },
62984         {
62985             xtype: 'ColumnModel',
62986             xns: Roo.grid,
62987             dataIndex : 'weekday2',
62988             header : 'Tuesday',
62989             renderer : cellRender
62990         },
62991         {
62992             xtype: 'ColumnModel',
62993             xns: Roo.grid,
62994             dataIndex : 'weekday3',
62995             header : 'Wednesday',
62996             renderer : cellRender
62997         },
62998         {
62999             xtype: 'ColumnModel',
63000             xns: Roo.grid,
63001             dataIndex : 'weekday4',
63002             header : 'Thursday',
63003             renderer : cellRender
63004         },
63005         {
63006             xtype: 'ColumnModel',
63007             xns: Roo.grid,
63008             dataIndex : 'weekday5',
63009             header : 'Friday',
63010             renderer : cellRender
63011         },
63012         {
63013             xtype: 'ColumnModel',
63014             xns: Roo.grid,
63015             dataIndex : 'weekday6',
63016             header : 'Saturday',
63017             renderer : cellRender
63018         }
63019     ]);
63020     this.cm = this.colModel;
63021     this.cm.xmodule = this.xmodule || false;
63022  
63023         
63024           
63025     //this.selModel = new Roo.grid.CellSelectionModel();
63026     //this.sm = this.selModel;
63027     //this.selModel.init(this);
63028     
63029     
63030     if(this.width){
63031         this.container.setWidth(this.width);
63032     }
63033
63034     if(this.height){
63035         this.container.setHeight(this.height);
63036     }
63037     /** @private */
63038         this.addEvents({
63039         // raw events
63040         /**
63041          * @event click
63042          * The raw click event for the entire grid.
63043          * @param {Roo.EventObject} e
63044          */
63045         "click" : true,
63046         /**
63047          * @event dblclick
63048          * The raw dblclick event for the entire grid.
63049          * @param {Roo.EventObject} e
63050          */
63051         "dblclick" : true,
63052         /**
63053          * @event contextmenu
63054          * The raw contextmenu event for the entire grid.
63055          * @param {Roo.EventObject} e
63056          */
63057         "contextmenu" : true,
63058         /**
63059          * @event mousedown
63060          * The raw mousedown event for the entire grid.
63061          * @param {Roo.EventObject} e
63062          */
63063         "mousedown" : true,
63064         /**
63065          * @event mouseup
63066          * The raw mouseup event for the entire grid.
63067          * @param {Roo.EventObject} e
63068          */
63069         "mouseup" : true,
63070         /**
63071          * @event mouseover
63072          * The raw mouseover event for the entire grid.
63073          * @param {Roo.EventObject} e
63074          */
63075         "mouseover" : true,
63076         /**
63077          * @event mouseout
63078          * The raw mouseout event for the entire grid.
63079          * @param {Roo.EventObject} e
63080          */
63081         "mouseout" : true,
63082         /**
63083          * @event keypress
63084          * The raw keypress event for the entire grid.
63085          * @param {Roo.EventObject} e
63086          */
63087         "keypress" : true,
63088         /**
63089          * @event keydown
63090          * The raw keydown event for the entire grid.
63091          * @param {Roo.EventObject} e
63092          */
63093         "keydown" : true,
63094
63095         // custom events
63096
63097         /**
63098          * @event cellclick
63099          * Fires when a cell is clicked
63100          * @param {Grid} this
63101          * @param {Number} rowIndex
63102          * @param {Number} columnIndex
63103          * @param {Roo.EventObject} e
63104          */
63105         "cellclick" : true,
63106         /**
63107          * @event celldblclick
63108          * Fires when a cell is double clicked
63109          * @param {Grid} this
63110          * @param {Number} rowIndex
63111          * @param {Number} columnIndex
63112          * @param {Roo.EventObject} e
63113          */
63114         "celldblclick" : true,
63115         /**
63116          * @event rowclick
63117          * Fires when a row is clicked
63118          * @param {Grid} this
63119          * @param {Number} rowIndex
63120          * @param {Roo.EventObject} e
63121          */
63122         "rowclick" : true,
63123         /**
63124          * @event rowdblclick
63125          * Fires when a row is double clicked
63126          * @param {Grid} this
63127          * @param {Number} rowIndex
63128          * @param {Roo.EventObject} e
63129          */
63130         "rowdblclick" : true,
63131         /**
63132          * @event headerclick
63133          * Fires when a header is clicked
63134          * @param {Grid} this
63135          * @param {Number} columnIndex
63136          * @param {Roo.EventObject} e
63137          */
63138         "headerclick" : true,
63139         /**
63140          * @event headerdblclick
63141          * Fires when a header cell is double clicked
63142          * @param {Grid} this
63143          * @param {Number} columnIndex
63144          * @param {Roo.EventObject} e
63145          */
63146         "headerdblclick" : true,
63147         /**
63148          * @event rowcontextmenu
63149          * Fires when a row is right clicked
63150          * @param {Grid} this
63151          * @param {Number} rowIndex
63152          * @param {Roo.EventObject} e
63153          */
63154         "rowcontextmenu" : true,
63155         /**
63156          * @event cellcontextmenu
63157          * Fires when a cell is right clicked
63158          * @param {Grid} this
63159          * @param {Number} rowIndex
63160          * @param {Number} cellIndex
63161          * @param {Roo.EventObject} e
63162          */
63163          "cellcontextmenu" : true,
63164         /**
63165          * @event headercontextmenu
63166          * Fires when a header is right clicked
63167          * @param {Grid} this
63168          * @param {Number} columnIndex
63169          * @param {Roo.EventObject} e
63170          */
63171         "headercontextmenu" : true,
63172         /**
63173          * @event bodyscroll
63174          * Fires when the body element is scrolled
63175          * @param {Number} scrollLeft
63176          * @param {Number} scrollTop
63177          */
63178         "bodyscroll" : true,
63179         /**
63180          * @event columnresize
63181          * Fires when the user resizes a column
63182          * @param {Number} columnIndex
63183          * @param {Number} newSize
63184          */
63185         "columnresize" : true,
63186         /**
63187          * @event columnmove
63188          * Fires when the user moves a column
63189          * @param {Number} oldIndex
63190          * @param {Number} newIndex
63191          */
63192         "columnmove" : true,
63193         /**
63194          * @event startdrag
63195          * Fires when row(s) start being dragged
63196          * @param {Grid} this
63197          * @param {Roo.GridDD} dd The drag drop object
63198          * @param {event} e The raw browser event
63199          */
63200         "startdrag" : true,
63201         /**
63202          * @event enddrag
63203          * Fires when a drag operation is complete
63204          * @param {Grid} this
63205          * @param {Roo.GridDD} dd The drag drop object
63206          * @param {event} e The raw browser event
63207          */
63208         "enddrag" : true,
63209         /**
63210          * @event dragdrop
63211          * Fires when dragged row(s) are dropped on a valid DD target
63212          * @param {Grid} this
63213          * @param {Roo.GridDD} dd The drag drop object
63214          * @param {String} targetId The target drag drop object
63215          * @param {event} e The raw browser event
63216          */
63217         "dragdrop" : true,
63218         /**
63219          * @event dragover
63220          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63221          * @param {Grid} this
63222          * @param {Roo.GridDD} dd The drag drop object
63223          * @param {String} targetId The target drag drop object
63224          * @param {event} e The raw browser event
63225          */
63226         "dragover" : true,
63227         /**
63228          * @event dragenter
63229          *  Fires when the dragged row(s) first cross another DD target while being dragged
63230          * @param {Grid} this
63231          * @param {Roo.GridDD} dd The drag drop object
63232          * @param {String} targetId The target drag drop object
63233          * @param {event} e The raw browser event
63234          */
63235         "dragenter" : true,
63236         /**
63237          * @event dragout
63238          * Fires when the dragged row(s) leave another DD target while being dragged
63239          * @param {Grid} this
63240          * @param {Roo.GridDD} dd The drag drop object
63241          * @param {String} targetId The target drag drop object
63242          * @param {event} e The raw browser event
63243          */
63244         "dragout" : true,
63245         /**
63246          * @event rowclass
63247          * Fires when a row is rendered, so you can change add a style to it.
63248          * @param {GridView} gridview   The grid view
63249          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63250          */
63251         'rowclass' : true,
63252
63253         /**
63254          * @event render
63255          * Fires when the grid is rendered
63256          * @param {Grid} grid
63257          */
63258         'render' : true,
63259             /**
63260              * @event select
63261              * Fires when a date is selected
63262              * @param {DatePicker} this
63263              * @param {Date} date The selected date
63264              */
63265         'select': true,
63266         /**
63267              * @event monthchange
63268              * Fires when the displayed month changes 
63269              * @param {DatePicker} this
63270              * @param {Date} date The selected month
63271              */
63272         'monthchange': true,
63273         /**
63274              * @event evententer
63275              * Fires when mouse over an event
63276              * @param {Calendar} this
63277              * @param {event} Event
63278              */
63279         'evententer': true,
63280         /**
63281              * @event eventleave
63282              * Fires when the mouse leaves an
63283              * @param {Calendar} this
63284              * @param {event}
63285              */
63286         'eventleave': true,
63287         /**
63288              * @event eventclick
63289              * Fires when the mouse click an
63290              * @param {Calendar} this
63291              * @param {event}
63292              */
63293         'eventclick': true,
63294         /**
63295              * @event eventrender
63296              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63297              * @param {Calendar} this
63298              * @param {data} data to be modified
63299              */
63300         'eventrender': true
63301         
63302     });
63303
63304     Roo.grid.Grid.superclass.constructor.call(this);
63305     this.on('render', function() {
63306         this.view.el.addClass('x-grid-cal'); 
63307         
63308         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63309
63310     },this);
63311     
63312     if (!Roo.grid.Calendar.style) {
63313         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63314             
63315             
63316             '.x-grid-cal .x-grid-col' :  {
63317                 height: 'auto !important',
63318                 'vertical-align': 'top'
63319             },
63320             '.x-grid-cal  .fc-event-hori' : {
63321                 height: '14px'
63322             }
63323              
63324             
63325         }, Roo.id());
63326     }
63327
63328     
63329     
63330 };
63331 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63332     /**
63333      * @cfg {Store} eventStore The store that loads events.
63334      */
63335     eventStore : 25,
63336
63337      
63338     activeDate : false,
63339     startDay : 0,
63340     autoWidth : true,
63341     monitorWindowResize : false,
63342
63343     
63344     resizeColumns : function() {
63345         var col = (this.view.el.getWidth() / 7) - 3;
63346         // loop through cols, and setWidth
63347         for(var i =0 ; i < 7 ; i++){
63348             this.cm.setColumnWidth(i, col);
63349         }
63350     },
63351      setDate :function(date) {
63352         
63353         Roo.log('setDate?');
63354         
63355         this.resizeColumns();
63356         var vd = this.activeDate;
63357         this.activeDate = date;
63358 //        if(vd && this.el){
63359 //            var t = date.getTime();
63360 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63361 //                Roo.log('using add remove');
63362 //                
63363 //                this.fireEvent('monthchange', this, date);
63364 //                
63365 //                this.cells.removeClass("fc-state-highlight");
63366 //                this.cells.each(function(c){
63367 //                   if(c.dateValue == t){
63368 //                       c.addClass("fc-state-highlight");
63369 //                       setTimeout(function(){
63370 //                            try{c.dom.firstChild.focus();}catch(e){}
63371 //                       }, 50);
63372 //                       return false;
63373 //                   }
63374 //                   return true;
63375 //                });
63376 //                return;
63377 //            }
63378 //        }
63379         
63380         var days = date.getDaysInMonth();
63381         
63382         var firstOfMonth = date.getFirstDateOfMonth();
63383         var startingPos = firstOfMonth.getDay()-this.startDay;
63384         
63385         if(startingPos < this.startDay){
63386             startingPos += 7;
63387         }
63388         
63389         var pm = date.add(Date.MONTH, -1);
63390         var prevStart = pm.getDaysInMonth()-startingPos;
63391 //        
63392         
63393         
63394         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63395         
63396         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63397         //this.cells.addClassOnOver('fc-state-hover');
63398         
63399         var cells = this.cells.elements;
63400         var textEls = this.textNodes;
63401         
63402         //Roo.each(cells, function(cell){
63403         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63404         //});
63405         
63406         days += startingPos;
63407
63408         // convert everything to numbers so it's fast
63409         var day = 86400000;
63410         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63411         //Roo.log(d);
63412         //Roo.log(pm);
63413         //Roo.log(prevStart);
63414         
63415         var today = new Date().clearTime().getTime();
63416         var sel = date.clearTime().getTime();
63417         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63418         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63419         var ddMatch = this.disabledDatesRE;
63420         var ddText = this.disabledDatesText;
63421         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63422         var ddaysText = this.disabledDaysText;
63423         var format = this.format;
63424         
63425         var setCellClass = function(cal, cell){
63426             
63427             //Roo.log('set Cell Class');
63428             cell.title = "";
63429             var t = d.getTime();
63430             
63431             //Roo.log(d);
63432             
63433             
63434             cell.dateValue = t;
63435             if(t == today){
63436                 cell.className += " fc-today";
63437                 cell.className += " fc-state-highlight";
63438                 cell.title = cal.todayText;
63439             }
63440             if(t == sel){
63441                 // disable highlight in other month..
63442                 cell.className += " fc-state-highlight";
63443                 
63444             }
63445             // disabling
63446             if(t < min) {
63447                 //cell.className = " fc-state-disabled";
63448                 cell.title = cal.minText;
63449                 return;
63450             }
63451             if(t > max) {
63452                 //cell.className = " fc-state-disabled";
63453                 cell.title = cal.maxText;
63454                 return;
63455             }
63456             if(ddays){
63457                 if(ddays.indexOf(d.getDay()) != -1){
63458                     // cell.title = ddaysText;
63459                    // cell.className = " fc-state-disabled";
63460                 }
63461             }
63462             if(ddMatch && format){
63463                 var fvalue = d.dateFormat(format);
63464                 if(ddMatch.test(fvalue)){
63465                     cell.title = ddText.replace("%0", fvalue);
63466                    cell.className = " fc-state-disabled";
63467                 }
63468             }
63469             
63470             if (!cell.initialClassName) {
63471                 cell.initialClassName = cell.dom.className;
63472             }
63473             
63474             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63475         };
63476
63477         var i = 0;
63478         
63479         for(; i < startingPos; i++) {
63480             cells[i].dayName =  (++prevStart);
63481             Roo.log(textEls[i]);
63482             d.setDate(d.getDate()+1);
63483             
63484             //cells[i].className = "fc-past fc-other-month";
63485             setCellClass(this, cells[i]);
63486         }
63487         
63488         var intDay = 0;
63489         
63490         for(; i < days; i++){
63491             intDay = i - startingPos + 1;
63492             cells[i].dayName =  (intDay);
63493             d.setDate(d.getDate()+1);
63494             
63495             cells[i].className = ''; // "x-date-active";
63496             setCellClass(this, cells[i]);
63497         }
63498         var extraDays = 0;
63499         
63500         for(; i < 42; i++) {
63501             //textEls[i].innerHTML = (++extraDays);
63502             
63503             d.setDate(d.getDate()+1);
63504             cells[i].dayName = (++extraDays);
63505             cells[i].className = "fc-future fc-other-month";
63506             setCellClass(this, cells[i]);
63507         }
63508         
63509         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63510         
63511         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63512         
63513         // this will cause all the cells to mis
63514         var rows= [];
63515         var i =0;
63516         for (var r = 0;r < 6;r++) {
63517             for (var c =0;c < 7;c++) {
63518                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63519             }    
63520         }
63521         
63522         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63523         for(i=0;i<cells.length;i++) {
63524             
63525             this.cells.elements[i].dayName = cells[i].dayName ;
63526             this.cells.elements[i].className = cells[i].className;
63527             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63528             this.cells.elements[i].title = cells[i].title ;
63529             this.cells.elements[i].dateValue = cells[i].dateValue ;
63530         }
63531         
63532         
63533         
63534         
63535         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63536         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63537         
63538         ////if(totalRows != 6){
63539             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63540            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63541        // }
63542         
63543         this.fireEvent('monthchange', this, date);
63544         
63545         
63546     },
63547  /**
63548      * Returns the grid's SelectionModel.
63549      * @return {SelectionModel}
63550      */
63551     getSelectionModel : function(){
63552         if(!this.selModel){
63553             this.selModel = new Roo.grid.CellSelectionModel();
63554         }
63555         return this.selModel;
63556     },
63557
63558     load: function() {
63559         this.eventStore.load()
63560         
63561         
63562         
63563     },
63564     
63565     findCell : function(dt) {
63566         dt = dt.clearTime().getTime();
63567         var ret = false;
63568         this.cells.each(function(c){
63569             //Roo.log("check " +c.dateValue + '?=' + dt);
63570             if(c.dateValue == dt){
63571                 ret = c;
63572                 return false;
63573             }
63574             return true;
63575         });
63576         
63577         return ret;
63578     },
63579     
63580     findCells : function(rec) {
63581         var s = rec.data.start_dt.clone().clearTime().getTime();
63582        // Roo.log(s);
63583         var e= rec.data.end_dt.clone().clearTime().getTime();
63584        // Roo.log(e);
63585         var ret = [];
63586         this.cells.each(function(c){
63587              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63588             
63589             if(c.dateValue > e){
63590                 return ;
63591             }
63592             if(c.dateValue < s){
63593                 return ;
63594             }
63595             ret.push(c);
63596         });
63597         
63598         return ret;    
63599     },
63600     
63601     findBestRow: function(cells)
63602     {
63603         var ret = 0;
63604         
63605         for (var i =0 ; i < cells.length;i++) {
63606             ret  = Math.max(cells[i].rows || 0,ret);
63607         }
63608         return ret;
63609         
63610     },
63611     
63612     
63613     addItem : function(rec)
63614     {
63615         // look for vertical location slot in
63616         var cells = this.findCells(rec);
63617         
63618         rec.row = this.findBestRow(cells);
63619         
63620         // work out the location.
63621         
63622         var crow = false;
63623         var rows = [];
63624         for(var i =0; i < cells.length; i++) {
63625             if (!crow) {
63626                 crow = {
63627                     start : cells[i],
63628                     end :  cells[i]
63629                 };
63630                 continue;
63631             }
63632             if (crow.start.getY() == cells[i].getY()) {
63633                 // on same row.
63634                 crow.end = cells[i];
63635                 continue;
63636             }
63637             // different row.
63638             rows.push(crow);
63639             crow = {
63640                 start: cells[i],
63641                 end : cells[i]
63642             };
63643             
63644         }
63645         
63646         rows.push(crow);
63647         rec.els = [];
63648         rec.rows = rows;
63649         rec.cells = cells;
63650         for (var i = 0; i < cells.length;i++) {
63651             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63652             
63653         }
63654         
63655         
63656     },
63657     
63658     clearEvents: function() {
63659         
63660         if (!this.eventStore.getCount()) {
63661             return;
63662         }
63663         // reset number of rows in cells.
63664         Roo.each(this.cells.elements, function(c){
63665             c.rows = 0;
63666         });
63667         
63668         this.eventStore.each(function(e) {
63669             this.clearEvent(e);
63670         },this);
63671         
63672     },
63673     
63674     clearEvent : function(ev)
63675     {
63676         if (ev.els) {
63677             Roo.each(ev.els, function(el) {
63678                 el.un('mouseenter' ,this.onEventEnter, this);
63679                 el.un('mouseleave' ,this.onEventLeave, this);
63680                 el.remove();
63681             },this);
63682             ev.els = [];
63683         }
63684     },
63685     
63686     
63687     renderEvent : function(ev,ctr) {
63688         if (!ctr) {
63689              ctr = this.view.el.select('.fc-event-container',true).first();
63690         }
63691         
63692          
63693         this.clearEvent(ev);
63694             //code
63695        
63696         
63697         
63698         ev.els = [];
63699         var cells = ev.cells;
63700         var rows = ev.rows;
63701         this.fireEvent('eventrender', this, ev);
63702         
63703         for(var i =0; i < rows.length; i++) {
63704             
63705             cls = '';
63706             if (i == 0) {
63707                 cls += ' fc-event-start';
63708             }
63709             if ((i+1) == rows.length) {
63710                 cls += ' fc-event-end';
63711             }
63712             
63713             //Roo.log(ev.data);
63714             // how many rows should it span..
63715             var cg = this.eventTmpl.append(ctr,Roo.apply({
63716                 fccls : cls
63717                 
63718             }, ev.data) , true);
63719             
63720             
63721             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63722             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63723             cg.on('click', this.onEventClick, this, ev);
63724             
63725             ev.els.push(cg);
63726             
63727             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63728             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63729             //Roo.log(cg);
63730              
63731             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63732             cg.setWidth(ebox.right - sbox.x -2);
63733         }
63734     },
63735     
63736     renderEvents: function()
63737     {   
63738         // first make sure there is enough space..
63739         
63740         if (!this.eventTmpl) {
63741             this.eventTmpl = new Roo.Template(
63742                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63743                     '<div class="fc-event-inner">' +
63744                         '<span class="fc-event-time">{time}</span>' +
63745                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63746                     '</div>' +
63747                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63748                 '</div>'
63749             );
63750                 
63751         }
63752                
63753         
63754         
63755         this.cells.each(function(c) {
63756             //Roo.log(c.select('.fc-day-content div',true).first());
63757             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63758         });
63759         
63760         var ctr = this.view.el.select('.fc-event-container',true).first();
63761         
63762         var cls;
63763         this.eventStore.each(function(ev){
63764             
63765             this.renderEvent(ev);
63766              
63767              
63768         }, this);
63769         this.view.layout();
63770         
63771     },
63772     
63773     onEventEnter: function (e, el,event,d) {
63774         this.fireEvent('evententer', this, el, event);
63775     },
63776     
63777     onEventLeave: function (e, el,event,d) {
63778         this.fireEvent('eventleave', this, el, event);
63779     },
63780     
63781     onEventClick: function (e, el,event,d) {
63782         this.fireEvent('eventclick', this, el, event);
63783     },
63784     
63785     onMonthChange: function () {
63786         this.store.load();
63787     },
63788     
63789     onLoad: function () {
63790         
63791         //Roo.log('calendar onload');
63792 //         
63793         if(this.eventStore.getCount() > 0){
63794             
63795            
63796             
63797             this.eventStore.each(function(d){
63798                 
63799                 
63800                 // FIXME..
63801                 var add =   d.data;
63802                 if (typeof(add.end_dt) == 'undefined')  {
63803                     Roo.log("Missing End time in calendar data: ");
63804                     Roo.log(d);
63805                     return;
63806                 }
63807                 if (typeof(add.start_dt) == 'undefined')  {
63808                     Roo.log("Missing Start time in calendar data: ");
63809                     Roo.log(d);
63810                     return;
63811                 }
63812                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63813                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63814                 add.id = add.id || d.id;
63815                 add.title = add.title || '??';
63816                 
63817                 this.addItem(d);
63818                 
63819              
63820             },this);
63821         }
63822         
63823         this.renderEvents();
63824     }
63825     
63826
63827 });
63828 /*
63829  grid : {
63830                 xtype: 'Grid',
63831                 xns: Roo.grid,
63832                 listeners : {
63833                     render : function ()
63834                     {
63835                         _this.grid = this;
63836                         
63837                         if (!this.view.el.hasClass('course-timesheet')) {
63838                             this.view.el.addClass('course-timesheet');
63839                         }
63840                         if (this.tsStyle) {
63841                             this.ds.load({});
63842                             return; 
63843                         }
63844                         Roo.log('width');
63845                         Roo.log(_this.grid.view.el.getWidth());
63846                         
63847                         
63848                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63849                             '.course-timesheet .x-grid-row' : {
63850                                 height: '80px'
63851                             },
63852                             '.x-grid-row td' : {
63853                                 'vertical-align' : 0
63854                             },
63855                             '.course-edit-link' : {
63856                                 'color' : 'blue',
63857                                 'text-overflow' : 'ellipsis',
63858                                 'overflow' : 'hidden',
63859                                 'white-space' : 'nowrap',
63860                                 'cursor' : 'pointer'
63861                             },
63862                             '.sub-link' : {
63863                                 'color' : 'green'
63864                             },
63865                             '.de-act-sup-link' : {
63866                                 'color' : 'purple',
63867                                 'text-decoration' : 'line-through'
63868                             },
63869                             '.de-act-link' : {
63870                                 'color' : 'red',
63871                                 'text-decoration' : 'line-through'
63872                             },
63873                             '.course-timesheet .course-highlight' : {
63874                                 'border-top-style': 'dashed !important',
63875                                 'border-bottom-bottom': 'dashed !important'
63876                             },
63877                             '.course-timesheet .course-item' : {
63878                                 'font-family'   : 'tahoma, arial, helvetica',
63879                                 'font-size'     : '11px',
63880                                 'overflow'      : 'hidden',
63881                                 'padding-left'  : '10px',
63882                                 'padding-right' : '10px',
63883                                 'padding-top' : '10px' 
63884                             }
63885                             
63886                         }, Roo.id());
63887                                 this.ds.load({});
63888                     }
63889                 },
63890                 autoWidth : true,
63891                 monitorWindowResize : false,
63892                 cellrenderer : function(v,x,r)
63893                 {
63894                     return v;
63895                 },
63896                 sm : {
63897                     xtype: 'CellSelectionModel',
63898                     xns: Roo.grid
63899                 },
63900                 dataSource : {
63901                     xtype: 'Store',
63902                     xns: Roo.data,
63903                     listeners : {
63904                         beforeload : function (_self, options)
63905                         {
63906                             options.params = options.params || {};
63907                             options.params._month = _this.monthField.getValue();
63908                             options.params.limit = 9999;
63909                             options.params['sort'] = 'when_dt';    
63910                             options.params['dir'] = 'ASC';    
63911                             this.proxy.loadResponse = this.loadResponse;
63912                             Roo.log("load?");
63913                             //this.addColumns();
63914                         },
63915                         load : function (_self, records, options)
63916                         {
63917                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63918                                 // if you click on the translation.. you can edit it...
63919                                 var el = Roo.get(this);
63920                                 var id = el.dom.getAttribute('data-id');
63921                                 var d = el.dom.getAttribute('data-date');
63922                                 var t = el.dom.getAttribute('data-time');
63923                                 //var id = this.child('span').dom.textContent;
63924                                 
63925                                 //Roo.log(this);
63926                                 Pman.Dialog.CourseCalendar.show({
63927                                     id : id,
63928                                     when_d : d,
63929                                     when_t : t,
63930                                     productitem_active : id ? 1 : 0
63931                                 }, function() {
63932                                     _this.grid.ds.load({});
63933                                 });
63934                            
63935                            });
63936                            
63937                            _this.panel.fireEvent('resize', [ '', '' ]);
63938                         }
63939                     },
63940                     loadResponse : function(o, success, response){
63941                             // this is overridden on before load..
63942                             
63943                             Roo.log("our code?");       
63944                             //Roo.log(success);
63945                             //Roo.log(response)
63946                             delete this.activeRequest;
63947                             if(!success){
63948                                 this.fireEvent("loadexception", this, o, response);
63949                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63950                                 return;
63951                             }
63952                             var result;
63953                             try {
63954                                 result = o.reader.read(response);
63955                             }catch(e){
63956                                 Roo.log("load exception?");
63957                                 this.fireEvent("loadexception", this, o, response, e);
63958                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63959                                 return;
63960                             }
63961                             Roo.log("ready...");        
63962                             // loop through result.records;
63963                             // and set this.tdate[date] = [] << array of records..
63964                             _this.tdata  = {};
63965                             Roo.each(result.records, function(r){
63966                                 //Roo.log(r.data);
63967                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63968                                     _this.tdata[r.data.when_dt.format('j')] = [];
63969                                 }
63970                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63971                             });
63972                             
63973                             //Roo.log(_this.tdata);
63974                             
63975                             result.records = [];
63976                             result.totalRecords = 6;
63977                     
63978                             // let's generate some duumy records for the rows.
63979                             //var st = _this.dateField.getValue();
63980                             
63981                             // work out monday..
63982                             //st = st.add(Date.DAY, -1 * st.format('w'));
63983                             
63984                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63985                             
63986                             var firstOfMonth = date.getFirstDayOfMonth();
63987                             var days = date.getDaysInMonth();
63988                             var d = 1;
63989                             var firstAdded = false;
63990                             for (var i = 0; i < result.totalRecords ; i++) {
63991                                 //var d= st.add(Date.DAY, i);
63992                                 var row = {};
63993                                 var added = 0;
63994                                 for(var w = 0 ; w < 7 ; w++){
63995                                     if(!firstAdded && firstOfMonth != w){
63996                                         continue;
63997                                     }
63998                                     if(d > days){
63999                                         continue;
64000                                     }
64001                                     firstAdded = true;
64002                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64003                                     row['weekday'+w] = String.format(
64004                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64005                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64006                                                     d,
64007                                                     date.format('Y-m-')+dd
64008                                                 );
64009                                     added++;
64010                                     if(typeof(_this.tdata[d]) != 'undefined'){
64011                                         Roo.each(_this.tdata[d], function(r){
64012                                             var is_sub = '';
64013                                             var deactive = '';
64014                                             var id = r.id;
64015                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64016                                             if(r.parent_id*1>0){
64017                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64018                                                 id = r.parent_id;
64019                                             }
64020                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64021                                                 deactive = 'de-act-link';
64022                                             }
64023                                             
64024                                             row['weekday'+w] += String.format(
64025                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64026                                                     id, //0
64027                                                     r.product_id_name, //1
64028                                                     r.when_dt.format('h:ia'), //2
64029                                                     is_sub, //3
64030                                                     deactive, //4
64031                                                     desc // 5
64032                                             );
64033                                         });
64034                                     }
64035                                     d++;
64036                                 }
64037                                 
64038                                 // only do this if something added..
64039                                 if(added > 0){ 
64040                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64041                                 }
64042                                 
64043                                 
64044                                 // push it twice. (second one with an hour..
64045                                 
64046                             }
64047                             //Roo.log(result);
64048                             this.fireEvent("load", this, o, o.request.arg);
64049                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64050                         },
64051                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64052                     proxy : {
64053                         xtype: 'HttpProxy',
64054                         xns: Roo.data,
64055                         method : 'GET',
64056                         url : baseURL + '/Roo/Shop_course.php'
64057                     },
64058                     reader : {
64059                         xtype: 'JsonReader',
64060                         xns: Roo.data,
64061                         id : 'id',
64062                         fields : [
64063                             {
64064                                 'name': 'id',
64065                                 'type': 'int'
64066                             },
64067                             {
64068                                 'name': 'when_dt',
64069                                 'type': 'string'
64070                             },
64071                             {
64072                                 'name': 'end_dt',
64073                                 'type': 'string'
64074                             },
64075                             {
64076                                 'name': 'parent_id',
64077                                 'type': 'int'
64078                             },
64079                             {
64080                                 'name': 'product_id',
64081                                 'type': 'int'
64082                             },
64083                             {
64084                                 'name': 'productitem_id',
64085                                 'type': 'int'
64086                             },
64087                             {
64088                                 'name': 'guid',
64089                                 'type': 'int'
64090                             }
64091                         ]
64092                     }
64093                 },
64094                 toolbar : {
64095                     xtype: 'Toolbar',
64096                     xns: Roo,
64097                     items : [
64098                         {
64099                             xtype: 'Button',
64100                             xns: Roo.Toolbar,
64101                             listeners : {
64102                                 click : function (_self, e)
64103                                 {
64104                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64105                                     sd.setMonth(sd.getMonth()-1);
64106                                     _this.monthField.setValue(sd.format('Y-m-d'));
64107                                     _this.grid.ds.load({});
64108                                 }
64109                             },
64110                             text : "Back"
64111                         },
64112                         {
64113                             xtype: 'Separator',
64114                             xns: Roo.Toolbar
64115                         },
64116                         {
64117                             xtype: 'MonthField',
64118                             xns: Roo.form,
64119                             listeners : {
64120                                 render : function (_self)
64121                                 {
64122                                     _this.monthField = _self;
64123                                    // _this.monthField.set  today
64124                                 },
64125                                 select : function (combo, date)
64126                                 {
64127                                     _this.grid.ds.load({});
64128                                 }
64129                             },
64130                             value : (function() { return new Date(); })()
64131                         },
64132                         {
64133                             xtype: 'Separator',
64134                             xns: Roo.Toolbar
64135                         },
64136                         {
64137                             xtype: 'TextItem',
64138                             xns: Roo.Toolbar,
64139                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64140                         },
64141                         {
64142                             xtype: 'Fill',
64143                             xns: Roo.Toolbar
64144                         },
64145                         {
64146                             xtype: 'Button',
64147                             xns: Roo.Toolbar,
64148                             listeners : {
64149                                 click : function (_self, e)
64150                                 {
64151                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64152                                     sd.setMonth(sd.getMonth()+1);
64153                                     _this.monthField.setValue(sd.format('Y-m-d'));
64154                                     _this.grid.ds.load({});
64155                                 }
64156                             },
64157                             text : "Next"
64158                         }
64159                     ]
64160                 },
64161                  
64162             }
64163         };
64164         
64165         *//*
64166  * Based on:
64167  * Ext JS Library 1.1.1
64168  * Copyright(c) 2006-2007, Ext JS, LLC.
64169  *
64170  * Originally Released Under LGPL - original licence link has changed is not relivant.
64171  *
64172  * Fork - LGPL
64173  * <script type="text/javascript">
64174  */
64175  
64176 /**
64177  * @class Roo.LoadMask
64178  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64179  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64180  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64181  * element's UpdateManager load indicator and will be destroyed after the initial load.
64182  * @constructor
64183  * Create a new LoadMask
64184  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64185  * @param {Object} config The config object
64186  */
64187 Roo.LoadMask = function(el, config){
64188     this.el = Roo.get(el);
64189     Roo.apply(this, config);
64190     if(this.store){
64191         this.store.on('beforeload', this.onBeforeLoad, this);
64192         this.store.on('load', this.onLoad, this);
64193         this.store.on('loadexception', this.onLoadException, this);
64194         this.removeMask = false;
64195     }else{
64196         var um = this.el.getUpdateManager();
64197         um.showLoadIndicator = false; // disable the default indicator
64198         um.on('beforeupdate', this.onBeforeLoad, this);
64199         um.on('update', this.onLoad, this);
64200         um.on('failure', this.onLoad, this);
64201         this.removeMask = true;
64202     }
64203 };
64204
64205 Roo.LoadMask.prototype = {
64206     /**
64207      * @cfg {Boolean} removeMask
64208      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64209      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64210      */
64211     removeMask : false,
64212     /**
64213      * @cfg {String} msg
64214      * The text to display in a centered loading message box (defaults to 'Loading...')
64215      */
64216     msg : 'Loading...',
64217     /**
64218      * @cfg {String} msgCls
64219      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64220      */
64221     msgCls : 'x-mask-loading',
64222
64223     /**
64224      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64225      * @type Boolean
64226      */
64227     disabled: false,
64228
64229     /**
64230      * Disables the mask to prevent it from being displayed
64231      */
64232     disable : function(){
64233        this.disabled = true;
64234     },
64235
64236     /**
64237      * Enables the mask so that it can be displayed
64238      */
64239     enable : function(){
64240         this.disabled = false;
64241     },
64242     
64243     onLoadException : function()
64244     {
64245         Roo.log(arguments);
64246         
64247         if (typeof(arguments[3]) != 'undefined') {
64248             Roo.MessageBox.alert("Error loading",arguments[3]);
64249         } 
64250         /*
64251         try {
64252             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64253                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64254             }   
64255         } catch(e) {
64256             
64257         }
64258         */
64259     
64260         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64261     },
64262     // private
64263     onLoad : function()
64264     {
64265         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64266     },
64267
64268     // private
64269     onBeforeLoad : function(){
64270         if(!this.disabled){
64271             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64272         }
64273     },
64274
64275     // private
64276     destroy : function(){
64277         if(this.store){
64278             this.store.un('beforeload', this.onBeforeLoad, this);
64279             this.store.un('load', this.onLoad, this);
64280             this.store.un('loadexception', this.onLoadException, this);
64281         }else{
64282             var um = this.el.getUpdateManager();
64283             um.un('beforeupdate', this.onBeforeLoad, this);
64284             um.un('update', this.onLoad, this);
64285             um.un('failure', this.onLoad, this);
64286         }
64287     }
64288 };/*
64289  * Based on:
64290  * Ext JS Library 1.1.1
64291  * Copyright(c) 2006-2007, Ext JS, LLC.
64292  *
64293  * Originally Released Under LGPL - original licence link has changed is not relivant.
64294  *
64295  * Fork - LGPL
64296  * <script type="text/javascript">
64297  */
64298
64299
64300 /**
64301  * @class Roo.XTemplate
64302  * @extends Roo.Template
64303  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64304 <pre><code>
64305 var t = new Roo.XTemplate(
64306         '&lt;select name="{name}"&gt;',
64307                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64308         '&lt;/select&gt;'
64309 );
64310  
64311 // then append, applying the master template values
64312  </code></pre>
64313  *
64314  * Supported features:
64315  *
64316  *  Tags:
64317
64318 <pre><code>
64319       {a_variable} - output encoded.
64320       {a_variable.format:("Y-m-d")} - call a method on the variable
64321       {a_variable:raw} - unencoded output
64322       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64323       {a_variable:this.method_on_template(...)} - call a method on the template object.
64324  
64325 </code></pre>
64326  *  The tpl tag:
64327 <pre><code>
64328         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64329         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64330         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64331         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64332   
64333         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64334         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64335 </code></pre>
64336  *      
64337  */
64338 Roo.XTemplate = function()
64339 {
64340     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64341     if (this.html) {
64342         this.compile();
64343     }
64344 };
64345
64346
64347 Roo.extend(Roo.XTemplate, Roo.Template, {
64348
64349     /**
64350      * The various sub templates
64351      */
64352     tpls : false,
64353     /**
64354      *
64355      * basic tag replacing syntax
64356      * WORD:WORD()
64357      *
64358      * // you can fake an object call by doing this
64359      *  x.t:(test,tesT) 
64360      * 
64361      */
64362     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64363
64364     /**
64365      * compile the template
64366      *
64367      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64368      *
64369      */
64370     compile: function()
64371     {
64372         var s = this.html;
64373      
64374         s = ['<tpl>', s, '</tpl>'].join('');
64375     
64376         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64377             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64378             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64379             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64380             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64381             m,
64382             id     = 0,
64383             tpls   = [];
64384     
64385         while(true == !!(m = s.match(re))){
64386             var forMatch   = m[0].match(nameRe),
64387                 ifMatch   = m[0].match(ifRe),
64388                 execMatch   = m[0].match(execRe),
64389                 namedMatch   = m[0].match(namedRe),
64390                 
64391                 exp  = null, 
64392                 fn   = null,
64393                 exec = null,
64394                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64395                 
64396             if (ifMatch) {
64397                 // if - puts fn into test..
64398                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64399                 if(exp){
64400                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64401                 }
64402             }
64403             
64404             if (execMatch) {
64405                 // exec - calls a function... returns empty if true is  returned.
64406                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64407                 if(exp){
64408                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64409                 }
64410             }
64411             
64412             
64413             if (name) {
64414                 // for = 
64415                 switch(name){
64416                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64417                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64418                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64419                 }
64420             }
64421             var uid = namedMatch ? namedMatch[1] : id;
64422             
64423             
64424             tpls.push({
64425                 id:     namedMatch ? namedMatch[1] : id,
64426                 target: name,
64427                 exec:   exec,
64428                 test:   fn,
64429                 body:   m[1] || ''
64430             });
64431             if (namedMatch) {
64432                 s = s.replace(m[0], '');
64433             } else { 
64434                 s = s.replace(m[0], '{xtpl'+ id + '}');
64435             }
64436             ++id;
64437         }
64438         this.tpls = [];
64439         for(var i = tpls.length-1; i >= 0; --i){
64440             this.compileTpl(tpls[i]);
64441             this.tpls[tpls[i].id] = tpls[i];
64442         }
64443         this.master = tpls[tpls.length-1];
64444         return this;
64445     },
64446     /**
64447      * same as applyTemplate, except it's done to one of the subTemplates
64448      * when using named templates, you can do:
64449      *
64450      * var str = pl.applySubTemplate('your-name', values);
64451      *
64452      * 
64453      * @param {Number} id of the template
64454      * @param {Object} values to apply to template
64455      * @param {Object} parent (normaly the instance of this object)
64456      */
64457     applySubTemplate : function(id, values, parent)
64458     {
64459         
64460         
64461         var t = this.tpls[id];
64462         
64463         
64464         try { 
64465             if(t.test && !t.test.call(this, values, parent)){
64466                 return '';
64467             }
64468         } catch(e) {
64469             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64470             Roo.log(e.toString());
64471             Roo.log(t.test);
64472             return ''
64473         }
64474         try { 
64475             
64476             if(t.exec && t.exec.call(this, values, parent)){
64477                 return '';
64478             }
64479         } catch(e) {
64480             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64481             Roo.log(e.toString());
64482             Roo.log(t.exec);
64483             return ''
64484         }
64485         try {
64486             var vs = t.target ? t.target.call(this, values, parent) : values;
64487             parent = t.target ? values : parent;
64488             if(t.target && vs instanceof Array){
64489                 var buf = [];
64490                 for(var i = 0, len = vs.length; i < len; i++){
64491                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64492                 }
64493                 return buf.join('');
64494             }
64495             return t.compiled.call(this, vs, parent);
64496         } catch (e) {
64497             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64498             Roo.log(e.toString());
64499             Roo.log(t.compiled);
64500             return '';
64501         }
64502     },
64503
64504     compileTpl : function(tpl)
64505     {
64506         var fm = Roo.util.Format;
64507         var useF = this.disableFormats !== true;
64508         var sep = Roo.isGecko ? "+" : ",";
64509         var undef = function(str) {
64510             Roo.log("Property not found :"  + str);
64511             return '';
64512         };
64513         
64514         var fn = function(m, name, format, args)
64515         {
64516             //Roo.log(arguments);
64517             args = args ? args.replace(/\\'/g,"'") : args;
64518             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64519             if (typeof(format) == 'undefined') {
64520                 format= 'htmlEncode';
64521             }
64522             if (format == 'raw' ) {
64523                 format = false;
64524             }
64525             
64526             if(name.substr(0, 4) == 'xtpl'){
64527                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64528             }
64529             
64530             // build an array of options to determine if value is undefined..
64531             
64532             // basically get 'xxxx.yyyy' then do
64533             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64534             //    (function () { Roo.log("Property not found"); return ''; })() :
64535             //    ......
64536             
64537             var udef_ar = [];
64538             var lookfor = '';
64539             Roo.each(name.split('.'), function(st) {
64540                 lookfor += (lookfor.length ? '.': '') + st;
64541                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64542             });
64543             
64544             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64545             
64546             
64547             if(format && useF){
64548                 
64549                 args = args ? ',' + args : "";
64550                  
64551                 if(format.substr(0, 5) != "this."){
64552                     format = "fm." + format + '(';
64553                 }else{
64554                     format = 'this.call("'+ format.substr(5) + '", ';
64555                     args = ", values";
64556                 }
64557                 
64558                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64559             }
64560              
64561             if (args.length) {
64562                 // called with xxyx.yuu:(test,test)
64563                 // change to ()
64564                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64565             }
64566             // raw.. - :raw modifier..
64567             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64568             
64569         };
64570         var body;
64571         // branched to use + in gecko and [].join() in others
64572         if(Roo.isGecko){
64573             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64574                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64575                     "';};};";
64576         }else{
64577             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64578             body.push(tpl.body.replace(/(\r\n|\n)/g,
64579                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64580             body.push("'].join('');};};");
64581             body = body.join('');
64582         }
64583         
64584         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64585        
64586         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64587         eval(body);
64588         
64589         return this;
64590     },
64591
64592     applyTemplate : function(values){
64593         return this.master.compiled.call(this, values, {});
64594         //var s = this.subs;
64595     },
64596
64597     apply : function(){
64598         return this.applyTemplate.apply(this, arguments);
64599     }
64600
64601  });
64602
64603 Roo.XTemplate.from = function(el){
64604     el = Roo.getDom(el);
64605     return new Roo.XTemplate(el.value || el.innerHTML);
64606 };