roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /**
4901  * Originally based of this code... - refactored for Roo...
4902  * https://github.com/aaalsaleh/undo-manager
4903  
4904  * undo-manager.js
4905  * @author  Abdulrahman Alsaleh 
4906  * @copyright 2015 Abdulrahman Alsaleh 
4907  * @license  MIT License (c) 
4908  *
4909  * Hackily modifyed by alan@roojs.com
4910  *
4911  *
4912  *  
4913  *
4914  *  TOTALLY UNTESTED...
4915  *
4916  *  Documentation to be done....
4917  */
4918  
4919
4920 /**
4921 * @class Roo.lib.UndoManager
4922 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4923 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4924
4925  * Usage:
4926  * <pre><code>
4927
4928
4929 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4930  
4931 </code></pre>
4932
4933 * For more information see this blog post with examples:
4934 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4935      - Create Elements using DOM, HTML fragments and Templates</a>. 
4936 * @constructor
4937 * @param {Number} limit how far back to go ... use 1000?
4938 * @param {Object} scope usually use document..
4939 */
4940
4941 Roo.lib.UndoManager = function (limit, undoScopeHost)
4942 {
4943     this.stack = [];
4944     this.limit = limit;
4945     this.scope = undoScopeHost;
4946     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4947     if (this.fireEvent) {
4948         this.bindEvents();
4949     }
4950     
4951 };
4952         
4953 Roo.lib.UndoManager.prototype = {
4954     
4955     limit : false,
4956     stack : false,
4957     scope :  false,
4958     fireEvent : false,
4959     position : 0,
4960     length : 0,
4961     
4962     
4963      /**
4964      * To push and execute a transaction, the method undoManager.transact
4965      * must be called by passing a transaction object as the first argument, and a merge
4966      * flag as the second argument. A transaction object has the following properties:
4967      *
4968      * Usage:
4969 <pre><code>
4970 undoManager.transact({
4971     label: 'Typing',
4972     execute: function() { ... },
4973     undo: function() { ... },
4974     // redo same as execute
4975     redo: function() { this.execute(); }
4976 }, false);
4977
4978 // merge transaction
4979 undoManager.transact({
4980     label: 'Typing',
4981     execute: function() { ... },  // this will be run...
4982     undo: function() { ... }, // what to do when undo is run.
4983     // redo same as execute
4984     redo: function() { this.execute(); }
4985 }, true); 
4986 </code></pre> 
4987      *
4988      * 
4989      * @param {Object} transaction The transaction to add to the stack.
4990      * @return {String} The HTML fragment
4991      */
4992     
4993     
4994     transact : function (transaction, merge)
4995     {
4996         if (arguments.length < 2) {
4997             throw new TypeError('Not enough arguments to UndoManager.transact.');
4998         }
4999
5000         transaction.execute();
5001
5002         this.stack.splice(0, this.position);
5003         if (merge && this.length) {
5004             this.stack[0].push(transaction);
5005         } else {
5006             this.stack.unshift([transaction]);
5007         }
5008     
5009         this.position = 0;
5010
5011         if (this.limit && this.stack.length > this.limit) {
5012             this.length = this.stack.length = this.limit;
5013         } else {
5014             this.length = this.stack.length;
5015         }
5016
5017         if (this.fireEvent) {
5018             this.scope.dispatchEvent(
5019                 new CustomEvent('DOMTransaction', {
5020                     detail: {
5021                         transactions: this.stack[0].slice()
5022                     },
5023                     bubbles: true,
5024                     cancelable: false
5025                 })
5026             );
5027         }
5028     },
5029
5030     undo : function ()
5031     {
5032         if (this.position < this.length) {
5033             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5034                 this.stack[this.position][i].undo();
5035             }
5036             this.position++;
5037
5038             if (this.fireEvent) {
5039                 this.scope.dispatchEvent(
5040                     new CustomEvent('undo', {
5041                         detail: {
5042                             transactions: this.stack[this.position - 1].slice()
5043                         },
5044                         bubbles: true,
5045                         cancelable: false
5046                     })
5047                 );
5048             }
5049         }
5050     },
5051
5052     redo : function ()
5053     {
5054         if (this.position > 0) {
5055             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5056                 this.stack[this.position - 1][i].redo();
5057             }
5058             this.position--;
5059
5060             if (this.fireEvent) {
5061                 this.scope.dispatchEvent(
5062                     new CustomEvent('redo', {
5063                         detail: {
5064                             transactions: this.stack[this.position].slice()
5065                         },
5066                         bubbles: true,
5067                         cancelable: false
5068                     })
5069                 );
5070             }
5071         }
5072     },
5073
5074     item : function (index)
5075     {
5076         if (index >= 0 && index < this.length) {
5077             return this.stack[index].slice();
5078         }
5079         return null;
5080     },
5081
5082     clearUndo : function () {
5083         this.stack.length = this.length = this.position;
5084     },
5085
5086     clearRedo : function () {
5087         this.stack.splice(0, this.position);
5088         this.position = 0;
5089         this.length = this.stack.length;
5090     },
5091     /**
5092      * Reset the undo - probaly done on load to clear all history.
5093      */
5094     reset : function()
5095     {
5096         this.stack = [];
5097         this.position = 0;
5098         this.length = 0;
5099         this.current_html = this.scope.innerHTML;
5100         if (this.timer !== false) {
5101             clearTimeout(this.timer);
5102         }
5103         this.timer = false;
5104         this.merge = false;
5105         
5106     },
5107     current_html : '',
5108     timer : false,
5109     merge : false,
5110     
5111     
5112     // this will handle the undo/redo on the element.?
5113     bindEvents : function()
5114     {
5115         var el  = this.scope;
5116         el.undoManager = this;
5117         
5118         
5119         this.scope.addEventListener('keydown', function(e) {
5120             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5121                 if (e.shiftKey) {
5122                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5123                 } else {
5124                     el.undoManager.undo(); // Ctrl/Command + Z
5125                 }
5126         
5127                 e.preventDefault();
5128             }
5129         });
5130         
5131         
5132         var t = this;
5133         
5134         el.addEventListener('input', function(e) {
5135             if(el.innerHTML == t.current_html) {
5136                 return;
5137             }
5138             // only record events every second.
5139             if (t.timer !== false) {
5140                clearTimeout(t.timer);
5141                t.timer = false;
5142             }
5143             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5144             
5145             t.addEvent(t.merge);
5146             t.merge = true; // ignore changes happening every second..
5147         });
5148         },
5149     /**
5150      * Manually add an event.
5151      * Normall called without arguements - and it will just get added to the stack.
5152      * 
5153      */
5154     
5155     addEvent : function(merge)
5156     {
5157         // not sure if this should clear the timer 
5158         merge = typeof(merge) == 'undefined' ? false : merge; 
5159         
5160         el.undoManager.transact({
5161             oldHTML: this.current_html,
5162             newHTML: this.scope.innerHTML,
5163             // nothing to execute (content already changed when input is fired)
5164             execute: function() { },
5165             undo: function() {
5166                 this.scope.innerHTML = this.current_html = this.oldHTML;
5167             },
5168             redo: function() {
5169                 this.scope.innerHTML = this.current_html = this.newHTML;
5170             }
5171         }, merge);
5172         
5173         this.merge = merge;
5174         
5175         this.current_html = el.innerHTML;
5176     }
5177     
5178     
5179      
5180     
5181     
5182     
5183 };
5184 /*
5185  * Based on:
5186  * Ext JS Library 1.1.1
5187  * Copyright(c) 2006-2007, Ext JS, LLC.
5188  *
5189  * Originally Released Under LGPL - original licence link has changed is not relivant.
5190  *
5191  * Fork - LGPL
5192  * <script type="text/javascript">
5193  */
5194
5195
5196 // nasty IE9 hack - what a pile of crap that is..
5197
5198  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5199     Range.prototype.createContextualFragment = function (html) {
5200         var doc = window.document;
5201         var container = doc.createElement("div");
5202         container.innerHTML = html;
5203         var frag = doc.createDocumentFragment(), n;
5204         while ((n = container.firstChild)) {
5205             frag.appendChild(n);
5206         }
5207         return frag;
5208     };
5209 }
5210
5211 /**
5212  * @class Roo.DomHelper
5213  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5214  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
5215  * @static
5216  */
5217 Roo.DomHelper = function(){
5218     var tempTableEl = null;
5219     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5220     var tableRe = /^table|tbody|tr|td$/i;
5221     var xmlns = {};
5222     // build as innerHTML where available
5223     /** @ignore */
5224     var createHtml = function(o){
5225         if(typeof o == 'string'){
5226             return o;
5227         }
5228         var b = "";
5229         if(!o.tag){
5230             o.tag = "div";
5231         }
5232         b += "<" + o.tag;
5233         for(var attr in o){
5234             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5235             if(attr == "style"){
5236                 var s = o["style"];
5237                 if(typeof s == "function"){
5238                     s = s.call();
5239                 }
5240                 if(typeof s == "string"){
5241                     b += ' style="' + s + '"';
5242                 }else if(typeof s == "object"){
5243                     b += ' style="';
5244                     for(var key in s){
5245                         if(typeof s[key] != "function"){
5246                             b += key + ":" + s[key] + ";";
5247                         }
5248                     }
5249                     b += '"';
5250                 }
5251             }else{
5252                 if(attr == "cls"){
5253                     b += ' class="' + o["cls"] + '"';
5254                 }else if(attr == "htmlFor"){
5255                     b += ' for="' + o["htmlFor"] + '"';
5256                 }else{
5257                     b += " " + attr + '="' + o[attr] + '"';
5258                 }
5259             }
5260         }
5261         if(emptyTags.test(o.tag)){
5262             b += "/>";
5263         }else{
5264             b += ">";
5265             var cn = o.children || o.cn;
5266             if(cn){
5267                 //http://bugs.kde.org/show_bug.cgi?id=71506
5268                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5269                     for(var i = 0, len = cn.length; i < len; i++) {
5270                         b += createHtml(cn[i], b);
5271                     }
5272                 }else{
5273                     b += createHtml(cn, b);
5274                 }
5275             }
5276             if(o.html){
5277                 b += o.html;
5278             }
5279             b += "</" + o.tag + ">";
5280         }
5281         return b;
5282     };
5283
5284     // build as dom
5285     /** @ignore */
5286     var createDom = function(o, parentNode){
5287          
5288         // defininition craeted..
5289         var ns = false;
5290         if (o.ns && o.ns != 'html') {
5291                
5292             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5293                 xmlns[o.ns] = o.xmlns;
5294                 ns = o.xmlns;
5295             }
5296             if (typeof(xmlns[o.ns]) == 'undefined') {
5297                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5298             }
5299             ns = xmlns[o.ns];
5300         }
5301         
5302         
5303         if (typeof(o) == 'string') {
5304             return parentNode.appendChild(document.createTextNode(o));
5305         }
5306         o.tag = o.tag || div;
5307         if (o.ns && Roo.isIE) {
5308             ns = false;
5309             o.tag = o.ns + ':' + o.tag;
5310             
5311         }
5312         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5313         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5314         for(var attr in o){
5315             
5316             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5317                     attr == "style" || typeof o[attr] == "function") { continue; }
5318                     
5319             if(attr=="cls" && Roo.isIE){
5320                 el.className = o["cls"];
5321             }else{
5322                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5323                 else { 
5324                     el[attr] = o[attr];
5325                 }
5326             }
5327         }
5328         Roo.DomHelper.applyStyles(el, o.style);
5329         var cn = o.children || o.cn;
5330         if(cn){
5331             //http://bugs.kde.org/show_bug.cgi?id=71506
5332              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5333                 for(var i = 0, len = cn.length; i < len; i++) {
5334                     createDom(cn[i], el);
5335                 }
5336             }else{
5337                 createDom(cn, el);
5338             }
5339         }
5340         if(o.html){
5341             el.innerHTML = o.html;
5342         }
5343         if(parentNode){
5344            parentNode.appendChild(el);
5345         }
5346         return el;
5347     };
5348
5349     var ieTable = function(depth, s, h, e){
5350         tempTableEl.innerHTML = [s, h, e].join('');
5351         var i = -1, el = tempTableEl;
5352         while(++i < depth && el.firstChild){
5353             el = el.firstChild;
5354         }
5355         return el;
5356     };
5357
5358     // kill repeat to save bytes
5359     var ts = '<table>',
5360         te = '</table>',
5361         tbs = ts+'<tbody>',
5362         tbe = '</tbody>'+te,
5363         trs = tbs + '<tr>',
5364         tre = '</tr>'+tbe;
5365
5366     /**
5367      * @ignore
5368      * Nasty code for IE's broken table implementation
5369      */
5370     var insertIntoTable = function(tag, where, el, html){
5371         if(!tempTableEl){
5372             tempTableEl = document.createElement('div');
5373         }
5374         var node;
5375         var before = null;
5376         if(tag == 'td'){
5377             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5378                 return;
5379             }
5380             if(where == 'beforebegin'){
5381                 before = el;
5382                 el = el.parentNode;
5383             } else{
5384                 before = el.nextSibling;
5385                 el = el.parentNode;
5386             }
5387             node = ieTable(4, trs, html, tre);
5388         }
5389         else if(tag == 'tr'){
5390             if(where == 'beforebegin'){
5391                 before = el;
5392                 el = el.parentNode;
5393                 node = ieTable(3, tbs, html, tbe);
5394             } else if(where == 'afterend'){
5395                 before = el.nextSibling;
5396                 el = el.parentNode;
5397                 node = ieTable(3, tbs, html, tbe);
5398             } else{ // INTO a TR
5399                 if(where == 'afterbegin'){
5400                     before = el.firstChild;
5401                 }
5402                 node = ieTable(4, trs, html, tre);
5403             }
5404         } else if(tag == 'tbody'){
5405             if(where == 'beforebegin'){
5406                 before = el;
5407                 el = el.parentNode;
5408                 node = ieTable(2, ts, html, te);
5409             } else if(where == 'afterend'){
5410                 before = el.nextSibling;
5411                 el = el.parentNode;
5412                 node = ieTable(2, ts, html, te);
5413             } else{
5414                 if(where == 'afterbegin'){
5415                     before = el.firstChild;
5416                 }
5417                 node = ieTable(3, tbs, html, tbe);
5418             }
5419         } else{ // TABLE
5420             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5421                 return;
5422             }
5423             if(where == 'afterbegin'){
5424                 before = el.firstChild;
5425             }
5426             node = ieTable(2, ts, html, te);
5427         }
5428         el.insertBefore(node, before);
5429         return node;
5430     };
5431     
5432     // this is a bit like the react update code...
5433     // 
5434     
5435     var updateNode = function(from, to)
5436     {
5437         // should we handle non-standard elements?
5438         Roo.log(["UpdateNode" , from, to]);
5439         if (from.nodeType != to.nodeType) {
5440             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5441             from.parentNode.replaceChild(to, from);
5442         }
5443         
5444         if (from.nodeType == 3) {
5445             // assume it's text?!
5446             if (from.data == to.data) {
5447                 return;
5448             }
5449             from.data = to.data;
5450             return;
5451         }
5452         
5453         // assume 'to' doesnt have '1/3 nodetypes!
5454         if (from.nodeType !=1 || from.tagName != to.tagName) {
5455             Roo.log(["ReplaceChild" , from, to ]);
5456             from.parentNode.replaceChild(to, from);
5457             return;
5458         }
5459         // compare attributes
5460         var ar = Array.from(from.attributes);
5461         for(var i = 0; i< ar.length;i++) {
5462             if (to.hasAttribute(ar[i].name)) {
5463                 continue;
5464             }
5465             if (ar[i].name == 'id') { // always keep ids?
5466                 continue;
5467             }
5468             from.removeAttribute(ar[i].name);
5469         }
5470         ar = to.attributes;
5471         for(var i = 0; i< ar.length;i++) {
5472             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5473                 continue;
5474             }
5475             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5476         }
5477         // children
5478         var far = Array.from(from.childNodes);
5479         var tar = Array.from(to.childNodes);
5480         // if the lengths are different.. then it's probably a editable content change, rather than
5481         // a change of the block definition..
5482         
5483         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5484          /*if (from.innerHTML == to.innerHTML) {
5485             return;
5486         }
5487         if (far.length != tar.length) {
5488             from.innerHTML = to.innerHTML;
5489             return;
5490         }
5491         */
5492         
5493         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5494             if (i >= far.length) {
5495                 from.appendChild(tar[i]);
5496                 Roo.log(["add", tar[i]]);
5497                 
5498             } else if ( i  >= tar.length) {
5499                 from.removeChild(far[i]);
5500                 Roo.log(["remove", far[i]]);
5501             } else {
5502                 
5503                 updateNode(far[i], tar[i]);
5504             }    
5505         }
5506         
5507         
5508         
5509         
5510     };
5511     
5512     
5513
5514     return {
5515         /** True to force the use of DOM instead of html fragments @type Boolean */
5516         useDom : false,
5517     
5518         /**
5519          * Returns the markup for the passed Element(s) config
5520          * @param {Object} o The Dom object spec (and children)
5521          * @return {String}
5522          */
5523         markup : function(o){
5524             return createHtml(o);
5525         },
5526     
5527         /**
5528          * Applies a style specification to an element
5529          * @param {String/HTMLElement} el The element to apply styles to
5530          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5531          * a function which returns such a specification.
5532          */
5533         applyStyles : function(el, styles){
5534             if(styles){
5535                el = Roo.fly(el);
5536                if(typeof styles == "string"){
5537                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5538                    var matches;
5539                    while ((matches = re.exec(styles)) != null){
5540                        el.setStyle(matches[1], matches[2]);
5541                    }
5542                }else if (typeof styles == "object"){
5543                    for (var style in styles){
5544                       el.setStyle(style, styles[style]);
5545                    }
5546                }else if (typeof styles == "function"){
5547                     Roo.DomHelper.applyStyles(el, styles.call());
5548                }
5549             }
5550         },
5551     
5552         /**
5553          * Inserts an HTML fragment into the Dom
5554          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5555          * @param {HTMLElement} el The context element
5556          * @param {String} html The HTML fragmenet
5557          * @return {HTMLElement} The new node
5558          */
5559         insertHtml : function(where, el, html){
5560             where = where.toLowerCase();
5561             if(el.insertAdjacentHTML){
5562                 if(tableRe.test(el.tagName)){
5563                     var rs;
5564                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5565                         return rs;
5566                     }
5567                 }
5568                 switch(where){
5569                     case "beforebegin":
5570                         el.insertAdjacentHTML('BeforeBegin', html);
5571                         return el.previousSibling;
5572                     case "afterbegin":
5573                         el.insertAdjacentHTML('AfterBegin', html);
5574                         return el.firstChild;
5575                     case "beforeend":
5576                         el.insertAdjacentHTML('BeforeEnd', html);
5577                         return el.lastChild;
5578                     case "afterend":
5579                         el.insertAdjacentHTML('AfterEnd', html);
5580                         return el.nextSibling;
5581                 }
5582                 throw 'Illegal insertion point -> "' + where + '"';
5583             }
5584             var range = el.ownerDocument.createRange();
5585             var frag;
5586             switch(where){
5587                  case "beforebegin":
5588                     range.setStartBefore(el);
5589                     frag = range.createContextualFragment(html);
5590                     el.parentNode.insertBefore(frag, el);
5591                     return el.previousSibling;
5592                  case "afterbegin":
5593                     if(el.firstChild){
5594                         range.setStartBefore(el.firstChild);
5595                         frag = range.createContextualFragment(html);
5596                         el.insertBefore(frag, el.firstChild);
5597                         return el.firstChild;
5598                     }else{
5599                         el.innerHTML = html;
5600                         return el.firstChild;
5601                     }
5602                 case "beforeend":
5603                     if(el.lastChild){
5604                         range.setStartAfter(el.lastChild);
5605                         frag = range.createContextualFragment(html);
5606                         el.appendChild(frag);
5607                         return el.lastChild;
5608                     }else{
5609                         el.innerHTML = html;
5610                         return el.lastChild;
5611                     }
5612                 case "afterend":
5613                     range.setStartAfter(el);
5614                     frag = range.createContextualFragment(html);
5615                     el.parentNode.insertBefore(frag, el.nextSibling);
5616                     return el.nextSibling;
5617                 }
5618                 throw 'Illegal insertion point -> "' + where + '"';
5619         },
5620     
5621         /**
5622          * Creates new Dom element(s) and inserts them before el
5623          * @param {String/HTMLElement/Element} el The context element
5624          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5625          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5626          * @return {HTMLElement/Roo.Element} The new node
5627          */
5628         insertBefore : function(el, o, returnElement){
5629             return this.doInsert(el, o, returnElement, "beforeBegin");
5630         },
5631     
5632         /**
5633          * Creates new Dom element(s) and inserts them after el
5634          * @param {String/HTMLElement/Element} el The context element
5635          * @param {Object} o The Dom object spec (and children)
5636          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5637          * @return {HTMLElement/Roo.Element} The new node
5638          */
5639         insertAfter : function(el, o, returnElement){
5640             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5641         },
5642     
5643         /**
5644          * Creates new Dom element(s) and inserts them as the first child of el
5645          * @param {String/HTMLElement/Element} el The context element
5646          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5647          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5648          * @return {HTMLElement/Roo.Element} The new node
5649          */
5650         insertFirst : function(el, o, returnElement){
5651             return this.doInsert(el, o, returnElement, "afterBegin");
5652         },
5653     
5654         // private
5655         doInsert : function(el, o, returnElement, pos, sibling){
5656             el = Roo.getDom(el);
5657             var newNode;
5658             if(this.useDom || o.ns){
5659                 newNode = createDom(o, null);
5660                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5661             }else{
5662                 var html = createHtml(o);
5663                 newNode = this.insertHtml(pos, el, html);
5664             }
5665             return returnElement ? Roo.get(newNode, true) : newNode;
5666         },
5667     
5668         /**
5669          * Creates new Dom element(s) and appends them to el
5670          * @param {String/HTMLElement/Element} el The context element
5671          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5672          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5673          * @return {HTMLElement/Roo.Element} The new node
5674          */
5675         append : function(el, o, returnElement){
5676             el = Roo.getDom(el);
5677             var newNode;
5678             if(this.useDom || o.ns){
5679                 newNode = createDom(o, null);
5680                 el.appendChild(newNode);
5681             }else{
5682                 var html = createHtml(o);
5683                 newNode = this.insertHtml("beforeEnd", el, html);
5684             }
5685             return returnElement ? Roo.get(newNode, true) : newNode;
5686         },
5687     
5688         /**
5689          * Creates new Dom element(s) and overwrites the contents of el with them
5690          * @param {String/HTMLElement/Element} el The context element
5691          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5692          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5693          * @return {HTMLElement/Roo.Element} The new node
5694          */
5695         overwrite : function(el, o, returnElement)
5696         {
5697             el = Roo.getDom(el);
5698             if (o.ns) {
5699               
5700                 while (el.childNodes.length) {
5701                     el.removeChild(el.firstChild);
5702                 }
5703                 createDom(o, el);
5704             } else {
5705                 el.innerHTML = createHtml(o);   
5706             }
5707             
5708             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5709         },
5710     
5711         /**
5712          * Creates a new Roo.DomHelper.Template from the Dom object spec
5713          * @param {Object} o The Dom object spec (and children)
5714          * @return {Roo.DomHelper.Template} The new template
5715          */
5716         createTemplate : function(o){
5717             var html = createHtml(o);
5718             return new Roo.Template(html);
5719         },
5720          /**
5721          * Updates the first element with the spec from the o (replacing if necessary)
5722          * This iterates through the children, and updates attributes / children etc..
5723          * @param {String/HTMLElement/Element} el The context element
5724          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5725          */
5726         
5727         update : function(el, o)
5728         {
5729             updateNode(Roo.getDom(el), createDom(o));
5730             
5731         }
5732         
5733         
5734     };
5735 }();
5736 /*
5737  * Based on:
5738  * Ext JS Library 1.1.1
5739  * Copyright(c) 2006-2007, Ext JS, LLC.
5740  *
5741  * Originally Released Under LGPL - original licence link has changed is not relivant.
5742  *
5743  * Fork - LGPL
5744  * <script type="text/javascript">
5745  */
5746  
5747 /**
5748 * @class Roo.Template
5749 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5750 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5751 * Usage:
5752 <pre><code>
5753 var t = new Roo.Template({
5754     html :  '&lt;div name="{id}"&gt;' + 
5755         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5756         '&lt;/div&gt;',
5757     myformat: function (value, allValues) {
5758         return 'XX' + value;
5759     }
5760 });
5761 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5762 </code></pre>
5763 * For more information see this blog post with examples:
5764 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5765      - Create Elements using DOM, HTML fragments and Templates</a>. 
5766 * @constructor
5767 * @param {Object} cfg - Configuration object.
5768 */
5769 Roo.Template = function(cfg){
5770     // BC!
5771     if(cfg instanceof Array){
5772         cfg = cfg.join("");
5773     }else if(arguments.length > 1){
5774         cfg = Array.prototype.join.call(arguments, "");
5775     }
5776     
5777     
5778     if (typeof(cfg) == 'object') {
5779         Roo.apply(this,cfg)
5780     } else {
5781         // bc
5782         this.html = cfg;
5783     }
5784     if (this.url) {
5785         this.load();
5786     }
5787     
5788 };
5789 Roo.Template.prototype = {
5790     
5791     /**
5792      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5793      */
5794     onLoad : false,
5795     
5796     
5797     /**
5798      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
5799      *                    it should be fixed so that template is observable...
5800      */
5801     url : false,
5802     /**
5803      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5804      */
5805     html : '',
5806     
5807     
5808     compiled : false,
5809     loaded : false,
5810     /**
5811      * Returns an HTML fragment of this template with the specified values applied.
5812      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5813      * @return {String} The HTML fragment
5814      */
5815     
5816    
5817     
5818     applyTemplate : function(values){
5819         //Roo.log(["applyTemplate", values]);
5820         try {
5821            
5822             if(this.compiled){
5823                 return this.compiled(values);
5824             }
5825             var useF = this.disableFormats !== true;
5826             var fm = Roo.util.Format, tpl = this;
5827             var fn = function(m, name, format, args){
5828                 if(format && useF){
5829                     if(format.substr(0, 5) == "this."){
5830                         return tpl.call(format.substr(5), values[name], values);
5831                     }else{
5832                         if(args){
5833                             // quoted values are required for strings in compiled templates, 
5834                             // but for non compiled we need to strip them
5835                             // quoted reversed for jsmin
5836                             var re = /^\s*['"](.*)["']\s*$/;
5837                             args = args.split(',');
5838                             for(var i = 0, len = args.length; i < len; i++){
5839                                 args[i] = args[i].replace(re, "$1");
5840                             }
5841                             args = [values[name]].concat(args);
5842                         }else{
5843                             args = [values[name]];
5844                         }
5845                         return fm[format].apply(fm, args);
5846                     }
5847                 }else{
5848                     return values[name] !== undefined ? values[name] : "";
5849                 }
5850             };
5851             return this.html.replace(this.re, fn);
5852         } catch (e) {
5853             Roo.log(e);
5854             throw e;
5855         }
5856          
5857     },
5858     
5859     loading : false,
5860       
5861     load : function ()
5862     {
5863          
5864         if (this.loading) {
5865             return;
5866         }
5867         var _t = this;
5868         
5869         this.loading = true;
5870         this.compiled = false;
5871         
5872         var cx = new Roo.data.Connection();
5873         cx.request({
5874             url : this.url,
5875             method : 'GET',
5876             success : function (response) {
5877                 _t.loading = false;
5878                 _t.url = false;
5879                 
5880                 _t.set(response.responseText,true);
5881                 _t.loaded = true;
5882                 if (_t.onLoad) {
5883                     _t.onLoad();
5884                 }
5885              },
5886             failure : function(response) {
5887                 Roo.log("Template failed to load from " + _t.url);
5888                 _t.loading = false;
5889             }
5890         });
5891     },
5892
5893     /**
5894      * Sets the HTML used as the template and optionally compiles it.
5895      * @param {String} html
5896      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5897      * @return {Roo.Template} this
5898      */
5899     set : function(html, compile){
5900         this.html = html;
5901         this.compiled = false;
5902         if(compile){
5903             this.compile();
5904         }
5905         return this;
5906     },
5907     
5908     /**
5909      * True to disable format functions (defaults to false)
5910      * @type Boolean
5911      */
5912     disableFormats : false,
5913     
5914     /**
5915     * The regular expression used to match template variables 
5916     * @type RegExp
5917     * @property 
5918     */
5919     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5920     
5921     /**
5922      * Compiles the template into an internal function, eliminating the RegEx overhead.
5923      * @return {Roo.Template} this
5924      */
5925     compile : function(){
5926         var fm = Roo.util.Format;
5927         var useF = this.disableFormats !== true;
5928         var sep = Roo.isGecko ? "+" : ",";
5929         var fn = function(m, name, format, args){
5930             if(format && useF){
5931                 args = args ? ',' + args : "";
5932                 if(format.substr(0, 5) != "this."){
5933                     format = "fm." + format + '(';
5934                 }else{
5935                     format = 'this.call("'+ format.substr(5) + '", ';
5936                     args = ", values";
5937                 }
5938             }else{
5939                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5940             }
5941             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5942         };
5943         var body;
5944         // branched to use + in gecko and [].join() in others
5945         if(Roo.isGecko){
5946             body = "this.compiled = function(values){ return '" +
5947                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5948                     "';};";
5949         }else{
5950             body = ["this.compiled = function(values){ return ['"];
5951             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5952             body.push("'].join('');};");
5953             body = body.join('');
5954         }
5955         /**
5956          * eval:var:values
5957          * eval:var:fm
5958          */
5959         eval(body);
5960         return this;
5961     },
5962     
5963     // private function used to call members
5964     call : function(fnName, value, allValues){
5965         return this[fnName](value, allValues);
5966     },
5967     
5968     /**
5969      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5970      * @param {String/HTMLElement/Roo.Element} el The context element
5971      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5972      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5973      * @return {HTMLElement/Roo.Element} The new node or Element
5974      */
5975     insertFirst: function(el, values, returnElement){
5976         return this.doInsert('afterBegin', el, values, returnElement);
5977     },
5978
5979     /**
5980      * Applies the supplied values to the template and inserts the new node(s) before el.
5981      * @param {String/HTMLElement/Roo.Element} el The context element
5982      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5983      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5984      * @return {HTMLElement/Roo.Element} The new node or Element
5985      */
5986     insertBefore: function(el, values, returnElement){
5987         return this.doInsert('beforeBegin', el, values, returnElement);
5988     },
5989
5990     /**
5991      * Applies the supplied values to the template and inserts the new node(s) after el.
5992      * @param {String/HTMLElement/Roo.Element} el The context element
5993      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5994      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5995      * @return {HTMLElement/Roo.Element} The new node or Element
5996      */
5997     insertAfter : function(el, values, returnElement){
5998         return this.doInsert('afterEnd', el, values, returnElement);
5999     },
6000     
6001     /**
6002      * Applies the supplied values to the template and appends the new node(s) to el.
6003      * @param {String/HTMLElement/Roo.Element} el The context element
6004      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6005      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006      * @return {HTMLElement/Roo.Element} The new node or Element
6007      */
6008     append : function(el, values, returnElement){
6009         return this.doInsert('beforeEnd', el, values, returnElement);
6010     },
6011
6012     doInsert : function(where, el, values, returnEl){
6013         el = Roo.getDom(el);
6014         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6015         return returnEl ? Roo.get(newNode, true) : newNode;
6016     },
6017
6018     /**
6019      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6020      * @param {String/HTMLElement/Roo.Element} el The context element
6021      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6022      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6023      * @return {HTMLElement/Roo.Element} The new node or Element
6024      */
6025     overwrite : function(el, values, returnElement){
6026         el = Roo.getDom(el);
6027         el.innerHTML = this.applyTemplate(values);
6028         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6029     }
6030 };
6031 /**
6032  * Alias for {@link #applyTemplate}
6033  * @method
6034  */
6035 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6036
6037 // backwards compat
6038 Roo.DomHelper.Template = Roo.Template;
6039
6040 /**
6041  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6042  * @param {String/HTMLElement} el A DOM element or its id
6043  * @returns {Roo.Template} The created template
6044  * @static
6045  */
6046 Roo.Template.from = function(el){
6047     el = Roo.getDom(el);
6048     return new Roo.Template(el.value || el.innerHTML);
6049 };/*
6050  * Based on:
6051  * Ext JS Library 1.1.1
6052  * Copyright(c) 2006-2007, Ext JS, LLC.
6053  *
6054  * Originally Released Under LGPL - original licence link has changed is not relivant.
6055  *
6056  * Fork - LGPL
6057  * <script type="text/javascript">
6058  */
6059  
6060
6061 /*
6062  * This is code is also distributed under MIT license for use
6063  * with jQuery and prototype JavaScript libraries.
6064  */
6065 /**
6066  * @class Roo.DomQuery
6067 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
6068 <p>
6069 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
6070
6071 <p>
6072 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
6073 </p>
6074 <h4>Element Selectors:</h4>
6075 <ul class="list">
6076     <li> <b>*</b> any element</li>
6077     <li> <b>E</b> an element with the tag E</li>
6078     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6079     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6080     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6081     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6082 </ul>
6083 <h4>Attribute Selectors:</h4>
6084 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6085 <ul class="list">
6086     <li> <b>E[foo]</b> has an attribute "foo"</li>
6087     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6088     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6089     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6090     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6091     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6092     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6093 </ul>
6094 <h4>Pseudo Classes:</h4>
6095 <ul class="list">
6096     <li> <b>E:first-child</b> E is the first child of its parent</li>
6097     <li> <b>E:last-child</b> E is the last child of its parent</li>
6098     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
6099     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6100     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6101     <li> <b>E:only-child</b> E is the only child of its parent</li>
6102     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
6103     <li> <b>E:first</b> the first E in the resultset</li>
6104     <li> <b>E:last</b> the last E in the resultset</li>
6105     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6106     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6107     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6108     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6109     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6110     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6111     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6112     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6113     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6114 </ul>
6115 <h4>CSS Value Selectors:</h4>
6116 <ul class="list">
6117     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6118     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6119     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6120     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6121     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6122     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6123 </ul>
6124  * @static
6125  */
6126 Roo.DomQuery = function(){
6127     var cache = {}, simpleCache = {}, valueCache = {};
6128     var nonSpace = /\S/;
6129     var trimRe = /^\s+|\s+$/g;
6130     var tplRe = /\{(\d+)\}/g;
6131     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6132     var tagTokenRe = /^(#)?([\w-\*]+)/;
6133     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6134
6135     function child(p, index){
6136         var i = 0;
6137         var n = p.firstChild;
6138         while(n){
6139             if(n.nodeType == 1){
6140                if(++i == index){
6141                    return n;
6142                }
6143             }
6144             n = n.nextSibling;
6145         }
6146         return null;
6147     };
6148
6149     function next(n){
6150         while((n = n.nextSibling) && n.nodeType != 1);
6151         return n;
6152     };
6153
6154     function prev(n){
6155         while((n = n.previousSibling) && n.nodeType != 1);
6156         return n;
6157     };
6158
6159     function children(d){
6160         var n = d.firstChild, ni = -1;
6161             while(n){
6162                 var nx = n.nextSibling;
6163                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6164                     d.removeChild(n);
6165                 }else{
6166                     n.nodeIndex = ++ni;
6167                 }
6168                 n = nx;
6169             }
6170             return this;
6171         };
6172
6173     function byClassName(c, a, v){
6174         if(!v){
6175             return c;
6176         }
6177         var r = [], ri = -1, cn;
6178         for(var i = 0, ci; ci = c[i]; i++){
6179             
6180             
6181             if((' '+
6182                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6183                  +' ').indexOf(v) != -1){
6184                 r[++ri] = ci;
6185             }
6186         }
6187         return r;
6188     };
6189
6190     function attrValue(n, attr){
6191         if(!n.tagName && typeof n.length != "undefined"){
6192             n = n[0];
6193         }
6194         if(!n){
6195             return null;
6196         }
6197         if(attr == "for"){
6198             return n.htmlFor;
6199         }
6200         if(attr == "class" || attr == "className"){
6201             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6202         }
6203         return n.getAttribute(attr) || n[attr];
6204
6205     };
6206
6207     function getNodes(ns, mode, tagName){
6208         var result = [], ri = -1, cs;
6209         if(!ns){
6210             return result;
6211         }
6212         tagName = tagName || "*";
6213         if(typeof ns.getElementsByTagName != "undefined"){
6214             ns = [ns];
6215         }
6216         if(!mode){
6217             for(var i = 0, ni; ni = ns[i]; i++){
6218                 cs = ni.getElementsByTagName(tagName);
6219                 for(var j = 0, ci; ci = cs[j]; j++){
6220                     result[++ri] = ci;
6221                 }
6222             }
6223         }else if(mode == "/" || mode == ">"){
6224             var utag = tagName.toUpperCase();
6225             for(var i = 0, ni, cn; ni = ns[i]; i++){
6226                 cn = ni.children || ni.childNodes;
6227                 for(var j = 0, cj; cj = cn[j]; j++){
6228                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6229                         result[++ri] = cj;
6230                     }
6231                 }
6232             }
6233         }else if(mode == "+"){
6234             var utag = tagName.toUpperCase();
6235             for(var i = 0, n; n = ns[i]; i++){
6236                 while((n = n.nextSibling) && n.nodeType != 1);
6237                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6238                     result[++ri] = n;
6239                 }
6240             }
6241         }else if(mode == "~"){
6242             for(var i = 0, n; n = ns[i]; i++){
6243                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6244                 if(n){
6245                     result[++ri] = n;
6246                 }
6247             }
6248         }
6249         return result;
6250     };
6251
6252     function concat(a, b){
6253         if(b.slice){
6254             return a.concat(b);
6255         }
6256         for(var i = 0, l = b.length; i < l; i++){
6257             a[a.length] = b[i];
6258         }
6259         return a;
6260     }
6261
6262     function byTag(cs, tagName){
6263         if(cs.tagName || cs == document){
6264             cs = [cs];
6265         }
6266         if(!tagName){
6267             return cs;
6268         }
6269         var r = [], ri = -1;
6270         tagName = tagName.toLowerCase();
6271         for(var i = 0, ci; ci = cs[i]; i++){
6272             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6273                 r[++ri] = ci;
6274             }
6275         }
6276         return r;
6277     };
6278
6279     function byId(cs, attr, id){
6280         if(cs.tagName || cs == document){
6281             cs = [cs];
6282         }
6283         if(!id){
6284             return cs;
6285         }
6286         var r = [], ri = -1;
6287         for(var i = 0,ci; ci = cs[i]; i++){
6288             if(ci && ci.id == id){
6289                 r[++ri] = ci;
6290                 return r;
6291             }
6292         }
6293         return r;
6294     };
6295
6296     function byAttribute(cs, attr, value, op, custom){
6297         var r = [], ri = -1, st = custom=="{";
6298         var f = Roo.DomQuery.operators[op];
6299         for(var i = 0, ci; ci = cs[i]; i++){
6300             var a;
6301             if(st){
6302                 a = Roo.DomQuery.getStyle(ci, attr);
6303             }
6304             else if(attr == "class" || attr == "className"){
6305                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6306             }else if(attr == "for"){
6307                 a = ci.htmlFor;
6308             }else if(attr == "href"){
6309                 a = ci.getAttribute("href", 2);
6310             }else{
6311                 a = ci.getAttribute(attr);
6312             }
6313             if((f && f(a, value)) || (!f && a)){
6314                 r[++ri] = ci;
6315             }
6316         }
6317         return r;
6318     };
6319
6320     function byPseudo(cs, name, value){
6321         return Roo.DomQuery.pseudos[name](cs, value);
6322     };
6323
6324     // This is for IE MSXML which does not support expandos.
6325     // IE runs the same speed using setAttribute, however FF slows way down
6326     // and Safari completely fails so they need to continue to use expandos.
6327     var isIE = window.ActiveXObject ? true : false;
6328
6329     // this eval is stop the compressor from
6330     // renaming the variable to something shorter
6331     
6332     /** eval:var:batch */
6333     var batch = 30803; 
6334
6335     var key = 30803;
6336
6337     function nodupIEXml(cs){
6338         var d = ++key;
6339         cs[0].setAttribute("_nodup", d);
6340         var r = [cs[0]];
6341         for(var i = 1, len = cs.length; i < len; i++){
6342             var c = cs[i];
6343             if(!c.getAttribute("_nodup") != d){
6344                 c.setAttribute("_nodup", d);
6345                 r[r.length] = c;
6346             }
6347         }
6348         for(var i = 0, len = cs.length; i < len; i++){
6349             cs[i].removeAttribute("_nodup");
6350         }
6351         return r;
6352     }
6353
6354     function nodup(cs){
6355         if(!cs){
6356             return [];
6357         }
6358         var len = cs.length, c, i, r = cs, cj, ri = -1;
6359         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6360             return cs;
6361         }
6362         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6363             return nodupIEXml(cs);
6364         }
6365         var d = ++key;
6366         cs[0]._nodup = d;
6367         for(i = 1; c = cs[i]; i++){
6368             if(c._nodup != d){
6369                 c._nodup = d;
6370             }else{
6371                 r = [];
6372                 for(var j = 0; j < i; j++){
6373                     r[++ri] = cs[j];
6374                 }
6375                 for(j = i+1; cj = cs[j]; j++){
6376                     if(cj._nodup != d){
6377                         cj._nodup = d;
6378                         r[++ri] = cj;
6379                     }
6380                 }
6381                 return r;
6382             }
6383         }
6384         return r;
6385     }
6386
6387     function quickDiffIEXml(c1, c2){
6388         var d = ++key;
6389         for(var i = 0, len = c1.length; i < len; i++){
6390             c1[i].setAttribute("_qdiff", d);
6391         }
6392         var r = [];
6393         for(var i = 0, len = c2.length; i < len; i++){
6394             if(c2[i].getAttribute("_qdiff") != d){
6395                 r[r.length] = c2[i];
6396             }
6397         }
6398         for(var i = 0, len = c1.length; i < len; i++){
6399            c1[i].removeAttribute("_qdiff");
6400         }
6401         return r;
6402     }
6403
6404     function quickDiff(c1, c2){
6405         var len1 = c1.length;
6406         if(!len1){
6407             return c2;
6408         }
6409         if(isIE && c1[0].selectSingleNode){
6410             return quickDiffIEXml(c1, c2);
6411         }
6412         var d = ++key;
6413         for(var i = 0; i < len1; i++){
6414             c1[i]._qdiff = d;
6415         }
6416         var r = [];
6417         for(var i = 0, len = c2.length; i < len; i++){
6418             if(c2[i]._qdiff != d){
6419                 r[r.length] = c2[i];
6420             }
6421         }
6422         return r;
6423     }
6424
6425     function quickId(ns, mode, root, id){
6426         if(ns == root){
6427            var d = root.ownerDocument || root;
6428            return d.getElementById(id);
6429         }
6430         ns = getNodes(ns, mode, "*");
6431         return byId(ns, null, id);
6432     }
6433
6434     return {
6435         getStyle : function(el, name){
6436             return Roo.fly(el).getStyle(name);
6437         },
6438         /**
6439          * Compiles a selector/xpath query into a reusable function. The returned function
6440          * takes one parameter "root" (optional), which is the context node from where the query should start.
6441          * @param {String} selector The selector/xpath query
6442          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6443          * @return {Function}
6444          */
6445         compile : function(path, type){
6446             type = type || "select";
6447             
6448             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6449             var q = path, mode, lq;
6450             var tk = Roo.DomQuery.matchers;
6451             var tklen = tk.length;
6452             var mm;
6453
6454             // accept leading mode switch
6455             var lmode = q.match(modeRe);
6456             if(lmode && lmode[1]){
6457                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6458                 q = q.replace(lmode[1], "");
6459             }
6460             // strip leading slashes
6461             while(path.substr(0, 1)=="/"){
6462                 path = path.substr(1);
6463             }
6464
6465             while(q && lq != q){
6466                 lq = q;
6467                 var tm = q.match(tagTokenRe);
6468                 if(type == "select"){
6469                     if(tm){
6470                         if(tm[1] == "#"){
6471                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6472                         }else{
6473                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6474                         }
6475                         q = q.replace(tm[0], "");
6476                     }else if(q.substr(0, 1) != '@'){
6477                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6478                     }
6479                 }else{
6480                     if(tm){
6481                         if(tm[1] == "#"){
6482                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6483                         }else{
6484                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6485                         }
6486                         q = q.replace(tm[0], "");
6487                     }
6488                 }
6489                 while(!(mm = q.match(modeRe))){
6490                     var matched = false;
6491                     for(var j = 0; j < tklen; j++){
6492                         var t = tk[j];
6493                         var m = q.match(t.re);
6494                         if(m){
6495                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6496                                                     return m[i];
6497                                                 });
6498                             q = q.replace(m[0], "");
6499                             matched = true;
6500                             break;
6501                         }
6502                     }
6503                     // prevent infinite loop on bad selector
6504                     if(!matched){
6505                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6506                     }
6507                 }
6508                 if(mm[1]){
6509                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6510                     q = q.replace(mm[1], "");
6511                 }
6512             }
6513             fn[fn.length] = "return nodup(n);\n}";
6514             
6515              /** 
6516               * list of variables that need from compression as they are used by eval.
6517              *  eval:var:batch 
6518              *  eval:var:nodup
6519              *  eval:var:byTag
6520              *  eval:var:ById
6521              *  eval:var:getNodes
6522              *  eval:var:quickId
6523              *  eval:var:mode
6524              *  eval:var:root
6525              *  eval:var:n
6526              *  eval:var:byClassName
6527              *  eval:var:byPseudo
6528              *  eval:var:byAttribute
6529              *  eval:var:attrValue
6530              * 
6531              **/ 
6532             eval(fn.join(""));
6533             return f;
6534         },
6535
6536         /**
6537          * Selects a group of elements.
6538          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6539          * @param {Node} root (optional) The start of the query (defaults to document).
6540          * @return {Array}
6541          */
6542         select : function(path, root, type){
6543             if(!root || root == document){
6544                 root = document;
6545             }
6546             if(typeof root == "string"){
6547                 root = document.getElementById(root);
6548             }
6549             var paths = path.split(",");
6550             var results = [];
6551             for(var i = 0, len = paths.length; i < len; i++){
6552                 var p = paths[i].replace(trimRe, "");
6553                 if(!cache[p]){
6554                     cache[p] = Roo.DomQuery.compile(p);
6555                     if(!cache[p]){
6556                         throw p + " is not a valid selector";
6557                     }
6558                 }
6559                 var result = cache[p](root);
6560                 if(result && result != document){
6561                     results = results.concat(result);
6562                 }
6563             }
6564             if(paths.length > 1){
6565                 return nodup(results);
6566             }
6567             return results;
6568         },
6569
6570         /**
6571          * Selects a single element.
6572          * @param {String} selector The selector/xpath query
6573          * @param {Node} root (optional) The start of the query (defaults to document).
6574          * @return {Element}
6575          */
6576         selectNode : function(path, root){
6577             return Roo.DomQuery.select(path, root)[0];
6578         },
6579
6580         /**
6581          * Selects the value of a node, optionally replacing null with the defaultValue.
6582          * @param {String} selector The selector/xpath query
6583          * @param {Node} root (optional) The start of the query (defaults to document).
6584          * @param {String} defaultValue
6585          */
6586         selectValue : function(path, root, defaultValue){
6587             path = path.replace(trimRe, "");
6588             if(!valueCache[path]){
6589                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6590             }
6591             var n = valueCache[path](root);
6592             n = n[0] ? n[0] : n;
6593             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6594             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6595         },
6596
6597         /**
6598          * Selects the value of a node, parsing integers and floats.
6599          * @param {String} selector The selector/xpath query
6600          * @param {Node} root (optional) The start of the query (defaults to document).
6601          * @param {Number} defaultValue
6602          * @return {Number}
6603          */
6604         selectNumber : function(path, root, defaultValue){
6605             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6606             return parseFloat(v);
6607         },
6608
6609         /**
6610          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6611          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6612          * @param {String} selector The simple selector to test
6613          * @return {Boolean}
6614          */
6615         is : function(el, ss){
6616             if(typeof el == "string"){
6617                 el = document.getElementById(el);
6618             }
6619             var isArray = (el instanceof Array);
6620             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6621             return isArray ? (result.length == el.length) : (result.length > 0);
6622         },
6623
6624         /**
6625          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6626          * @param {Array} el An array of elements to filter
6627          * @param {String} selector The simple selector to test
6628          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6629          * the selector instead of the ones that match
6630          * @return {Array}
6631          */
6632         filter : function(els, ss, nonMatches){
6633             ss = ss.replace(trimRe, "");
6634             if(!simpleCache[ss]){
6635                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6636             }
6637             var result = simpleCache[ss](els);
6638             return nonMatches ? quickDiff(result, els) : result;
6639         },
6640
6641         /**
6642          * Collection of matching regular expressions and code snippets.
6643          */
6644         matchers : [{
6645                 re: /^\.([\w-]+)/,
6646                 select: 'n = byClassName(n, null, " {1} ");'
6647             }, {
6648                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6649                 select: 'n = byPseudo(n, "{1}", "{2}");'
6650             },{
6651                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6652                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6653             }, {
6654                 re: /^#([\w-]+)/,
6655                 select: 'n = byId(n, null, "{1}");'
6656             },{
6657                 re: /^@([\w-]+)/,
6658                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6659             }
6660         ],
6661
6662         /**
6663          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6664          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
6665          */
6666         operators : {
6667             "=" : function(a, v){
6668                 return a == v;
6669             },
6670             "!=" : function(a, v){
6671                 return a != v;
6672             },
6673             "^=" : function(a, v){
6674                 return a && a.substr(0, v.length) == v;
6675             },
6676             "$=" : function(a, v){
6677                 return a && a.substr(a.length-v.length) == v;
6678             },
6679             "*=" : function(a, v){
6680                 return a && a.indexOf(v) !== -1;
6681             },
6682             "%=" : function(a, v){
6683                 return (a % v) == 0;
6684             },
6685             "|=" : function(a, v){
6686                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6687             },
6688             "~=" : function(a, v){
6689                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6690             }
6691         },
6692
6693         /**
6694          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6695          * and the argument (if any) supplied in the selector.
6696          */
6697         pseudos : {
6698             "first-child" : function(c){
6699                 var r = [], ri = -1, n;
6700                 for(var i = 0, ci; ci = n = c[i]; i++){
6701                     while((n = n.previousSibling) && n.nodeType != 1);
6702                     if(!n){
6703                         r[++ri] = ci;
6704                     }
6705                 }
6706                 return r;
6707             },
6708
6709             "last-child" : function(c){
6710                 var r = [], ri = -1, n;
6711                 for(var i = 0, ci; ci = n = c[i]; i++){
6712                     while((n = n.nextSibling) && n.nodeType != 1);
6713                     if(!n){
6714                         r[++ri] = ci;
6715                     }
6716                 }
6717                 return r;
6718             },
6719
6720             "nth-child" : function(c, a) {
6721                 var r = [], ri = -1;
6722                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6723                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6724                 for(var i = 0, n; n = c[i]; i++){
6725                     var pn = n.parentNode;
6726                     if (batch != pn._batch) {
6727                         var j = 0;
6728                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6729                             if(cn.nodeType == 1){
6730                                cn.nodeIndex = ++j;
6731                             }
6732                         }
6733                         pn._batch = batch;
6734                     }
6735                     if (f == 1) {
6736                         if (l == 0 || n.nodeIndex == l){
6737                             r[++ri] = n;
6738                         }
6739                     } else if ((n.nodeIndex + l) % f == 0){
6740                         r[++ri] = n;
6741                     }
6742                 }
6743
6744                 return r;
6745             },
6746
6747             "only-child" : function(c){
6748                 var r = [], ri = -1;;
6749                 for(var i = 0, ci; ci = c[i]; i++){
6750                     if(!prev(ci) && !next(ci)){
6751                         r[++ri] = ci;
6752                     }
6753                 }
6754                 return r;
6755             },
6756
6757             "empty" : function(c){
6758                 var r = [], ri = -1;
6759                 for(var i = 0, ci; ci = c[i]; i++){
6760                     var cns = ci.childNodes, j = 0, cn, empty = true;
6761                     while(cn = cns[j]){
6762                         ++j;
6763                         if(cn.nodeType == 1 || cn.nodeType == 3){
6764                             empty = false;
6765                             break;
6766                         }
6767                     }
6768                     if(empty){
6769                         r[++ri] = ci;
6770                     }
6771                 }
6772                 return r;
6773             },
6774
6775             "contains" : function(c, v){
6776                 var r = [], ri = -1;
6777                 for(var i = 0, ci; ci = c[i]; i++){
6778                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6779                         r[++ri] = ci;
6780                     }
6781                 }
6782                 return r;
6783             },
6784
6785             "nodeValue" : function(c, v){
6786                 var r = [], ri = -1;
6787                 for(var i = 0, ci; ci = c[i]; i++){
6788                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6789                         r[++ri] = ci;
6790                     }
6791                 }
6792                 return r;
6793             },
6794
6795             "checked" : function(c){
6796                 var r = [], ri = -1;
6797                 for(var i = 0, ci; ci = c[i]; i++){
6798                     if(ci.checked == true){
6799                         r[++ri] = ci;
6800                     }
6801                 }
6802                 return r;
6803             },
6804
6805             "not" : function(c, ss){
6806                 return Roo.DomQuery.filter(c, ss, true);
6807             },
6808
6809             "odd" : function(c){
6810                 return this["nth-child"](c, "odd");
6811             },
6812
6813             "even" : function(c){
6814                 return this["nth-child"](c, "even");
6815             },
6816
6817             "nth" : function(c, a){
6818                 return c[a-1] || [];
6819             },
6820
6821             "first" : function(c){
6822                 return c[0] || [];
6823             },
6824
6825             "last" : function(c){
6826                 return c[c.length-1] || [];
6827             },
6828
6829             "has" : function(c, ss){
6830                 var s = Roo.DomQuery.select;
6831                 var r = [], ri = -1;
6832                 for(var i = 0, ci; ci = c[i]; i++){
6833                     if(s(ss, ci).length > 0){
6834                         r[++ri] = ci;
6835                     }
6836                 }
6837                 return r;
6838             },
6839
6840             "next" : function(c, ss){
6841                 var is = Roo.DomQuery.is;
6842                 var r = [], ri = -1;
6843                 for(var i = 0, ci; ci = c[i]; i++){
6844                     var n = next(ci);
6845                     if(n && is(n, ss)){
6846                         r[++ri] = ci;
6847                     }
6848                 }
6849                 return r;
6850             },
6851
6852             "prev" : function(c, ss){
6853                 var is = Roo.DomQuery.is;
6854                 var r = [], ri = -1;
6855                 for(var i = 0, ci; ci = c[i]; i++){
6856                     var n = prev(ci);
6857                     if(n && is(n, ss)){
6858                         r[++ri] = ci;
6859                     }
6860                 }
6861                 return r;
6862             }
6863         }
6864     };
6865 }();
6866
6867 /**
6868  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6869  * @param {String} path The selector/xpath query
6870  * @param {Node} root (optional) The start of the query (defaults to document).
6871  * @return {Array}
6872  * @member Roo
6873  * @method query
6874  */
6875 Roo.query = Roo.DomQuery.select;
6876 /*
6877  * Based on:
6878  * Ext JS Library 1.1.1
6879  * Copyright(c) 2006-2007, Ext JS, LLC.
6880  *
6881  * Originally Released Under LGPL - original licence link has changed is not relivant.
6882  *
6883  * Fork - LGPL
6884  * <script type="text/javascript">
6885  */
6886
6887 /**
6888  * @class Roo.util.Observable
6889  * Base class that provides a common interface for publishing events. Subclasses are expected to
6890  * to have a property "events" with all the events defined.<br>
6891  * For example:
6892  * <pre><code>
6893  Employee = function(name){
6894     this.name = name;
6895     this.addEvents({
6896         "fired" : true,
6897         "quit" : true
6898     });
6899  }
6900  Roo.extend(Employee, Roo.util.Observable);
6901 </code></pre>
6902  * @param {Object} config properties to use (incuding events / listeners)
6903  */
6904
6905 Roo.util.Observable = function(cfg){
6906     
6907     cfg = cfg|| {};
6908     this.addEvents(cfg.events || {});
6909     if (cfg.events) {
6910         delete cfg.events; // make sure
6911     }
6912      
6913     Roo.apply(this, cfg);
6914     
6915     if(this.listeners){
6916         this.on(this.listeners);
6917         delete this.listeners;
6918     }
6919 };
6920 Roo.util.Observable.prototype = {
6921     /** 
6922  * @cfg {Object} listeners  list of events and functions to call for this object, 
6923  * For example :
6924  * <pre><code>
6925     listeners :  { 
6926        'click' : function(e) {
6927            ..... 
6928         } ,
6929         .... 
6930     } 
6931   </code></pre>
6932  */
6933     
6934     
6935     /**
6936      * Fires the specified event with the passed parameters (minus the event name).
6937      * @param {String} eventName
6938      * @param {Object...} args Variable number of parameters are passed to handlers
6939      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6940      */
6941     fireEvent : function(){
6942         var ce = this.events[arguments[0].toLowerCase()];
6943         if(typeof ce == "object"){
6944             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6945         }else{
6946             return true;
6947         }
6948     },
6949
6950     // private
6951     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6952
6953     /**
6954      * Appends an event handler to this component
6955      * @param {String}   eventName The type of event to listen for
6956      * @param {Function} handler The method the event invokes
6957      * @param {Object}   scope (optional) The scope in which to execute the handler
6958      * function. The handler function's "this" context.
6959      * @param {Object}   options (optional) An object containing handler configuration
6960      * properties. This may contain any of the following properties:<ul>
6961      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6962      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6963      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6964      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6965      * by the specified number of milliseconds. If the event fires again within that time, the original
6966      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6967      * </ul><br>
6968      * <p>
6969      * <b>Combining Options</b><br>
6970      * Using the options argument, it is possible to combine different types of listeners:<br>
6971      * <br>
6972      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6973                 <pre><code>
6974                 el.on('click', this.onClick, this, {
6975                         single: true,
6976                 delay: 100,
6977                 forumId: 4
6978                 });
6979                 </code></pre>
6980      * <p>
6981      * <b>Attaching multiple handlers in 1 call</b><br>
6982      * The method also allows for a single argument to be passed which is a config object containing properties
6983      * which specify multiple handlers.
6984      * <pre><code>
6985                 el.on({
6986                         'click': {
6987                         fn: this.onClick,
6988                         scope: this,
6989                         delay: 100
6990                 }, 
6991                 'mouseover': {
6992                         fn: this.onMouseOver,
6993                         scope: this
6994                 },
6995                 'mouseout': {
6996                         fn: this.onMouseOut,
6997                         scope: this
6998                 }
6999                 });
7000                 </code></pre>
7001      * <p>
7002      * Or a shorthand syntax which passes the same scope object to all handlers:
7003         <pre><code>
7004                 el.on({
7005                         'click': this.onClick,
7006                 'mouseover': this.onMouseOver,
7007                 'mouseout': this.onMouseOut,
7008                 scope: this
7009                 });
7010                 </code></pre>
7011      */
7012     addListener : function(eventName, fn, scope, o){
7013         if(typeof eventName == "object"){
7014             o = eventName;
7015             for(var e in o){
7016                 if(this.filterOptRe.test(e)){
7017                     continue;
7018                 }
7019                 if(typeof o[e] == "function"){
7020                     // shared options
7021                     this.addListener(e, o[e], o.scope,  o);
7022                 }else{
7023                     // individual options
7024                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7025                 }
7026             }
7027             return;
7028         }
7029         o = (!o || typeof o == "boolean") ? {} : o;
7030         eventName = eventName.toLowerCase();
7031         var ce = this.events[eventName] || true;
7032         if(typeof ce == "boolean"){
7033             ce = new Roo.util.Event(this, eventName);
7034             this.events[eventName] = ce;
7035         }
7036         ce.addListener(fn, scope, o);
7037     },
7038
7039     /**
7040      * Removes a listener
7041      * @param {String}   eventName     The type of event to listen for
7042      * @param {Function} handler        The handler to remove
7043      * @param {Object}   scope  (optional) The scope (this object) for the handler
7044      */
7045     removeListener : function(eventName, fn, scope){
7046         var ce = this.events[eventName.toLowerCase()];
7047         if(typeof ce == "object"){
7048             ce.removeListener(fn, scope);
7049         }
7050     },
7051
7052     /**
7053      * Removes all listeners for this object
7054      */
7055     purgeListeners : function(){
7056         for(var evt in this.events){
7057             if(typeof this.events[evt] == "object"){
7058                  this.events[evt].clearListeners();
7059             }
7060         }
7061     },
7062
7063     relayEvents : function(o, events){
7064         var createHandler = function(ename){
7065             return function(){
7066                  
7067                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7068             };
7069         };
7070         for(var i = 0, len = events.length; i < len; i++){
7071             var ename = events[i];
7072             if(!this.events[ename]){
7073                 this.events[ename] = true;
7074             };
7075             o.on(ename, createHandler(ename), this);
7076         }
7077     },
7078
7079     /**
7080      * Used to define events on this Observable
7081      * @param {Object} object The object with the events defined
7082      */
7083     addEvents : function(o){
7084         if(!this.events){
7085             this.events = {};
7086         }
7087         Roo.applyIf(this.events, o);
7088     },
7089
7090     /**
7091      * Checks to see if this object has any listeners for a specified event
7092      * @param {String} eventName The name of the event to check for
7093      * @return {Boolean} True if the event is being listened for, else false
7094      */
7095     hasListener : function(eventName){
7096         var e = this.events[eventName];
7097         return typeof e == "object" && e.listeners.length > 0;
7098     }
7099 };
7100 /**
7101  * Appends an event handler to this element (shorthand for addListener)
7102  * @param {String}   eventName     The type of event to listen for
7103  * @param {Function} handler        The method the event invokes
7104  * @param {Object}   scope (optional) The scope in which to execute the handler
7105  * function. The handler function's "this" context.
7106  * @param {Object}   options  (optional)
7107  * @method
7108  */
7109 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7110 /**
7111  * Removes a listener (shorthand for removeListener)
7112  * @param {String}   eventName     The type of event to listen for
7113  * @param {Function} handler        The handler to remove
7114  * @param {Object}   scope  (optional) The scope (this object) for the handler
7115  * @method
7116  */
7117 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7118
7119 /**
7120  * Starts capture on the specified Observable. All events will be passed
7121  * to the supplied function with the event name + standard signature of the event
7122  * <b>before</b> the event is fired. If the supplied function returns false,
7123  * the event will not fire.
7124  * @param {Observable} o The Observable to capture
7125  * @param {Function} fn The function to call
7126  * @param {Object} scope (optional) The scope (this object) for the fn
7127  * @static
7128  */
7129 Roo.util.Observable.capture = function(o, fn, scope){
7130     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7131 };
7132
7133 /**
7134  * Removes <b>all</b> added captures from the Observable.
7135  * @param {Observable} o The Observable to release
7136  * @static
7137  */
7138 Roo.util.Observable.releaseCapture = function(o){
7139     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7140 };
7141
7142 (function(){
7143
7144     var createBuffered = function(h, o, scope){
7145         var task = new Roo.util.DelayedTask();
7146         return function(){
7147             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7148         };
7149     };
7150
7151     var createSingle = function(h, e, fn, scope){
7152         return function(){
7153             e.removeListener(fn, scope);
7154             return h.apply(scope, arguments);
7155         };
7156     };
7157
7158     var createDelayed = function(h, o, scope){
7159         return function(){
7160             var args = Array.prototype.slice.call(arguments, 0);
7161             setTimeout(function(){
7162                 h.apply(scope, args);
7163             }, o.delay || 10);
7164         };
7165     };
7166
7167     Roo.util.Event = function(obj, name){
7168         this.name = name;
7169         this.obj = obj;
7170         this.listeners = [];
7171     };
7172
7173     Roo.util.Event.prototype = {
7174         addListener : function(fn, scope, options){
7175             var o = options || {};
7176             scope = scope || this.obj;
7177             if(!this.isListening(fn, scope)){
7178                 var l = {fn: fn, scope: scope, options: o};
7179                 var h = fn;
7180                 if(o.delay){
7181                     h = createDelayed(h, o, scope);
7182                 }
7183                 if(o.single){
7184                     h = createSingle(h, this, fn, scope);
7185                 }
7186                 if(o.buffer){
7187                     h = createBuffered(h, o, scope);
7188                 }
7189                 l.fireFn = h;
7190                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7191                     this.listeners.push(l);
7192                 }else{
7193                     this.listeners = this.listeners.slice(0);
7194                     this.listeners.push(l);
7195                 }
7196             }
7197         },
7198
7199         findListener : function(fn, scope){
7200             scope = scope || this.obj;
7201             var ls = this.listeners;
7202             for(var i = 0, len = ls.length; i < len; i++){
7203                 var l = ls[i];
7204                 if(l.fn == fn && l.scope == scope){
7205                     return i;
7206                 }
7207             }
7208             return -1;
7209         },
7210
7211         isListening : function(fn, scope){
7212             return this.findListener(fn, scope) != -1;
7213         },
7214
7215         removeListener : function(fn, scope){
7216             var index;
7217             if((index = this.findListener(fn, scope)) != -1){
7218                 if(!this.firing){
7219                     this.listeners.splice(index, 1);
7220                 }else{
7221                     this.listeners = this.listeners.slice(0);
7222                     this.listeners.splice(index, 1);
7223                 }
7224                 return true;
7225             }
7226             return false;
7227         },
7228
7229         clearListeners : function(){
7230             this.listeners = [];
7231         },
7232
7233         fire : function(){
7234             var ls = this.listeners, scope, len = ls.length;
7235             if(len > 0){
7236                 this.firing = true;
7237                 var args = Array.prototype.slice.call(arguments, 0);                
7238                 for(var i = 0; i < len; i++){
7239                     var l = ls[i];
7240                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7241                         this.firing = false;
7242                         return false;
7243                     }
7244                 }
7245                 this.firing = false;
7246             }
7247             return true;
7248         }
7249     };
7250 })();/*
7251  * RooJS Library 
7252  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7253  *
7254  * Licence LGPL 
7255  *
7256  */
7257  
7258 /**
7259  * @class Roo.Document
7260  * @extends Roo.util.Observable
7261  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7262  * 
7263  * @param {Object} config the methods and properties of the 'base' class for the application.
7264  * 
7265  *  Generic Page handler - implement this to start your app..
7266  * 
7267  * eg.
7268  *  MyProject = new Roo.Document({
7269         events : {
7270             'load' : true // your events..
7271         },
7272         listeners : {
7273             'ready' : function() {
7274                 // fired on Roo.onReady()
7275             }
7276         }
7277  * 
7278  */
7279 Roo.Document = function(cfg) {
7280      
7281     this.addEvents({ 
7282         'ready' : true
7283     });
7284     Roo.util.Observable.call(this,cfg);
7285     
7286     var _this = this;
7287     
7288     Roo.onReady(function() {
7289         _this.fireEvent('ready');
7290     },null,false);
7291     
7292     
7293 }
7294
7295 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7296  * Based on:
7297  * Ext JS Library 1.1.1
7298  * Copyright(c) 2006-2007, Ext JS, LLC.
7299  *
7300  * Originally Released Under LGPL - original licence link has changed is not relivant.
7301  *
7302  * Fork - LGPL
7303  * <script type="text/javascript">
7304  */
7305
7306 /**
7307  * @class Roo.EventManager
7308  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7309  * several useful events directly.
7310  * See {@link Roo.EventObject} for more details on normalized event objects.
7311  * @static
7312  */
7313 Roo.EventManager = function(){
7314     var docReadyEvent, docReadyProcId, docReadyState = false;
7315     var resizeEvent, resizeTask, textEvent, textSize;
7316     var E = Roo.lib.Event;
7317     var D = Roo.lib.Dom;
7318
7319     
7320     
7321
7322     var fireDocReady = function(){
7323         if(!docReadyState){
7324             docReadyState = true;
7325             Roo.isReady = true;
7326             if(docReadyProcId){
7327                 clearInterval(docReadyProcId);
7328             }
7329             if(Roo.isGecko || Roo.isOpera) {
7330                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7331             }
7332             if(Roo.isIE){
7333                 var defer = document.getElementById("ie-deferred-loader");
7334                 if(defer){
7335                     defer.onreadystatechange = null;
7336                     defer.parentNode.removeChild(defer);
7337                 }
7338             }
7339             if(docReadyEvent){
7340                 docReadyEvent.fire();
7341                 docReadyEvent.clearListeners();
7342             }
7343         }
7344     };
7345     
7346     var initDocReady = function(){
7347         docReadyEvent = new Roo.util.Event();
7348         if(Roo.isGecko || Roo.isOpera) {
7349             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7350         }else if(Roo.isIE){
7351             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7352             var defer = document.getElementById("ie-deferred-loader");
7353             defer.onreadystatechange = function(){
7354                 if(this.readyState == "complete"){
7355                     fireDocReady();
7356                 }
7357             };
7358         }else if(Roo.isSafari){ 
7359             docReadyProcId = setInterval(function(){
7360                 var rs = document.readyState;
7361                 if(rs == "complete") {
7362                     fireDocReady();     
7363                  }
7364             }, 10);
7365         }
7366         // no matter what, make sure it fires on load
7367         E.on(window, "load", fireDocReady);
7368     };
7369
7370     var createBuffered = function(h, o){
7371         var task = new Roo.util.DelayedTask(h);
7372         return function(e){
7373             // create new event object impl so new events don't wipe out properties
7374             e = new Roo.EventObjectImpl(e);
7375             task.delay(o.buffer, h, null, [e]);
7376         };
7377     };
7378
7379     var createSingle = function(h, el, ename, fn){
7380         return function(e){
7381             Roo.EventManager.removeListener(el, ename, fn);
7382             h(e);
7383         };
7384     };
7385
7386     var createDelayed = function(h, o){
7387         return function(e){
7388             // create new event object impl so new events don't wipe out properties
7389             e = new Roo.EventObjectImpl(e);
7390             setTimeout(function(){
7391                 h(e);
7392             }, o.delay || 10);
7393         };
7394     };
7395     var transitionEndVal = false;
7396     
7397     var transitionEnd = function()
7398     {
7399         if (transitionEndVal) {
7400             return transitionEndVal;
7401         }
7402         var el = document.createElement('div');
7403
7404         var transEndEventNames = {
7405             WebkitTransition : 'webkitTransitionEnd',
7406             MozTransition    : 'transitionend',
7407             OTransition      : 'oTransitionEnd otransitionend',
7408             transition       : 'transitionend'
7409         };
7410     
7411         for (var name in transEndEventNames) {
7412             if (el.style[name] !== undefined) {
7413                 transitionEndVal = transEndEventNames[name];
7414                 return  transitionEndVal ;
7415             }
7416         }
7417     }
7418     
7419   
7420
7421     var listen = function(element, ename, opt, fn, scope)
7422     {
7423         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7424         fn = fn || o.fn; scope = scope || o.scope;
7425         var el = Roo.getDom(element);
7426         
7427         
7428         if(!el){
7429             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7430         }
7431         
7432         if (ename == 'transitionend') {
7433             ename = transitionEnd();
7434         }
7435         var h = function(e){
7436             e = Roo.EventObject.setEvent(e);
7437             var t;
7438             if(o.delegate){
7439                 t = e.getTarget(o.delegate, el);
7440                 if(!t){
7441                     return;
7442                 }
7443             }else{
7444                 t = e.target;
7445             }
7446             if(o.stopEvent === true){
7447                 e.stopEvent();
7448             }
7449             if(o.preventDefault === true){
7450                e.preventDefault();
7451             }
7452             if(o.stopPropagation === true){
7453                 e.stopPropagation();
7454             }
7455
7456             if(o.normalized === false){
7457                 e = e.browserEvent;
7458             }
7459
7460             fn.call(scope || el, e, t, o);
7461         };
7462         if(o.delay){
7463             h = createDelayed(h, o);
7464         }
7465         if(o.single){
7466             h = createSingle(h, el, ename, fn);
7467         }
7468         if(o.buffer){
7469             h = createBuffered(h, o);
7470         }
7471         
7472         fn._handlers = fn._handlers || [];
7473         
7474         
7475         fn._handlers.push([Roo.id(el), ename, h]);
7476         
7477         
7478          
7479         E.on(el, ename, h); // this adds the actuall listener to the object..
7480         
7481         
7482         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7483             el.addEventListener("DOMMouseScroll", h, false);
7484             E.on(window, 'unload', function(){
7485                 el.removeEventListener("DOMMouseScroll", h, false);
7486             });
7487         }
7488         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7489             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7490         }
7491         return h;
7492     };
7493
7494     var stopListening = function(el, ename, fn){
7495         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7496         if(hds){
7497             for(var i = 0, len = hds.length; i < len; i++){
7498                 var h = hds[i];
7499                 if(h[0] == id && h[1] == ename){
7500                     hd = h[2];
7501                     hds.splice(i, 1);
7502                     break;
7503                 }
7504             }
7505         }
7506         E.un(el, ename, hd);
7507         el = Roo.getDom(el);
7508         if(ename == "mousewheel" && el.addEventListener){
7509             el.removeEventListener("DOMMouseScroll", hd, false);
7510         }
7511         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7512             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7513         }
7514     };
7515
7516     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7517     
7518     var pub = {
7519         
7520         
7521         /** 
7522          * Fix for doc tools
7523          * @scope Roo.EventManager
7524          */
7525         
7526         
7527         /** 
7528          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7529          * object with a Roo.EventObject
7530          * @param {Function} fn        The method the event invokes
7531          * @param {Object}   scope    An object that becomes the scope of the handler
7532          * @param {boolean}  override If true, the obj passed in becomes
7533          *                             the execution scope of the listener
7534          * @return {Function} The wrapped function
7535          * @deprecated
7536          */
7537         wrap : function(fn, scope, override){
7538             return function(e){
7539                 Roo.EventObject.setEvent(e);
7540                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7541             };
7542         },
7543         
7544         /**
7545      * Appends an event handler to an element (shorthand for addListener)
7546      * @param {String/HTMLElement}   element        The html element or id to assign the
7547      * @param {String}   eventName The type of event to listen for
7548      * @param {Function} handler The method the event invokes
7549      * @param {Object}   scope (optional) The scope in which to execute the handler
7550      * function. The handler function's "this" context.
7551      * @param {Object}   options (optional) An object containing handler configuration
7552      * properties. This may contain any of the following properties:<ul>
7553      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7554      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7555      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7556      * <li>preventDefault {Boolean} True to prevent the default action</li>
7557      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7558      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7559      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7560      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7561      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7562      * by the specified number of milliseconds. If the event fires again within that time, the original
7563      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7564      * </ul><br>
7565      * <p>
7566      * <b>Combining Options</b><br>
7567      * Using the options argument, it is possible to combine different types of listeners:<br>
7568      * <br>
7569      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7570      * Code:<pre><code>
7571 el.on('click', this.onClick, this, {
7572     single: true,
7573     delay: 100,
7574     stopEvent : true,
7575     forumId: 4
7576 });</code></pre>
7577      * <p>
7578      * <b>Attaching multiple handlers in 1 call</b><br>
7579       * The method also allows for a single argument to be passed which is a config object containing properties
7580      * which specify multiple handlers.
7581      * <p>
7582      * Code:<pre><code>
7583 el.on({
7584     'click' : {
7585         fn: this.onClick
7586         scope: this,
7587         delay: 100
7588     },
7589     'mouseover' : {
7590         fn: this.onMouseOver
7591         scope: this
7592     },
7593     'mouseout' : {
7594         fn: this.onMouseOut
7595         scope: this
7596     }
7597 });</code></pre>
7598      * <p>
7599      * Or a shorthand syntax:<br>
7600      * Code:<pre><code>
7601 el.on({
7602     'click' : this.onClick,
7603     'mouseover' : this.onMouseOver,
7604     'mouseout' : this.onMouseOut
7605     scope: this
7606 });</code></pre>
7607      */
7608         addListener : function(element, eventName, fn, scope, options){
7609             if(typeof eventName == "object"){
7610                 var o = eventName;
7611                 for(var e in o){
7612                     if(propRe.test(e)){
7613                         continue;
7614                     }
7615                     if(typeof o[e] == "function"){
7616                         // shared options
7617                         listen(element, e, o, o[e], o.scope);
7618                     }else{
7619                         // individual options
7620                         listen(element, e, o[e]);
7621                     }
7622                 }
7623                 return;
7624             }
7625             return listen(element, eventName, options, fn, scope);
7626         },
7627         
7628         /**
7629          * Removes an event handler
7630          *
7631          * @param {String/HTMLElement}   element        The id or html element to remove the 
7632          *                             event from
7633          * @param {String}   eventName     The type of event
7634          * @param {Function} fn
7635          * @return {Boolean} True if a listener was actually removed
7636          */
7637         removeListener : function(element, eventName, fn){
7638             return stopListening(element, eventName, fn);
7639         },
7640         
7641         /**
7642          * Fires when the document is ready (before onload and before images are loaded). Can be 
7643          * accessed shorthanded Roo.onReady().
7644          * @param {Function} fn        The method the event invokes
7645          * @param {Object}   scope    An  object that becomes the scope of the handler
7646          * @param {boolean}  options
7647          */
7648         onDocumentReady : function(fn, scope, options){
7649             if(docReadyState){ // if it already fired
7650                 docReadyEvent.addListener(fn, scope, options);
7651                 docReadyEvent.fire();
7652                 docReadyEvent.clearListeners();
7653                 return;
7654             }
7655             if(!docReadyEvent){
7656                 initDocReady();
7657             }
7658             docReadyEvent.addListener(fn, scope, options);
7659         },
7660         
7661         /**
7662          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7663          * @param {Function} fn        The method the event invokes
7664          * @param {Object}   scope    An object that becomes the scope of the handler
7665          * @param {boolean}  options
7666          */
7667         onWindowResize : function(fn, scope, options)
7668         {
7669             if(!resizeEvent){
7670                 resizeEvent = new Roo.util.Event();
7671                 resizeTask = new Roo.util.DelayedTask(function(){
7672                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7673                 });
7674                 E.on(window, "resize", function()
7675                 {
7676                     if (Roo.isIE) {
7677                         resizeTask.delay(50);
7678                     } else {
7679                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7680                     }
7681                 });
7682             }
7683             resizeEvent.addListener(fn, scope, options);
7684         },
7685
7686         /**
7687          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7688          * @param {Function} fn        The method the event invokes
7689          * @param {Object}   scope    An object that becomes the scope of the handler
7690          * @param {boolean}  options
7691          */
7692         onTextResize : function(fn, scope, options){
7693             if(!textEvent){
7694                 textEvent = new Roo.util.Event();
7695                 var textEl = new Roo.Element(document.createElement('div'));
7696                 textEl.dom.className = 'x-text-resize';
7697                 textEl.dom.innerHTML = 'X';
7698                 textEl.appendTo(document.body);
7699                 textSize = textEl.dom.offsetHeight;
7700                 setInterval(function(){
7701                     if(textEl.dom.offsetHeight != textSize){
7702                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7703                     }
7704                 }, this.textResizeInterval);
7705             }
7706             textEvent.addListener(fn, scope, options);
7707         },
7708
7709         /**
7710          * Removes the passed window resize listener.
7711          * @param {Function} fn        The method the event invokes
7712          * @param {Object}   scope    The scope of handler
7713          */
7714         removeResizeListener : function(fn, scope){
7715             if(resizeEvent){
7716                 resizeEvent.removeListener(fn, scope);
7717             }
7718         },
7719
7720         // private
7721         fireResize : function(){
7722             if(resizeEvent){
7723                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7724             }   
7725         },
7726         /**
7727          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7728          */
7729         ieDeferSrc : false,
7730         /**
7731          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7732          */
7733         textResizeInterval : 50
7734     };
7735     
7736     /**
7737      * Fix for doc tools
7738      * @scopeAlias pub=Roo.EventManager
7739      */
7740     
7741      /**
7742      * Appends an event handler to an element (shorthand for addListener)
7743      * @param {String/HTMLElement}   element        The html element or id to assign the
7744      * @param {String}   eventName The type of event to listen for
7745      * @param {Function} handler The method the event invokes
7746      * @param {Object}   scope (optional) The scope in which to execute the handler
7747      * function. The handler function's "this" context.
7748      * @param {Object}   options (optional) An object containing handler configuration
7749      * properties. This may contain any of the following properties:<ul>
7750      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7751      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7752      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7753      * <li>preventDefault {Boolean} True to prevent the default action</li>
7754      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7755      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7756      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7757      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7758      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7759      * by the specified number of milliseconds. If the event fires again within that time, the original
7760      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7761      * </ul><br>
7762      * <p>
7763      * <b>Combining Options</b><br>
7764      * Using the options argument, it is possible to combine different types of listeners:<br>
7765      * <br>
7766      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7767      * Code:<pre><code>
7768 el.on('click', this.onClick, this, {
7769     single: true,
7770     delay: 100,
7771     stopEvent : true,
7772     forumId: 4
7773 });</code></pre>
7774      * <p>
7775      * <b>Attaching multiple handlers in 1 call</b><br>
7776       * The method also allows for a single argument to be passed which is a config object containing properties
7777      * which specify multiple handlers.
7778      * <p>
7779      * Code:<pre><code>
7780 el.on({
7781     'click' : {
7782         fn: this.onClick
7783         scope: this,
7784         delay: 100
7785     },
7786     'mouseover' : {
7787         fn: this.onMouseOver
7788         scope: this
7789     },
7790     'mouseout' : {
7791         fn: this.onMouseOut
7792         scope: this
7793     }
7794 });</code></pre>
7795      * <p>
7796      * Or a shorthand syntax:<br>
7797      * Code:<pre><code>
7798 el.on({
7799     'click' : this.onClick,
7800     'mouseover' : this.onMouseOver,
7801     'mouseout' : this.onMouseOut
7802     scope: this
7803 });</code></pre>
7804      */
7805     pub.on = pub.addListener;
7806     pub.un = pub.removeListener;
7807
7808     pub.stoppedMouseDownEvent = new Roo.util.Event();
7809     return pub;
7810 }();
7811 /**
7812   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7813   * @param {Function} fn        The method the event invokes
7814   * @param {Object}   scope    An  object that becomes the scope of the handler
7815   * @param {boolean}  override If true, the obj passed in becomes
7816   *                             the execution scope of the listener
7817   * @member Roo
7818   * @method onReady
7819  */
7820 Roo.onReady = Roo.EventManager.onDocumentReady;
7821
7822 Roo.onReady(function(){
7823     var bd = Roo.get(document.body);
7824     if(!bd){ return; }
7825
7826     var cls = [
7827             Roo.isIE ? "roo-ie"
7828             : Roo.isIE11 ? "roo-ie11"
7829             : Roo.isEdge ? "roo-edge"
7830             : Roo.isGecko ? "roo-gecko"
7831             : Roo.isOpera ? "roo-opera"
7832             : Roo.isSafari ? "roo-safari" : ""];
7833
7834     if(Roo.isMac){
7835         cls.push("roo-mac");
7836     }
7837     if(Roo.isLinux){
7838         cls.push("roo-linux");
7839     }
7840     if(Roo.isIOS){
7841         cls.push("roo-ios");
7842     }
7843     if(Roo.isTouch){
7844         cls.push("roo-touch");
7845     }
7846     if(Roo.isBorderBox){
7847         cls.push('roo-border-box');
7848     }
7849     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7850         var p = bd.dom.parentNode;
7851         if(p){
7852             p.className += ' roo-strict';
7853         }
7854     }
7855     bd.addClass(cls.join(' '));
7856 });
7857
7858 /**
7859  * @class Roo.EventObject
7860  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7861  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7862  * Example:
7863  * <pre><code>
7864  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7865     e.preventDefault();
7866     var target = e.getTarget();
7867     ...
7868  }
7869  var myDiv = Roo.get("myDiv");
7870  myDiv.on("click", handleClick);
7871  //or
7872  Roo.EventManager.on("myDiv", 'click', handleClick);
7873  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7874  </code></pre>
7875  * @static
7876  */
7877 Roo.EventObject = function(){
7878     
7879     var E = Roo.lib.Event;
7880     
7881     // safari keypress events for special keys return bad keycodes
7882     var safariKeys = {
7883         63234 : 37, // left
7884         63235 : 39, // right
7885         63232 : 38, // up
7886         63233 : 40, // down
7887         63276 : 33, // page up
7888         63277 : 34, // page down
7889         63272 : 46, // delete
7890         63273 : 36, // home
7891         63275 : 35  // end
7892     };
7893
7894     // normalize button clicks
7895     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7896                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7897
7898     Roo.EventObjectImpl = function(e){
7899         if(e){
7900             this.setEvent(e.browserEvent || e);
7901         }
7902     };
7903     Roo.EventObjectImpl.prototype = {
7904         /**
7905          * Used to fix doc tools.
7906          * @scope Roo.EventObject.prototype
7907          */
7908             
7909
7910         
7911         
7912         /** The normal browser event */
7913         browserEvent : null,
7914         /** The button pressed in a mouse event */
7915         button : -1,
7916         /** True if the shift key was down during the event */
7917         shiftKey : false,
7918         /** True if the control key was down during the event */
7919         ctrlKey : false,
7920         /** True if the alt key was down during the event */
7921         altKey : false,
7922
7923         /** Key constant 
7924         * @type Number */
7925         BACKSPACE : 8,
7926         /** Key constant 
7927         * @type Number */
7928         TAB : 9,
7929         /** Key constant 
7930         * @type Number */
7931         RETURN : 13,
7932         /** Key constant 
7933         * @type Number */
7934         ENTER : 13,
7935         /** Key constant 
7936         * @type Number */
7937         SHIFT : 16,
7938         /** Key constant 
7939         * @type Number */
7940         CONTROL : 17,
7941         /** Key constant 
7942         * @type Number */
7943         ESC : 27,
7944         /** Key constant 
7945         * @type Number */
7946         SPACE : 32,
7947         /** Key constant 
7948         * @type Number */
7949         PAGEUP : 33,
7950         /** Key constant 
7951         * @type Number */
7952         PAGEDOWN : 34,
7953         /** Key constant 
7954         * @type Number */
7955         END : 35,
7956         /** Key constant 
7957         * @type Number */
7958         HOME : 36,
7959         /** Key constant 
7960         * @type Number */
7961         LEFT : 37,
7962         /** Key constant 
7963         * @type Number */
7964         UP : 38,
7965         /** Key constant 
7966         * @type Number */
7967         RIGHT : 39,
7968         /** Key constant 
7969         * @type Number */
7970         DOWN : 40,
7971         /** Key constant 
7972         * @type Number */
7973         DELETE : 46,
7974         /** Key constant 
7975         * @type Number */
7976         F5 : 116,
7977
7978            /** @private */
7979         setEvent : function(e){
7980             if(e == this || (e && e.browserEvent)){ // already wrapped
7981                 return e;
7982             }
7983             this.browserEvent = e;
7984             if(e){
7985                 // normalize buttons
7986                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7987                 if(e.type == 'click' && this.button == -1){
7988                     this.button = 0;
7989                 }
7990                 this.type = e.type;
7991                 this.shiftKey = e.shiftKey;
7992                 // mac metaKey behaves like ctrlKey
7993                 this.ctrlKey = e.ctrlKey || e.metaKey;
7994                 this.altKey = e.altKey;
7995                 // in getKey these will be normalized for the mac
7996                 this.keyCode = e.keyCode;
7997                 // keyup warnings on firefox.
7998                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7999                 // cache the target for the delayed and or buffered events
8000                 this.target = E.getTarget(e);
8001                 // same for XY
8002                 this.xy = E.getXY(e);
8003             }else{
8004                 this.button = -1;
8005                 this.shiftKey = false;
8006                 this.ctrlKey = false;
8007                 this.altKey = false;
8008                 this.keyCode = 0;
8009                 this.charCode =0;
8010                 this.target = null;
8011                 this.xy = [0, 0];
8012             }
8013             return this;
8014         },
8015
8016         /**
8017          * Stop the event (preventDefault and stopPropagation)
8018          */
8019         stopEvent : function(){
8020             if(this.browserEvent){
8021                 if(this.browserEvent.type == 'mousedown'){
8022                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8023                 }
8024                 E.stopEvent(this.browserEvent);
8025             }
8026         },
8027
8028         /**
8029          * Prevents the browsers default handling of the event.
8030          */
8031         preventDefault : function(){
8032             if(this.browserEvent){
8033                 E.preventDefault(this.browserEvent);
8034             }
8035         },
8036
8037         /** @private */
8038         isNavKeyPress : function(){
8039             var k = this.keyCode;
8040             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8041             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8042         },
8043
8044         isSpecialKey : function(){
8045             var k = this.keyCode;
8046             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8047             (k == 16) || (k == 17) ||
8048             (k >= 18 && k <= 20) ||
8049             (k >= 33 && k <= 35) ||
8050             (k >= 36 && k <= 39) ||
8051             (k >= 44 && k <= 45);
8052         },
8053         /**
8054          * Cancels bubbling of the event.
8055          */
8056         stopPropagation : function(){
8057             if(this.browserEvent){
8058                 if(this.type == 'mousedown'){
8059                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8060                 }
8061                 E.stopPropagation(this.browserEvent);
8062             }
8063         },
8064
8065         /**
8066          * Gets the key code for the event.
8067          * @return {Number}
8068          */
8069         getCharCode : function(){
8070             return this.charCode || this.keyCode;
8071         },
8072
8073         /**
8074          * Returns a normalized keyCode for the event.
8075          * @return {Number} The key code
8076          */
8077         getKey : function(){
8078             var k = this.keyCode || this.charCode;
8079             return Roo.isSafari ? (safariKeys[k] || k) : k;
8080         },
8081
8082         /**
8083          * Gets the x coordinate of the event.
8084          * @return {Number}
8085          */
8086         getPageX : function(){
8087             return this.xy[0];
8088         },
8089
8090         /**
8091          * Gets the y coordinate of the event.
8092          * @return {Number}
8093          */
8094         getPageY : function(){
8095             return this.xy[1];
8096         },
8097
8098         /**
8099          * Gets the time of the event.
8100          * @return {Number}
8101          */
8102         getTime : function(){
8103             if(this.browserEvent){
8104                 return E.getTime(this.browserEvent);
8105             }
8106             return null;
8107         },
8108
8109         /**
8110          * Gets the page coordinates of the event.
8111          * @return {Array} The xy values like [x, y]
8112          */
8113         getXY : function(){
8114             return this.xy;
8115         },
8116
8117         /**
8118          * Gets the target for the event.
8119          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8120          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8121                 search as a number or element (defaults to 10 || document.body)
8122          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8123          * @return {HTMLelement}
8124          */
8125         getTarget : function(selector, maxDepth, returnEl){
8126             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8127         },
8128         /**
8129          * Gets the related target.
8130          * @return {HTMLElement}
8131          */
8132         getRelatedTarget : function(){
8133             if(this.browserEvent){
8134                 return E.getRelatedTarget(this.browserEvent);
8135             }
8136             return null;
8137         },
8138
8139         /**
8140          * Normalizes mouse wheel delta across browsers
8141          * @return {Number} The delta
8142          */
8143         getWheelDelta : function(){
8144             var e = this.browserEvent;
8145             var delta = 0;
8146             if(e.wheelDelta){ /* IE/Opera. */
8147                 delta = e.wheelDelta/120;
8148             }else if(e.detail){ /* Mozilla case. */
8149                 delta = -e.detail/3;
8150             }
8151             return delta;
8152         },
8153
8154         /**
8155          * Returns true if the control, meta, shift or alt key was pressed during this event.
8156          * @return {Boolean}
8157          */
8158         hasModifier : function(){
8159             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8160         },
8161
8162         /**
8163          * Returns true if the target of this event equals el or is a child of el
8164          * @param {String/HTMLElement/Element} el
8165          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8166          * @return {Boolean}
8167          */
8168         within : function(el, related){
8169             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8170             return t && Roo.fly(el).contains(t);
8171         },
8172
8173         getPoint : function(){
8174             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8175         }
8176     };
8177
8178     return new Roo.EventObjectImpl();
8179 }();
8180             
8181     /*
8182  * Based on:
8183  * Ext JS Library 1.1.1
8184  * Copyright(c) 2006-2007, Ext JS, LLC.
8185  *
8186  * Originally Released Under LGPL - original licence link has changed is not relivant.
8187  *
8188  * Fork - LGPL
8189  * <script type="text/javascript">
8190  */
8191
8192  
8193 // was in Composite Element!??!?!
8194  
8195 (function(){
8196     var D = Roo.lib.Dom;
8197     var E = Roo.lib.Event;
8198     var A = Roo.lib.Anim;
8199
8200     // local style camelizing for speed
8201     var propCache = {};
8202     var camelRe = /(-[a-z])/gi;
8203     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8204     var view = document.defaultView;
8205
8206 /**
8207  * @class Roo.Element
8208  * Represents an Element in the DOM.<br><br>
8209  * Usage:<br>
8210 <pre><code>
8211 var el = Roo.get("my-div");
8212
8213 // or with getEl
8214 var el = getEl("my-div");
8215
8216 // or with a DOM element
8217 var el = Roo.get(myDivElement);
8218 </code></pre>
8219  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8220  * each call instead of constructing a new one.<br><br>
8221  * <b>Animations</b><br />
8222  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8223  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8224 <pre>
8225 Option    Default   Description
8226 --------- --------  ---------------------------------------------
8227 duration  .35       The duration of the animation in seconds
8228 easing    easeOut   The YUI easing method
8229 callback  none      A function to execute when the anim completes
8230 scope     this      The scope (this) of the callback function
8231 </pre>
8232 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8233 * manipulate the animation. Here's an example:
8234 <pre><code>
8235 var el = Roo.get("my-div");
8236
8237 // no animation
8238 el.setWidth(100);
8239
8240 // default animation
8241 el.setWidth(100, true);
8242
8243 // animation with some options set
8244 el.setWidth(100, {
8245     duration: 1,
8246     callback: this.foo,
8247     scope: this
8248 });
8249
8250 // using the "anim" property to get the Anim object
8251 var opt = {
8252     duration: 1,
8253     callback: this.foo,
8254     scope: this
8255 };
8256 el.setWidth(100, opt);
8257 ...
8258 if(opt.anim.isAnimated()){
8259     opt.anim.stop();
8260 }
8261 </code></pre>
8262 * <b> Composite (Collections of) Elements</b><br />
8263  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8264  * @constructor Create a new Element directly.
8265  * @param {String/HTMLElement} element
8266  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
8267  */
8268     Roo.Element = function(element, forceNew)
8269     {
8270         var dom = typeof element == "string" ?
8271                 document.getElementById(element) : element;
8272         
8273         this.listeners = {};
8274         
8275         if(!dom){ // invalid id/element
8276             return null;
8277         }
8278         var id = dom.id;
8279         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8280             return Roo.Element.cache[id];
8281         }
8282
8283         /**
8284          * The DOM element
8285          * @type HTMLElement
8286          */
8287         this.dom = dom;
8288
8289         /**
8290          * The DOM element ID
8291          * @type String
8292          */
8293         this.id = id || Roo.id(dom);
8294         
8295         return this; // assumed for cctor?
8296     };
8297
8298     var El = Roo.Element;
8299
8300     El.prototype = {
8301         /**
8302          * The element's default display mode  (defaults to "") 
8303          * @type String
8304          */
8305         originalDisplay : "",
8306
8307         
8308         // note this is overridden in BS version..
8309         visibilityMode : 1, 
8310         /**
8311          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8312          * @type String
8313          */
8314         defaultUnit : "px",
8315         
8316         /**
8317          * Sets the element's visibility mode. When setVisible() is called it
8318          * will use this to determine whether to set the visibility or the display property.
8319          * @param visMode Element.VISIBILITY or Element.DISPLAY
8320          * @return {Roo.Element} this
8321          */
8322         setVisibilityMode : function(visMode){
8323             this.visibilityMode = visMode;
8324             return this;
8325         },
8326         /**
8327          * Convenience method for setVisibilityMode(Element.DISPLAY)
8328          * @param {String} display (optional) What to set display to when visible
8329          * @return {Roo.Element} this
8330          */
8331         enableDisplayMode : function(display){
8332             this.setVisibilityMode(El.DISPLAY);
8333             if(typeof display != "undefined") { this.originalDisplay = display; }
8334             return this;
8335         },
8336
8337         /**
8338          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8339          * @param {String} selector The simple selector to test
8340          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8341                 search as a number or element (defaults to 10 || document.body)
8342          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8343          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8344          */
8345         findParent : function(simpleSelector, maxDepth, returnEl){
8346             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8347             maxDepth = maxDepth || 50;
8348             if(typeof maxDepth != "number"){
8349                 stopEl = Roo.getDom(maxDepth);
8350                 maxDepth = 10;
8351             }
8352             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8353                 if(dq.is(p, simpleSelector)){
8354                     return returnEl ? Roo.get(p) : p;
8355                 }
8356                 depth++;
8357                 p = p.parentNode;
8358             }
8359             return null;
8360         },
8361
8362
8363         /**
8364          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8365          * @param {String} selector The simple selector to test
8366          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8367                 search as a number or element (defaults to 10 || document.body)
8368          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8369          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8370          */
8371         findParentNode : function(simpleSelector, maxDepth, returnEl){
8372             var p = Roo.fly(this.dom.parentNode, '_internal');
8373             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8374         },
8375         
8376         /**
8377          * Looks at  the scrollable parent element
8378          */
8379         findScrollableParent : function()
8380         {
8381             var overflowRegex = /(auto|scroll)/;
8382             
8383             if(this.getStyle('position') === 'fixed'){
8384                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8385             }
8386             
8387             var excludeStaticParent = this.getStyle('position') === "absolute";
8388             
8389             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8390                 
8391                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8392                     continue;
8393                 }
8394                 
8395                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8396                     return parent;
8397                 }
8398                 
8399                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8400                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8401                 }
8402             }
8403             
8404             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8405         },
8406
8407         /**
8408          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8409          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8410          * @param {String} selector The simple selector to test
8411          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8412                 search as a number or element (defaults to 10 || document.body)
8413          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8414          */
8415         up : function(simpleSelector, maxDepth){
8416             return this.findParentNode(simpleSelector, maxDepth, true);
8417         },
8418
8419
8420
8421         /**
8422          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8423          * @param {String} selector The simple selector to test
8424          * @return {Boolean} True if this element matches the selector, else false
8425          */
8426         is : function(simpleSelector){
8427             return Roo.DomQuery.is(this.dom, simpleSelector);
8428         },
8429
8430         /**
8431          * Perform animation on this element.
8432          * @param {Object} args The YUI animation control args
8433          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8434          * @param {Function} onComplete (optional) Function to call when animation completes
8435          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8436          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8437          * @return {Roo.Element} this
8438          */
8439         animate : function(args, duration, onComplete, easing, animType){
8440             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8441             return this;
8442         },
8443
8444         /*
8445          * @private Internal animation call
8446          */
8447         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8448             animType = animType || 'run';
8449             opt = opt || {};
8450             var anim = Roo.lib.Anim[animType](
8451                 this.dom, args,
8452                 (opt.duration || defaultDur) || .35,
8453                 (opt.easing || defaultEase) || 'easeOut',
8454                 function(){
8455                     Roo.callback(cb, this);
8456                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8457                 },
8458                 this
8459             );
8460             opt.anim = anim;
8461             return anim;
8462         },
8463
8464         // private legacy anim prep
8465         preanim : function(a, i){
8466             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8467         },
8468
8469         /**
8470          * Removes worthless text nodes
8471          * @param {Boolean} forceReclean (optional) By default the element
8472          * keeps track if it has been cleaned already so
8473          * you can call this over and over. However, if you update the element and
8474          * need to force a reclean, you can pass true.
8475          */
8476         clean : function(forceReclean){
8477             if(this.isCleaned && forceReclean !== true){
8478                 return this;
8479             }
8480             var ns = /\S/;
8481             var d = this.dom, n = d.firstChild, ni = -1;
8482             while(n){
8483                 var nx = n.nextSibling;
8484                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8485                     d.removeChild(n);
8486                 }else{
8487                     n.nodeIndex = ++ni;
8488                 }
8489                 n = nx;
8490             }
8491             this.isCleaned = true;
8492             return this;
8493         },
8494
8495         // private
8496         calcOffsetsTo : function(el){
8497             el = Roo.get(el);
8498             var d = el.dom;
8499             var restorePos = false;
8500             if(el.getStyle('position') == 'static'){
8501                 el.position('relative');
8502                 restorePos = true;
8503             }
8504             var x = 0, y =0;
8505             var op = this.dom;
8506             while(op && op != d && op.tagName != 'HTML'){
8507                 x+= op.offsetLeft;
8508                 y+= op.offsetTop;
8509                 op = op.offsetParent;
8510             }
8511             if(restorePos){
8512                 el.position('static');
8513             }
8514             return [x, y];
8515         },
8516
8517         /**
8518          * Scrolls this element into view within the passed container.
8519          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8520          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8521          * @return {Roo.Element} this
8522          */
8523         scrollIntoView : function(container, hscroll){
8524             var c = Roo.getDom(container) || document.body;
8525             var el = this.dom;
8526
8527             var o = this.calcOffsetsTo(c),
8528                 l = o[0],
8529                 t = o[1],
8530                 b = t+el.offsetHeight,
8531                 r = l+el.offsetWidth;
8532
8533             var ch = c.clientHeight;
8534             var ct = parseInt(c.scrollTop, 10);
8535             var cl = parseInt(c.scrollLeft, 10);
8536             var cb = ct + ch;
8537             var cr = cl + c.clientWidth;
8538
8539             if(t < ct){
8540                 c.scrollTop = t;
8541             }else if(b > cb){
8542                 c.scrollTop = b-ch;
8543             }
8544
8545             if(hscroll !== false){
8546                 if(l < cl){
8547                     c.scrollLeft = l;
8548                 }else if(r > cr){
8549                     c.scrollLeft = r-c.clientWidth;
8550                 }
8551             }
8552             return this;
8553         },
8554
8555         // private
8556         scrollChildIntoView : function(child, hscroll){
8557             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8558         },
8559
8560         /**
8561          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8562          * the new height may not be available immediately.
8563          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8564          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8565          * @param {Function} onComplete (optional) Function to call when animation completes
8566          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8567          * @return {Roo.Element} this
8568          */
8569         autoHeight : function(animate, duration, onComplete, easing){
8570             var oldHeight = this.getHeight();
8571             this.clip();
8572             this.setHeight(1); // force clipping
8573             setTimeout(function(){
8574                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8575                 if(!animate){
8576                     this.setHeight(height);
8577                     this.unclip();
8578                     if(typeof onComplete == "function"){
8579                         onComplete();
8580                     }
8581                 }else{
8582                     this.setHeight(oldHeight); // restore original height
8583                     this.setHeight(height, animate, duration, function(){
8584                         this.unclip();
8585                         if(typeof onComplete == "function") { onComplete(); }
8586                     }.createDelegate(this), easing);
8587                 }
8588             }.createDelegate(this), 0);
8589             return this;
8590         },
8591
8592         /**
8593          * Returns true if this element is an ancestor of the passed element
8594          * @param {HTMLElement/String} el The element to check
8595          * @return {Boolean} True if this element is an ancestor of el, else false
8596          */
8597         contains : function(el){
8598             if(!el){return false;}
8599             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8600         },
8601
8602         /**
8603          * Checks whether the element is currently visible using both visibility and display properties.
8604          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8605          * @return {Boolean} True if the element is currently visible, else false
8606          */
8607         isVisible : function(deep) {
8608             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8609             if(deep !== true || !vis){
8610                 return vis;
8611             }
8612             var p = this.dom.parentNode;
8613             while(p && p.tagName.toLowerCase() != "body"){
8614                 if(!Roo.fly(p, '_isVisible').isVisible()){
8615                     return false;
8616                 }
8617                 p = p.parentNode;
8618             }
8619             return true;
8620         },
8621
8622         /**
8623          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8624          * @param {String} selector The CSS selector
8625          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8626          * @return {CompositeElement/CompositeElementLite} The composite element
8627          */
8628         select : function(selector, unique){
8629             return El.select(selector, unique, this.dom);
8630         },
8631
8632         /**
8633          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8634          * @param {String} selector The CSS selector
8635          * @return {Array} An array of the matched nodes
8636          */
8637         query : function(selector, unique){
8638             return Roo.DomQuery.select(selector, this.dom);
8639         },
8640
8641         /**
8642          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8643          * @param {String} selector The CSS selector
8644          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8645          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8646          */
8647         child : function(selector, returnDom){
8648             var n = Roo.DomQuery.selectNode(selector, this.dom);
8649             return returnDom ? n : Roo.get(n);
8650         },
8651
8652         /**
8653          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8654          * @param {String} selector The CSS selector
8655          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8656          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8657          */
8658         down : function(selector, returnDom){
8659             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8660             return returnDom ? n : Roo.get(n);
8661         },
8662
8663         /**
8664          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8665          * @param {String} group The group the DD object is member of
8666          * @param {Object} config The DD config object
8667          * @param {Object} overrides An object containing methods to override/implement on the DD object
8668          * @return {Roo.dd.DD} The DD object
8669          */
8670         initDD : function(group, config, overrides){
8671             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8672             return Roo.apply(dd, overrides);
8673         },
8674
8675         /**
8676          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8677          * @param {String} group The group the DDProxy object is member of
8678          * @param {Object} config The DDProxy config object
8679          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8680          * @return {Roo.dd.DDProxy} The DDProxy object
8681          */
8682         initDDProxy : function(group, config, overrides){
8683             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8684             return Roo.apply(dd, overrides);
8685         },
8686
8687         /**
8688          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8689          * @param {String} group The group the DDTarget object is member of
8690          * @param {Object} config The DDTarget config object
8691          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8692          * @return {Roo.dd.DDTarget} The DDTarget object
8693          */
8694         initDDTarget : function(group, config, overrides){
8695             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8696             return Roo.apply(dd, overrides);
8697         },
8698
8699         /**
8700          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8701          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8702          * @param {Boolean} visible Whether the element is visible
8703          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8704          * @return {Roo.Element} this
8705          */
8706          setVisible : function(visible, animate){
8707             if(!animate || !A){
8708                 if(this.visibilityMode == El.DISPLAY){
8709                     this.setDisplayed(visible);
8710                 }else{
8711                     this.fixDisplay();
8712                     this.dom.style.visibility = visible ? "visible" : "hidden";
8713                 }
8714             }else{
8715                 // closure for composites
8716                 var dom = this.dom;
8717                 var visMode = this.visibilityMode;
8718                 if(visible){
8719                     this.setOpacity(.01);
8720                     this.setVisible(true);
8721                 }
8722                 this.anim({opacity: { to: (visible?1:0) }},
8723                       this.preanim(arguments, 1),
8724                       null, .35, 'easeIn', function(){
8725                          if(!visible){
8726                              if(visMode == El.DISPLAY){
8727                                  dom.style.display = "none";
8728                              }else{
8729                                  dom.style.visibility = "hidden";
8730                              }
8731                              Roo.get(dom).setOpacity(1);
8732                          }
8733                      });
8734             }
8735             return this;
8736         },
8737
8738         /**
8739          * Returns true if display is not "none"
8740          * @return {Boolean}
8741          */
8742         isDisplayed : function() {
8743             return this.getStyle("display") != "none";
8744         },
8745
8746         /**
8747          * Toggles the element's visibility or display, depending on visibility mode.
8748          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8749          * @return {Roo.Element} this
8750          */
8751         toggle : function(animate){
8752             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8753             return this;
8754         },
8755
8756         /**
8757          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8758          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8759          * @return {Roo.Element} this
8760          */
8761         setDisplayed : function(value) {
8762             if(typeof value == "boolean"){
8763                value = value ? this.originalDisplay : "none";
8764             }
8765             this.setStyle("display", value);
8766             return this;
8767         },
8768
8769         /**
8770          * Tries to focus the element. Any exceptions are caught and ignored.
8771          * @return {Roo.Element} this
8772          */
8773         focus : function() {
8774             try{
8775                 this.dom.focus();
8776             }catch(e){}
8777             return this;
8778         },
8779
8780         /**
8781          * Tries to blur the element. Any exceptions are caught and ignored.
8782          * @return {Roo.Element} this
8783          */
8784         blur : function() {
8785             try{
8786                 this.dom.blur();
8787             }catch(e){}
8788             return this;
8789         },
8790
8791         /**
8792          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8793          * @param {String/Array} className The CSS class to add, or an array of classes
8794          * @return {Roo.Element} this
8795          */
8796         addClass : function(className){
8797             if(className instanceof Array){
8798                 for(var i = 0, len = className.length; i < len; i++) {
8799                     this.addClass(className[i]);
8800                 }
8801             }else{
8802                 if(className && !this.hasClass(className)){
8803                     if (this.dom instanceof SVGElement) {
8804                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8805                     } else {
8806                         this.dom.className = this.dom.className + " " + className;
8807                     }
8808                 }
8809             }
8810             return this;
8811         },
8812
8813         /**
8814          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8815          * @param {String/Array} className The CSS class to add, or an array of classes
8816          * @return {Roo.Element} this
8817          */
8818         radioClass : function(className){
8819             var siblings = this.dom.parentNode.childNodes;
8820             for(var i = 0; i < siblings.length; i++) {
8821                 var s = siblings[i];
8822                 if(s.nodeType == 1){
8823                     Roo.get(s).removeClass(className);
8824                 }
8825             }
8826             this.addClass(className);
8827             return this;
8828         },
8829
8830         /**
8831          * Removes one or more CSS classes from the element.
8832          * @param {String/Array} className The CSS class to remove, or an array of classes
8833          * @return {Roo.Element} this
8834          */
8835         removeClass : function(className){
8836             
8837             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8838             if(!className || !cn){
8839                 return this;
8840             }
8841             if(className instanceof Array){
8842                 for(var i = 0, len = className.length; i < len; i++) {
8843                     this.removeClass(className[i]);
8844                 }
8845             }else{
8846                 if(this.hasClass(className)){
8847                     var re = this.classReCache[className];
8848                     if (!re) {
8849                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8850                        this.classReCache[className] = re;
8851                     }
8852                     if (this.dom instanceof SVGElement) {
8853                         this.dom.className.baseVal = cn.replace(re, " ");
8854                     } else {
8855                         this.dom.className = cn.replace(re, " ");
8856                     }
8857                 }
8858             }
8859             return this;
8860         },
8861
8862         // private
8863         classReCache: {},
8864
8865         /**
8866          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8867          * @param {String} className The CSS class to toggle
8868          * @return {Roo.Element} this
8869          */
8870         toggleClass : function(className){
8871             if(this.hasClass(className)){
8872                 this.removeClass(className);
8873             }else{
8874                 this.addClass(className);
8875             }
8876             return this;
8877         },
8878
8879         /**
8880          * Checks if the specified CSS class exists on this element's DOM node.
8881          * @param {String} className The CSS class to check for
8882          * @return {Boolean} True if the class exists, else false
8883          */
8884         hasClass : function(className){
8885             if (this.dom instanceof SVGElement) {
8886                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8887             } 
8888             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8889         },
8890
8891         /**
8892          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8893          * @param {String} oldClassName The CSS class to replace
8894          * @param {String} newClassName The replacement CSS class
8895          * @return {Roo.Element} this
8896          */
8897         replaceClass : function(oldClassName, newClassName){
8898             this.removeClass(oldClassName);
8899             this.addClass(newClassName);
8900             return this;
8901         },
8902
8903         /**
8904          * Returns an object with properties matching the styles requested.
8905          * For example, el.getStyles('color', 'font-size', 'width') might return
8906          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8907          * @param {String} style1 A style name
8908          * @param {String} style2 A style name
8909          * @param {String} etc.
8910          * @return {Object} The style object
8911          */
8912         getStyles : function(){
8913             var a = arguments, len = a.length, r = {};
8914             for(var i = 0; i < len; i++){
8915                 r[a[i]] = this.getStyle(a[i]);
8916             }
8917             return r;
8918         },
8919
8920         /**
8921          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8922          * @param {String} property The style property whose value is returned.
8923          * @return {String} The current value of the style property for this element.
8924          */
8925         getStyle : function(){
8926             return view && view.getComputedStyle ?
8927                 function(prop){
8928                     var el = this.dom, v, cs, camel;
8929                     if(prop == 'float'){
8930                         prop = "cssFloat";
8931                     }
8932                     if(el.style && (v = el.style[prop])){
8933                         return v;
8934                     }
8935                     if(cs = view.getComputedStyle(el, "")){
8936                         if(!(camel = propCache[prop])){
8937                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8938                         }
8939                         return cs[camel];
8940                     }
8941                     return null;
8942                 } :
8943                 function(prop){
8944                     var el = this.dom, v, cs, camel;
8945                     if(prop == 'opacity'){
8946                         if(typeof el.style.filter == 'string'){
8947                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8948                             if(m){
8949                                 var fv = parseFloat(m[1]);
8950                                 if(!isNaN(fv)){
8951                                     return fv ? fv / 100 : 0;
8952                                 }
8953                             }
8954                         }
8955                         return 1;
8956                     }else if(prop == 'float'){
8957                         prop = "styleFloat";
8958                     }
8959                     if(!(camel = propCache[prop])){
8960                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8961                     }
8962                     if(v = el.style[camel]){
8963                         return v;
8964                     }
8965                     if(cs = el.currentStyle){
8966                         return cs[camel];
8967                     }
8968                     return null;
8969                 };
8970         }(),
8971
8972         /**
8973          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8974          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8975          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8976          * @return {Roo.Element} this
8977          */
8978         setStyle : function(prop, value){
8979             if(typeof prop == "string"){
8980                 
8981                 if (prop == 'float') {
8982                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8983                     return this;
8984                 }
8985                 
8986                 var camel;
8987                 if(!(camel = propCache[prop])){
8988                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8989                 }
8990                 
8991                 if(camel == 'opacity') {
8992                     this.setOpacity(value);
8993                 }else{
8994                     this.dom.style[camel] = value;
8995                 }
8996             }else{
8997                 for(var style in prop){
8998                     if(typeof prop[style] != "function"){
8999                        this.setStyle(style, prop[style]);
9000                     }
9001                 }
9002             }
9003             return this;
9004         },
9005
9006         /**
9007          * More flexible version of {@link #setStyle} for setting style properties.
9008          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9009          * a function which returns such a specification.
9010          * @return {Roo.Element} this
9011          */
9012         applyStyles : function(style){
9013             Roo.DomHelper.applyStyles(this.dom, style);
9014             return this;
9015         },
9016
9017         /**
9018           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9019           * @return {Number} The X position of the element
9020           */
9021         getX : function(){
9022             return D.getX(this.dom);
9023         },
9024
9025         /**
9026           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9027           * @return {Number} The Y position of the element
9028           */
9029         getY : function(){
9030             return D.getY(this.dom);
9031         },
9032
9033         /**
9034           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9035           * @return {Array} The XY position of the element
9036           */
9037         getXY : function(){
9038             return D.getXY(this.dom);
9039         },
9040
9041         /**
9042          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9043          * @param {Number} The X position of the element
9044          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9045          * @return {Roo.Element} this
9046          */
9047         setX : function(x, animate){
9048             if(!animate || !A){
9049                 D.setX(this.dom, x);
9050             }else{
9051                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9052             }
9053             return this;
9054         },
9055
9056         /**
9057          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9058          * @param {Number} The Y position of the element
9059          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9060          * @return {Roo.Element} this
9061          */
9062         setY : function(y, animate){
9063             if(!animate || !A){
9064                 D.setY(this.dom, y);
9065             }else{
9066                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9067             }
9068             return this;
9069         },
9070
9071         /**
9072          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9073          * @param {String} left The left CSS property value
9074          * @return {Roo.Element} this
9075          */
9076         setLeft : function(left){
9077             this.setStyle("left", this.addUnits(left));
9078             return this;
9079         },
9080
9081         /**
9082          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9083          * @param {String} top The top CSS property value
9084          * @return {Roo.Element} this
9085          */
9086         setTop : function(top){
9087             this.setStyle("top", this.addUnits(top));
9088             return this;
9089         },
9090
9091         /**
9092          * Sets the element's CSS right style.
9093          * @param {String} right The right CSS property value
9094          * @return {Roo.Element} this
9095          */
9096         setRight : function(right){
9097             this.setStyle("right", this.addUnits(right));
9098             return this;
9099         },
9100
9101         /**
9102          * Sets the element's CSS bottom style.
9103          * @param {String} bottom The bottom CSS property value
9104          * @return {Roo.Element} this
9105          */
9106         setBottom : function(bottom){
9107             this.setStyle("bottom", this.addUnits(bottom));
9108             return this;
9109         },
9110
9111         /**
9112          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9113          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9114          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9115          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9116          * @return {Roo.Element} this
9117          */
9118         setXY : function(pos, animate){
9119             if(!animate || !A){
9120                 D.setXY(this.dom, pos);
9121             }else{
9122                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9123             }
9124             return this;
9125         },
9126
9127         /**
9128          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9129          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9130          * @param {Number} x X value for new position (coordinates are page-based)
9131          * @param {Number} y Y value for new position (coordinates are page-based)
9132          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9133          * @return {Roo.Element} this
9134          */
9135         setLocation : function(x, y, animate){
9136             this.setXY([x, y], this.preanim(arguments, 2));
9137             return this;
9138         },
9139
9140         /**
9141          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9142          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9143          * @param {Number} x X value for new position (coordinates are page-based)
9144          * @param {Number} y Y value for new position (coordinates are page-based)
9145          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9146          * @return {Roo.Element} this
9147          */
9148         moveTo : function(x, y, animate){
9149             this.setXY([x, y], this.preanim(arguments, 2));
9150             return this;
9151         },
9152
9153         /**
9154          * Returns the region of the given element.
9155          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9156          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9157          */
9158         getRegion : function(){
9159             return D.getRegion(this.dom);
9160         },
9161
9162         /**
9163          * Returns the offset height of the element
9164          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9165          * @return {Number} The element's height
9166          */
9167         getHeight : function(contentHeight){
9168             var h = this.dom.offsetHeight || 0;
9169             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9170         },
9171
9172         /**
9173          * Returns the offset width of the element
9174          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9175          * @return {Number} The element's width
9176          */
9177         getWidth : function(contentWidth){
9178             var w = this.dom.offsetWidth || 0;
9179             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9180         },
9181
9182         /**
9183          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9184          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9185          * if a height has not been set using CSS.
9186          * @return {Number}
9187          */
9188         getComputedHeight : function(){
9189             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9190             if(!h){
9191                 h = parseInt(this.getStyle('height'), 10) || 0;
9192                 if(!this.isBorderBox()){
9193                     h += this.getFrameWidth('tb');
9194                 }
9195             }
9196             return h;
9197         },
9198
9199         /**
9200          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9201          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9202          * if a width has not been set using CSS.
9203          * @return {Number}
9204          */
9205         getComputedWidth : function(){
9206             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9207             if(!w){
9208                 w = parseInt(this.getStyle('width'), 10) || 0;
9209                 if(!this.isBorderBox()){
9210                     w += this.getFrameWidth('lr');
9211                 }
9212             }
9213             return w;
9214         },
9215
9216         /**
9217          * Returns the size of the element.
9218          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9219          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9220          */
9221         getSize : function(contentSize){
9222             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9223         },
9224
9225         /**
9226          * Returns the width and height of the viewport.
9227          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9228          */
9229         getViewSize : function(){
9230             var d = this.dom, doc = document, aw = 0, ah = 0;
9231             if(d == doc || d == doc.body){
9232                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9233             }else{
9234                 return {
9235                     width : d.clientWidth,
9236                     height: d.clientHeight
9237                 };
9238             }
9239         },
9240
9241         /**
9242          * Returns the value of the "value" attribute
9243          * @param {Boolean} asNumber true to parse the value as a number
9244          * @return {String/Number}
9245          */
9246         getValue : function(asNumber){
9247             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9248         },
9249
9250         // private
9251         adjustWidth : function(width){
9252             if(typeof width == "number"){
9253                 if(this.autoBoxAdjust && !this.isBorderBox()){
9254                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9255                 }
9256                 if(width < 0){
9257                     width = 0;
9258                 }
9259             }
9260             return width;
9261         },
9262
9263         // private
9264         adjustHeight : function(height){
9265             if(typeof height == "number"){
9266                if(this.autoBoxAdjust && !this.isBorderBox()){
9267                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9268                }
9269                if(height < 0){
9270                    height = 0;
9271                }
9272             }
9273             return height;
9274         },
9275
9276         /**
9277          * Set the width of the element
9278          * @param {Number} width The new width
9279          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9280          * @return {Roo.Element} this
9281          */
9282         setWidth : function(width, animate){
9283             width = this.adjustWidth(width);
9284             if(!animate || !A){
9285                 this.dom.style.width = this.addUnits(width);
9286             }else{
9287                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9288             }
9289             return this;
9290         },
9291
9292         /**
9293          * Set the height of the element
9294          * @param {Number} height The new height
9295          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9296          * @return {Roo.Element} this
9297          */
9298          setHeight : function(height, animate){
9299             height = this.adjustHeight(height);
9300             if(!animate || !A){
9301                 this.dom.style.height = this.addUnits(height);
9302             }else{
9303                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9310          * @param {Number} width The new width
9311          * @param {Number} height The new height
9312          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313          * @return {Roo.Element} this
9314          */
9315          setSize : function(width, height, animate){
9316             if(typeof width == "object"){ // in case of object from getSize()
9317                 height = width.height; width = width.width;
9318             }
9319             width = this.adjustWidth(width); height = this.adjustHeight(height);
9320             if(!animate || !A){
9321                 this.dom.style.width = this.addUnits(width);
9322                 this.dom.style.height = this.addUnits(height);
9323             }else{
9324                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9325             }
9326             return this;
9327         },
9328
9329         /**
9330          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9331          * @param {Number} x X value for new position (coordinates are page-based)
9332          * @param {Number} y Y value for new position (coordinates are page-based)
9333          * @param {Number} width The new width
9334          * @param {Number} height The new height
9335          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9336          * @return {Roo.Element} this
9337          */
9338         setBounds : function(x, y, width, height, animate){
9339             if(!animate || !A){
9340                 this.setSize(width, height);
9341                 this.setLocation(x, y);
9342             }else{
9343                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9344                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9345                               this.preanim(arguments, 4), 'motion');
9346             }
9347             return this;
9348         },
9349
9350         /**
9351          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9352          * @param {Roo.lib.Region} region The region to fill
9353          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9354          * @return {Roo.Element} this
9355          */
9356         setRegion : function(region, animate){
9357             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9358             return this;
9359         },
9360
9361         /**
9362          * Appends an event handler
9363          *
9364          * @param {String}   eventName     The type of event to append
9365          * @param {Function} fn        The method the event invokes
9366          * @param {Object} scope       (optional) The scope (this object) of the fn
9367          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368          */
9369         addListener : function(eventName, fn, scope, options)
9370         {
9371             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9372                 this.addListener('touchstart', this.onTapHandler, this);
9373             }
9374             
9375             // we need to handle a special case where dom element is a svg element.
9376             // in this case we do not actua
9377             if (!this.dom) {
9378                 return;
9379             }
9380             
9381             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9382                 if (typeof(this.listeners[eventName]) == 'undefined') {
9383                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9384                 }
9385                 this.listeners[eventName].addListener(fn, scope, options);
9386                 return;
9387             }
9388             
9389                 
9390             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9391             
9392             
9393         },
9394         tapedTwice : false,
9395         onTapHandler : function(event)
9396         {
9397             if(!this.tapedTwice) {
9398                 this.tapedTwice = true;
9399                 var s = this;
9400                 setTimeout( function() {
9401                     s.tapedTwice = false;
9402                 }, 300 );
9403                 return;
9404             }
9405             event.preventDefault();
9406             var revent = new MouseEvent('dblclick',  {
9407                 view: window,
9408                 bubbles: true,
9409                 cancelable: true
9410             });
9411              
9412             this.dom.dispatchEvent(revent);
9413             //action on double tap goes below
9414              
9415         }, 
9416  
9417         /**
9418          * Removes an event handler from this element
9419          * @param {String} eventName the type of event to remove
9420          * @param {Function} fn the method the event invokes
9421          * @param {Function} scope (needed for svg fake listeners)
9422          * @return {Roo.Element} this
9423          */
9424         removeListener : function(eventName, fn, scope){
9425             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9426             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9427                 return this;
9428             }
9429             this.listeners[eventName].removeListener(fn, scope);
9430             return this;
9431         },
9432
9433         /**
9434          * Removes all previous added listeners from this element
9435          * @return {Roo.Element} this
9436          */
9437         removeAllListeners : function(){
9438             E.purgeElement(this.dom);
9439             this.listeners = {};
9440             return this;
9441         },
9442
9443         relayEvent : function(eventName, observable){
9444             this.on(eventName, function(e){
9445                 observable.fireEvent(eventName, e);
9446             });
9447         },
9448
9449         
9450         /**
9451          * Set the opacity of the element
9452          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9454          * @return {Roo.Element} this
9455          */
9456          setOpacity : function(opacity, animate){
9457             if(!animate || !A){
9458                 var s = this.dom.style;
9459                 if(Roo.isIE){
9460                     s.zoom = 1;
9461                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9462                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9463                 }else{
9464                     s.opacity = opacity;
9465                 }
9466             }else{
9467                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9468             }
9469             return this;
9470         },
9471
9472         /**
9473          * Gets the left X coordinate
9474          * @param {Boolean} local True to get the local css position instead of page coordinate
9475          * @return {Number}
9476          */
9477         getLeft : function(local){
9478             if(!local){
9479                 return this.getX();
9480             }else{
9481                 return parseInt(this.getStyle("left"), 10) || 0;
9482             }
9483         },
9484
9485         /**
9486          * Gets the right X coordinate of the element (element X position + element width)
9487          * @param {Boolean} local True to get the local css position instead of page coordinate
9488          * @return {Number}
9489          */
9490         getRight : function(local){
9491             if(!local){
9492                 return this.getX() + this.getWidth();
9493             }else{
9494                 return (this.getLeft(true) + this.getWidth()) || 0;
9495             }
9496         },
9497
9498         /**
9499          * Gets the top Y coordinate
9500          * @param {Boolean} local True to get the local css position instead of page coordinate
9501          * @return {Number}
9502          */
9503         getTop : function(local) {
9504             if(!local){
9505                 return this.getY();
9506             }else{
9507                 return parseInt(this.getStyle("top"), 10) || 0;
9508             }
9509         },
9510
9511         /**
9512          * Gets the bottom Y coordinate of the element (element Y position + element height)
9513          * @param {Boolean} local True to get the local css position instead of page coordinate
9514          * @return {Number}
9515          */
9516         getBottom : function(local){
9517             if(!local){
9518                 return this.getY() + this.getHeight();
9519             }else{
9520                 return (this.getTop(true) + this.getHeight()) || 0;
9521             }
9522         },
9523
9524         /**
9525         * Initializes positioning on this element. If a desired position is not passed, it will make the
9526         * the element positioned relative IF it is not already positioned.
9527         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9528         * @param {Number} zIndex (optional) The zIndex to apply
9529         * @param {Number} x (optional) Set the page X position
9530         * @param {Number} y (optional) Set the page Y position
9531         */
9532         position : function(pos, zIndex, x, y){
9533             if(!pos){
9534                if(this.getStyle('position') == 'static'){
9535                    this.setStyle('position', 'relative');
9536                }
9537             }else{
9538                 this.setStyle("position", pos);
9539             }
9540             if(zIndex){
9541                 this.setStyle("z-index", zIndex);
9542             }
9543             if(x !== undefined && y !== undefined){
9544                 this.setXY([x, y]);
9545             }else if(x !== undefined){
9546                 this.setX(x);
9547             }else if(y !== undefined){
9548                 this.setY(y);
9549             }
9550         },
9551
9552         /**
9553         * Clear positioning back to the default when the document was loaded
9554         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9555         * @return {Roo.Element} this
9556          */
9557         clearPositioning : function(value){
9558             value = value ||'';
9559             this.setStyle({
9560                 "left": value,
9561                 "right": value,
9562                 "top": value,
9563                 "bottom": value,
9564                 "z-index": "",
9565                 "position" : "static"
9566             });
9567             return this;
9568         },
9569
9570         /**
9571         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9572         * snapshot before performing an update and then restoring the element.
9573         * @return {Object}
9574         */
9575         getPositioning : function(){
9576             var l = this.getStyle("left");
9577             var t = this.getStyle("top");
9578             return {
9579                 "position" : this.getStyle("position"),
9580                 "left" : l,
9581                 "right" : l ? "" : this.getStyle("right"),
9582                 "top" : t,
9583                 "bottom" : t ? "" : this.getStyle("bottom"),
9584                 "z-index" : this.getStyle("z-index")
9585             };
9586         },
9587
9588         /**
9589          * Gets the width of the border(s) for the specified side(s)
9590          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9591          * passing lr would get the border (l)eft width + the border (r)ight width.
9592          * @return {Number} The width of the sides passed added together
9593          */
9594         getBorderWidth : function(side){
9595             return this.addStyles(side, El.borders);
9596         },
9597
9598         /**
9599          * Gets the width of the padding(s) for the specified side(s)
9600          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9601          * passing lr would get the padding (l)eft + the padding (r)ight.
9602          * @return {Number} The padding of the sides passed added together
9603          */
9604         getPadding : function(side){
9605             return this.addStyles(side, El.paddings);
9606         },
9607
9608         /**
9609         * Set positioning with an object returned by getPositioning().
9610         * @param {Object} posCfg
9611         * @return {Roo.Element} this
9612          */
9613         setPositioning : function(pc){
9614             this.applyStyles(pc);
9615             if(pc.right == "auto"){
9616                 this.dom.style.right = "";
9617             }
9618             if(pc.bottom == "auto"){
9619                 this.dom.style.bottom = "";
9620             }
9621             return this;
9622         },
9623
9624         // private
9625         fixDisplay : function(){
9626             if(this.getStyle("display") == "none"){
9627                 this.setStyle("visibility", "hidden");
9628                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9629                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9630                     this.setStyle("display", "block");
9631                 }
9632             }
9633         },
9634
9635         /**
9636          * Quick set left and top adding default units
9637          * @param {String} left The left CSS property value
9638          * @param {String} top The top CSS property value
9639          * @return {Roo.Element} this
9640          */
9641          setLeftTop : function(left, top){
9642             this.dom.style.left = this.addUnits(left);
9643             this.dom.style.top = this.addUnits(top);
9644             return this;
9645         },
9646
9647         /**
9648          * Move this element relative to its current position.
9649          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9650          * @param {Number} distance How far to move the element in pixels
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654          move : function(direction, distance, animate){
9655             var xy = this.getXY();
9656             direction = direction.toLowerCase();
9657             switch(direction){
9658                 case "l":
9659                 case "left":
9660                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9661                     break;
9662                case "r":
9663                case "right":
9664                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9665                     break;
9666                case "t":
9667                case "top":
9668                case "up":
9669                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9670                     break;
9671                case "b":
9672                case "bottom":
9673                case "down":
9674                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9675                     break;
9676             }
9677             return this;
9678         },
9679
9680         /**
9681          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9682          * @return {Roo.Element} this
9683          */
9684         clip : function(){
9685             if(!this.isClipped){
9686                this.isClipped = true;
9687                this.originalClip = {
9688                    "o": this.getStyle("overflow"),
9689                    "x": this.getStyle("overflow-x"),
9690                    "y": this.getStyle("overflow-y")
9691                };
9692                this.setStyle("overflow", "hidden");
9693                this.setStyle("overflow-x", "hidden");
9694                this.setStyle("overflow-y", "hidden");
9695             }
9696             return this;
9697         },
9698
9699         /**
9700          *  Return clipping (overflow) to original clipping before clip() was called
9701          * @return {Roo.Element} this
9702          */
9703         unclip : function(){
9704             if(this.isClipped){
9705                 this.isClipped = false;
9706                 var o = this.originalClip;
9707                 if(o.o){this.setStyle("overflow", o.o);}
9708                 if(o.x){this.setStyle("overflow-x", o.x);}
9709                 if(o.y){this.setStyle("overflow-y", o.y);}
9710             }
9711             return this;
9712         },
9713
9714
9715         /**
9716          * Gets the x,y coordinates specified by the anchor position on the element.
9717          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9718          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9719          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9720          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9721          * @return {Array} [x, y] An array containing the element's x and y coordinates
9722          */
9723         getAnchorXY : function(anchor, local, s){
9724             //Passing a different size is useful for pre-calculating anchors,
9725             //especially for anchored animations that change the el size.
9726
9727             var w, h, vp = false;
9728             if(!s){
9729                 var d = this.dom;
9730                 if(d == document.body || d == document){
9731                     vp = true;
9732                     w = D.getViewWidth(); h = D.getViewHeight();
9733                 }else{
9734                     w = this.getWidth(); h = this.getHeight();
9735                 }
9736             }else{
9737                 w = s.width;  h = s.height;
9738             }
9739             var x = 0, y = 0, r = Math.round;
9740             switch((anchor || "tl").toLowerCase()){
9741                 case "c":
9742                     x = r(w*.5);
9743                     y = r(h*.5);
9744                 break;
9745                 case "t":
9746                     x = r(w*.5);
9747                     y = 0;
9748                 break;
9749                 case "l":
9750                     x = 0;
9751                     y = r(h*.5);
9752                 break;
9753                 case "r":
9754                     x = w;
9755                     y = r(h*.5);
9756                 break;
9757                 case "b":
9758                     x = r(w*.5);
9759                     y = h;
9760                 break;
9761                 case "tl":
9762                     x = 0;
9763                     y = 0;
9764                 break;
9765                 case "bl":
9766                     x = 0;
9767                     y = h;
9768                 break;
9769                 case "br":
9770                     x = w;
9771                     y = h;
9772                 break;
9773                 case "tr":
9774                     x = w;
9775                     y = 0;
9776                 break;
9777             }
9778             if(local === true){
9779                 return [x, y];
9780             }
9781             if(vp){
9782                 var sc = this.getScroll();
9783                 return [x + sc.left, y + sc.top];
9784             }
9785             //Add the element's offset xy
9786             var o = this.getXY();
9787             return [x+o[0], y+o[1]];
9788         },
9789
9790         /**
9791          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9792          * supported position values.
9793          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9794          * @param {String} position The position to align to.
9795          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9796          * @return {Array} [x, y]
9797          */
9798         getAlignToXY : function(el, p, o)
9799         {
9800             el = Roo.get(el);
9801             var d = this.dom;
9802             if(!el.dom){
9803                 throw "Element.alignTo with an element that doesn't exist";
9804             }
9805             var c = false; //constrain to viewport
9806             var p1 = "", p2 = "";
9807             o = o || [0,0];
9808
9809             if(!p){
9810                 p = "tl-bl";
9811             }else if(p == "?"){
9812                 p = "tl-bl?";
9813             }else if(p.indexOf("-") == -1){
9814                 p = "tl-" + p;
9815             }
9816             p = p.toLowerCase();
9817             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9818             if(!m){
9819                throw "Element.alignTo with an invalid alignment " + p;
9820             }
9821             p1 = m[1]; p2 = m[2]; c = !!m[3];
9822
9823             //Subtract the aligned el's internal xy from the target's offset xy
9824             //plus custom offset to get the aligned el's new offset xy
9825             var a1 = this.getAnchorXY(p1, true);
9826             var a2 = el.getAnchorXY(p2, false);
9827             var x = a2[0] - a1[0] + o[0];
9828             var y = a2[1] - a1[1] + o[1];
9829             if(c){
9830                 //constrain the aligned el to viewport if necessary
9831                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9832                 // 5px of margin for ie
9833                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9834
9835                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9836                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9837                 //otherwise swap the aligned el to the opposite border of the target.
9838                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9839                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9840                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9841                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9842
9843                var doc = document;
9844                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9845                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9846
9847                if((x+w) > dw + scrollX){
9848                     x = swapX ? r.left-w : dw+scrollX-w;
9849                 }
9850                if(x < scrollX){
9851                    x = swapX ? r.right : scrollX;
9852                }
9853                if((y+h) > dh + scrollY){
9854                     y = swapY ? r.top-h : dh+scrollY-h;
9855                 }
9856                if (y < scrollY){
9857                    y = swapY ? r.bottom : scrollY;
9858                }
9859             }
9860             return [x,y];
9861         },
9862
9863         // private
9864         getConstrainToXY : function(){
9865             var os = {top:0, left:0, bottom:0, right: 0};
9866
9867             return function(el, local, offsets, proposedXY){
9868                 el = Roo.get(el);
9869                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9870
9871                 var vw, vh, vx = 0, vy = 0;
9872                 if(el.dom == document.body || el.dom == document){
9873                     vw = Roo.lib.Dom.getViewWidth();
9874                     vh = Roo.lib.Dom.getViewHeight();
9875                 }else{
9876                     vw = el.dom.clientWidth;
9877                     vh = el.dom.clientHeight;
9878                     if(!local){
9879                         var vxy = el.getXY();
9880                         vx = vxy[0];
9881                         vy = vxy[1];
9882                     }
9883                 }
9884
9885                 var s = el.getScroll();
9886
9887                 vx += offsets.left + s.left;
9888                 vy += offsets.top + s.top;
9889
9890                 vw -= offsets.right;
9891                 vh -= offsets.bottom;
9892
9893                 var vr = vx+vw;
9894                 var vb = vy+vh;
9895
9896                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9897                 var x = xy[0], y = xy[1];
9898                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9899
9900                 // only move it if it needs it
9901                 var moved = false;
9902
9903                 // first validate right/bottom
9904                 if((x + w) > vr){
9905                     x = vr - w;
9906                     moved = true;
9907                 }
9908                 if((y + h) > vb){
9909                     y = vb - h;
9910                     moved = true;
9911                 }
9912                 // then make sure top/left isn't negative
9913                 if(x < vx){
9914                     x = vx;
9915                     moved = true;
9916                 }
9917                 if(y < vy){
9918                     y = vy;
9919                     moved = true;
9920                 }
9921                 return moved ? [x, y] : false;
9922             };
9923         }(),
9924
9925         // private
9926         adjustForConstraints : function(xy, parent, offsets){
9927             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9928         },
9929
9930         /**
9931          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9932          * document it aligns it to the viewport.
9933          * The position parameter is optional, and can be specified in any one of the following formats:
9934          * <ul>
9935          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9936          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9937          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9938          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9939          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
9940          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9941          * </ul>
9942          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9943          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9944          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9945          * that specified in order to enforce the viewport constraints.
9946          * Following are all of the supported anchor positions:
9947     <pre>
9948     Value  Description
9949     -----  -----------------------------
9950     tl     The top left corner (default)
9951     t      The center of the top edge
9952     tr     The top right corner
9953     l      The center of the left edge
9954     c      In the center of the element
9955     r      The center of the right edge
9956     bl     The bottom left corner
9957     b      The center of the bottom edge
9958     br     The bottom right corner
9959     </pre>
9960     Example Usage:
9961     <pre><code>
9962     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9963     el.alignTo("other-el");
9964
9965     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9966     el.alignTo("other-el", "tr?");
9967
9968     // align the bottom right corner of el with the center left edge of other-el
9969     el.alignTo("other-el", "br-l?");
9970
9971     // align the center of el with the bottom left corner of other-el and
9972     // adjust the x position by -6 pixels (and the y position by 0)
9973     el.alignTo("other-el", "c-bl", [-6, 0]);
9974     </code></pre>
9975          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9976          * @param {String} position The position to align to.
9977          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9978          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9979          * @return {Roo.Element} this
9980          */
9981         alignTo : function(element, position, offsets, animate){
9982             var xy = this.getAlignToXY(element, position, offsets);
9983             this.setXY(xy, this.preanim(arguments, 3));
9984             return this;
9985         },
9986
9987         /**
9988          * Anchors an element to another element and realigns it when the window is resized.
9989          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9990          * @param {String} position The position to align to.
9991          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9992          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9993          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9994          * is a number, it is used as the buffer delay (defaults to 50ms).
9995          * @param {Function} callback The function to call after the animation finishes
9996          * @return {Roo.Element} this
9997          */
9998         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9999             var action = function(){
10000                 this.alignTo(el, alignment, offsets, animate);
10001                 Roo.callback(callback, this);
10002             };
10003             Roo.EventManager.onWindowResize(action, this);
10004             var tm = typeof monitorScroll;
10005             if(tm != 'undefined'){
10006                 Roo.EventManager.on(window, 'scroll', action, this,
10007                     {buffer: tm == 'number' ? monitorScroll : 50});
10008             }
10009             action.call(this); // align immediately
10010             return this;
10011         },
10012         /**
10013          * Clears any opacity settings from this element. Required in some cases for IE.
10014          * @return {Roo.Element} this
10015          */
10016         clearOpacity : function(){
10017             if (window.ActiveXObject) {
10018                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10019                     this.dom.style.filter = "";
10020                 }
10021             } else {
10022                 this.dom.style.opacity = "";
10023                 this.dom.style["-moz-opacity"] = "";
10024                 this.dom.style["-khtml-opacity"] = "";
10025             }
10026             return this;
10027         },
10028
10029         /**
10030          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10031          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10032          * @return {Roo.Element} this
10033          */
10034         hide : function(animate){
10035             this.setVisible(false, this.preanim(arguments, 0));
10036             return this;
10037         },
10038
10039         /**
10040         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10041         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10042          * @return {Roo.Element} this
10043          */
10044         show : function(animate){
10045             this.setVisible(true, this.preanim(arguments, 0));
10046             return this;
10047         },
10048
10049         /**
10050          * @private Test if size has a unit, otherwise appends the default
10051          */
10052         addUnits : function(size){
10053             return Roo.Element.addUnits(size, this.defaultUnit);
10054         },
10055
10056         /**
10057          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10058          * @return {Roo.Element} this
10059          */
10060         beginMeasure : function(){
10061             var el = this.dom;
10062             if(el.offsetWidth || el.offsetHeight){
10063                 return this; // offsets work already
10064             }
10065             var changed = [];
10066             var p = this.dom, b = document.body; // start with this element
10067             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10068                 var pe = Roo.get(p);
10069                 if(pe.getStyle('display') == 'none'){
10070                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10071                     p.style.visibility = "hidden";
10072                     p.style.display = "block";
10073                 }
10074                 p = p.parentNode;
10075             }
10076             this._measureChanged = changed;
10077             return this;
10078
10079         },
10080
10081         /**
10082          * Restores displays to before beginMeasure was called
10083          * @return {Roo.Element} this
10084          */
10085         endMeasure : function(){
10086             var changed = this._measureChanged;
10087             if(changed){
10088                 for(var i = 0, len = changed.length; i < len; i++) {
10089                     var r = changed[i];
10090                     r.el.style.visibility = r.visibility;
10091                     r.el.style.display = "none";
10092                 }
10093                 this._measureChanged = null;
10094             }
10095             return this;
10096         },
10097
10098         /**
10099         * Update the innerHTML of this element, optionally searching for and processing scripts
10100         * @param {String} html The new HTML
10101         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10102         * @param {Function} callback For async script loading you can be noticed when the update completes
10103         * @return {Roo.Element} this
10104          */
10105         update : function(html, loadScripts, callback){
10106             if(typeof html == "undefined"){
10107                 html = "";
10108             }
10109             if(loadScripts !== true){
10110                 this.dom.innerHTML = html;
10111                 if(typeof callback == "function"){
10112                     callback();
10113                 }
10114                 return this;
10115             }
10116             var id = Roo.id();
10117             var dom = this.dom;
10118
10119             html += '<span id="' + id + '"></span>';
10120
10121             E.onAvailable(id, function(){
10122                 var hd = document.getElementsByTagName("head")[0];
10123                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10124                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10125                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10126
10127                 var match;
10128                 while(match = re.exec(html)){
10129                     var attrs = match[1];
10130                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10131                     if(srcMatch && srcMatch[2]){
10132                        var s = document.createElement("script");
10133                        s.src = srcMatch[2];
10134                        var typeMatch = attrs.match(typeRe);
10135                        if(typeMatch && typeMatch[2]){
10136                            s.type = typeMatch[2];
10137                        }
10138                        hd.appendChild(s);
10139                     }else if(match[2] && match[2].length > 0){
10140                         if(window.execScript) {
10141                            window.execScript(match[2]);
10142                         } else {
10143                             /**
10144                              * eval:var:id
10145                              * eval:var:dom
10146                              * eval:var:html
10147                              * 
10148                              */
10149                            window.eval(match[2]);
10150                         }
10151                     }
10152                 }
10153                 var el = document.getElementById(id);
10154                 if(el){el.parentNode.removeChild(el);}
10155                 if(typeof callback == "function"){
10156                     callback();
10157                 }
10158             });
10159             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10160             return this;
10161         },
10162
10163         /**
10164          * Direct access to the UpdateManager update() method (takes the same parameters).
10165          * @param {String/Function} url The url for this request or a function to call to get the url
10166          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
10167          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10168          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
10169          * @return {Roo.Element} this
10170          */
10171         load : function(){
10172             var um = this.getUpdateManager();
10173             um.update.apply(um, arguments);
10174             return this;
10175         },
10176
10177         /**
10178         * Gets this element's UpdateManager
10179         * @return {Roo.UpdateManager} The UpdateManager
10180         */
10181         getUpdateManager : function(){
10182             if(!this.updateManager){
10183                 this.updateManager = new Roo.UpdateManager(this);
10184             }
10185             return this.updateManager;
10186         },
10187
10188         /**
10189          * Disables text selection for this element (normalized across browsers)
10190          * @return {Roo.Element} this
10191          */
10192         unselectable : function(){
10193             this.dom.unselectable = "on";
10194             this.swallowEvent("selectstart", true);
10195             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10196             this.addClass("x-unselectable");
10197             return this;
10198         },
10199
10200         /**
10201         * Calculates the x, y to center this element on the screen
10202         * @return {Array} The x, y values [x, y]
10203         */
10204         getCenterXY : function(){
10205             return this.getAlignToXY(document, 'c-c');
10206         },
10207
10208         /**
10209         * Centers the Element in either the viewport, or another Element.
10210         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10211         */
10212         center : function(centerIn){
10213             this.alignTo(centerIn || document, 'c-c');
10214             return this;
10215         },
10216
10217         /**
10218          * Tests various css rules/browsers to determine if this element uses a border box
10219          * @return {Boolean}
10220          */
10221         isBorderBox : function(){
10222             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10223         },
10224
10225         /**
10226          * Return a box {x, y, width, height} that can be used to set another elements
10227          * size/location to match this element.
10228          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10229          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10230          * @return {Object} box An object in the format {x, y, width, height}
10231          */
10232         getBox : function(contentBox, local){
10233             var xy;
10234             if(!local){
10235                 xy = this.getXY();
10236             }else{
10237                 var left = parseInt(this.getStyle("left"), 10) || 0;
10238                 var top = parseInt(this.getStyle("top"), 10) || 0;
10239                 xy = [left, top];
10240             }
10241             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10242             if(!contentBox){
10243                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10244             }else{
10245                 var l = this.getBorderWidth("l")+this.getPadding("l");
10246                 var r = this.getBorderWidth("r")+this.getPadding("r");
10247                 var t = this.getBorderWidth("t")+this.getPadding("t");
10248                 var b = this.getBorderWidth("b")+this.getPadding("b");
10249                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
10250             }
10251             bx.right = bx.x + bx.width;
10252             bx.bottom = bx.y + bx.height;
10253             return bx;
10254         },
10255
10256         /**
10257          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10258          for more information about the sides.
10259          * @param {String} sides
10260          * @return {Number}
10261          */
10262         getFrameWidth : function(sides, onlyContentBox){
10263             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10264         },
10265
10266         /**
10267          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
10268          * @param {Object} box The box to fill {x, y, width, height}
10269          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10270          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10271          * @return {Roo.Element} this
10272          */
10273         setBox : function(box, adjust, animate){
10274             var w = box.width, h = box.height;
10275             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10276                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10277                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10278             }
10279             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10280             return this;
10281         },
10282
10283         /**
10284          * Forces the browser to repaint this element
10285          * @return {Roo.Element} this
10286          */
10287          repaint : function(){
10288             var dom = this.dom;
10289             this.addClass("x-repaint");
10290             setTimeout(function(){
10291                 Roo.get(dom).removeClass("x-repaint");
10292             }, 1);
10293             return this;
10294         },
10295
10296         /**
10297          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10298          * then it returns the calculated width of the sides (see getPadding)
10299          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10300          * @return {Object/Number}
10301          */
10302         getMargins : function(side){
10303             if(!side){
10304                 return {
10305                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10306                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10307                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10308                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10309                 };
10310             }else{
10311                 return this.addStyles(side, El.margins);
10312              }
10313         },
10314
10315         // private
10316         addStyles : function(sides, styles){
10317             var val = 0, v, w;
10318             for(var i = 0, len = sides.length; i < len; i++){
10319                 v = this.getStyle(styles[sides.charAt(i)]);
10320                 if(v){
10321                      w = parseInt(v, 10);
10322                      if(w){ val += w; }
10323                 }
10324             }
10325             return val;
10326         },
10327
10328         /**
10329          * Creates a proxy element of this element
10330          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10331          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10332          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10333          * @return {Roo.Element} The new proxy element
10334          */
10335         createProxy : function(config, renderTo, matchBox){
10336             if(renderTo){
10337                 renderTo = Roo.getDom(renderTo);
10338             }else{
10339                 renderTo = document.body;
10340             }
10341             config = typeof config == "object" ?
10342                 config : {tag : "div", cls: config};
10343             var proxy = Roo.DomHelper.append(renderTo, config, true);
10344             if(matchBox){
10345                proxy.setBox(this.getBox());
10346             }
10347             return proxy;
10348         },
10349
10350         /**
10351          * Puts a mask over this element to disable user interaction. Requires core.css.
10352          * This method can only be applied to elements which accept child nodes.
10353          * @param {String} msg (optional) A message to display in the mask
10354          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10355          * @return {Element} The mask  element
10356          */
10357         mask : function(msg, msgCls)
10358         {
10359             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10360                 this.setStyle("position", "relative");
10361             }
10362             if(!this._mask){
10363                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10364             }
10365             
10366             this.addClass("x-masked");
10367             this._mask.setDisplayed(true);
10368             
10369             // we wander
10370             var z = 0;
10371             var dom = this.dom;
10372             while (dom && dom.style) {
10373                 if (!isNaN(parseInt(dom.style.zIndex))) {
10374                     z = Math.max(z, parseInt(dom.style.zIndex));
10375                 }
10376                 dom = dom.parentNode;
10377             }
10378             // if we are masking the body - then it hides everything..
10379             if (this.dom == document.body) {
10380                 z = 1000000;
10381                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10382                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10383             }
10384            
10385             if(typeof msg == 'string'){
10386                 if(!this._maskMsg){
10387                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10388                         cls: "roo-el-mask-msg", 
10389                         cn: [
10390                             {
10391                                 tag: 'i',
10392                                 cls: 'fa fa-spinner fa-spin'
10393                             },
10394                             {
10395                                 tag: 'div'
10396                             }   
10397                         ]
10398                     }, true);
10399                 }
10400                 var mm = this._maskMsg;
10401                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10402                 if (mm.dom.lastChild) { // weird IE issue?
10403                     mm.dom.lastChild.innerHTML = msg;
10404                 }
10405                 mm.setDisplayed(true);
10406                 mm.center(this);
10407                 mm.setStyle('z-index', z + 102);
10408             }
10409             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10410                 this._mask.setHeight(this.getHeight());
10411             }
10412             this._mask.setStyle('z-index', z + 100);
10413             
10414             return this._mask;
10415         },
10416
10417         /**
10418          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10419          * it is cached for reuse.
10420          */
10421         unmask : function(removeEl){
10422             if(this._mask){
10423                 if(removeEl === true){
10424                     this._mask.remove();
10425                     delete this._mask;
10426                     if(this._maskMsg){
10427                         this._maskMsg.remove();
10428                         delete this._maskMsg;
10429                     }
10430                 }else{
10431                     this._mask.setDisplayed(false);
10432                     if(this._maskMsg){
10433                         this._maskMsg.setDisplayed(false);
10434                     }
10435                 }
10436             }
10437             this.removeClass("x-masked");
10438         },
10439
10440         /**
10441          * Returns true if this element is masked
10442          * @return {Boolean}
10443          */
10444         isMasked : function(){
10445             return this._mask && this._mask.isVisible();
10446         },
10447
10448         /**
10449          * Creates an iframe shim for this element to keep selects and other windowed objects from
10450          * showing through.
10451          * @return {Roo.Element} The new shim element
10452          */
10453         createShim : function(){
10454             var el = document.createElement('iframe');
10455             el.frameBorder = 'no';
10456             el.className = 'roo-shim';
10457             if(Roo.isIE && Roo.isSecure){
10458                 el.src = Roo.SSL_SECURE_URL;
10459             }
10460             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10461             shim.autoBoxAdjust = false;
10462             return shim;
10463         },
10464
10465         /**
10466          * Removes this element from the DOM and deletes it from the cache
10467          */
10468         remove : function(){
10469             if(this.dom.parentNode){
10470                 this.dom.parentNode.removeChild(this.dom);
10471             }
10472             delete El.cache[this.dom.id];
10473         },
10474
10475         /**
10476          * Sets up event handlers to add and remove a css class when the mouse is over this element
10477          * @param {String} className
10478          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10479          * mouseout events for children elements
10480          * @return {Roo.Element} this
10481          */
10482         addClassOnOver : function(className, preventFlicker){
10483             this.on("mouseover", function(){
10484                 Roo.fly(this, '_internal').addClass(className);
10485             }, this.dom);
10486             var removeFn = function(e){
10487                 if(preventFlicker !== true || !e.within(this, true)){
10488                     Roo.fly(this, '_internal').removeClass(className);
10489                 }
10490             };
10491             this.on("mouseout", removeFn, this.dom);
10492             return this;
10493         },
10494
10495         /**
10496          * Sets up event handlers to add and remove a css class when this element has the focus
10497          * @param {String} className
10498          * @return {Roo.Element} this
10499          */
10500         addClassOnFocus : function(className){
10501             this.on("focus", function(){
10502                 Roo.fly(this, '_internal').addClass(className);
10503             }, this.dom);
10504             this.on("blur", function(){
10505                 Roo.fly(this, '_internal').removeClass(className);
10506             }, this.dom);
10507             return this;
10508         },
10509         /**
10510          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10511          * @param {String} className
10512          * @return {Roo.Element} this
10513          */
10514         addClassOnClick : function(className){
10515             var dom = this.dom;
10516             this.on("mousedown", function(){
10517                 Roo.fly(dom, '_internal').addClass(className);
10518                 var d = Roo.get(document);
10519                 var fn = function(){
10520                     Roo.fly(dom, '_internal').removeClass(className);
10521                     d.removeListener("mouseup", fn);
10522                 };
10523                 d.on("mouseup", fn);
10524             });
10525             return this;
10526         },
10527
10528         /**
10529          * Stops the specified event from bubbling and optionally prevents the default action
10530          * @param {String} eventName
10531          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10532          * @return {Roo.Element} this
10533          */
10534         swallowEvent : function(eventName, preventDefault){
10535             var fn = function(e){
10536                 e.stopPropagation();
10537                 if(preventDefault){
10538                     e.preventDefault();
10539                 }
10540             };
10541             if(eventName instanceof Array){
10542                 for(var i = 0, len = eventName.length; i < len; i++){
10543                      this.on(eventName[i], fn);
10544                 }
10545                 return this;
10546             }
10547             this.on(eventName, fn);
10548             return this;
10549         },
10550
10551         /**
10552          * @private
10553          */
10554         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10555
10556         /**
10557          * Sizes this element to its parent element's dimensions performing
10558          * neccessary box adjustments.
10559          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10560          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10561          * @return {Roo.Element} this
10562          */
10563         fitToParent : function(monitorResize, targetParent) {
10564           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10565           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10566           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10567             return this;
10568           }
10569           var p = Roo.get(targetParent || this.dom.parentNode);
10570           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10571           if (monitorResize === true) {
10572             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10573             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10574           }
10575           return this;
10576         },
10577
10578         /**
10579          * Gets the next sibling, skipping text nodes
10580          * @return {HTMLElement} The next sibling or null
10581          */
10582         getNextSibling : function(){
10583             var n = this.dom.nextSibling;
10584             while(n && n.nodeType != 1){
10585                 n = n.nextSibling;
10586             }
10587             return n;
10588         },
10589
10590         /**
10591          * Gets the previous sibling, skipping text nodes
10592          * @return {HTMLElement} The previous sibling or null
10593          */
10594         getPrevSibling : function(){
10595             var n = this.dom.previousSibling;
10596             while(n && n.nodeType != 1){
10597                 n = n.previousSibling;
10598             }
10599             return n;
10600         },
10601
10602
10603         /**
10604          * Appends the passed element(s) to this element
10605          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10606          * @return {Roo.Element} this
10607          */
10608         appendChild: function(el){
10609             el = Roo.get(el);
10610             el.appendTo(this);
10611             return this;
10612         },
10613
10614         /**
10615          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10616          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10617          * automatically generated with the specified attributes.
10618          * @param {HTMLElement} insertBefore (optional) a child element of this element
10619          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10620          * @return {Roo.Element} The new child element
10621          */
10622         createChild: function(config, insertBefore, returnDom){
10623             config = config || {tag:'div'};
10624             if(insertBefore){
10625                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10626             }
10627             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10628         },
10629
10630         /**
10631          * Appends this element to the passed element
10632          * @param {String/HTMLElement/Element} el The new parent element
10633          * @return {Roo.Element} this
10634          */
10635         appendTo: function(el){
10636             el = Roo.getDom(el);
10637             el.appendChild(this.dom);
10638             return this;
10639         },
10640
10641         /**
10642          * Inserts this element before the passed element in the DOM
10643          * @param {String/HTMLElement/Element} el The element to insert before
10644          * @return {Roo.Element} this
10645          */
10646         insertBefore: function(el){
10647             el = Roo.getDom(el);
10648             el.parentNode.insertBefore(this.dom, el);
10649             return this;
10650         },
10651
10652         /**
10653          * Inserts this element after the passed element in the DOM
10654          * @param {String/HTMLElement/Element} el The element to insert after
10655          * @return {Roo.Element} this
10656          */
10657         insertAfter: function(el){
10658             el = Roo.getDom(el);
10659             el.parentNode.insertBefore(this.dom, el.nextSibling);
10660             return this;
10661         },
10662
10663         /**
10664          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10665          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10666          * @return {Roo.Element} The new child
10667          */
10668         insertFirst: function(el, returnDom){
10669             el = el || {};
10670             if(typeof el == 'object' && !el.nodeType){ // dh config
10671                 return this.createChild(el, this.dom.firstChild, returnDom);
10672             }else{
10673                 el = Roo.getDom(el);
10674                 this.dom.insertBefore(el, this.dom.firstChild);
10675                 return !returnDom ? Roo.get(el) : el;
10676             }
10677         },
10678
10679         /**
10680          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10681          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10682          * @param {String} where (optional) 'before' or 'after' defaults to before
10683          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10684          * @return {Roo.Element} the inserted Element
10685          */
10686         insertSibling: function(el, where, returnDom){
10687             where = where ? where.toLowerCase() : 'before';
10688             el = el || {};
10689             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10690
10691             if(typeof el == 'object' && !el.nodeType){ // dh config
10692                 if(where == 'after' && !this.dom.nextSibling){
10693                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10694                 }else{
10695                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10696                 }
10697
10698             }else{
10699                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10700                             where == 'before' ? this.dom : this.dom.nextSibling);
10701                 if(!returnDom){
10702                     rt = Roo.get(rt);
10703                 }
10704             }
10705             return rt;
10706         },
10707
10708         /**
10709          * Creates and wraps this element with another element
10710          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10711          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10712          * @return {HTMLElement/Element} The newly created wrapper element
10713          */
10714         wrap: function(config, returnDom){
10715             if(!config){
10716                 config = {tag: "div"};
10717             }
10718             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10719             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10720             return newEl;
10721         },
10722
10723         /**
10724          * Replaces the passed element with this element
10725          * @param {String/HTMLElement/Element} el The element to replace
10726          * @return {Roo.Element} this
10727          */
10728         replace: function(el){
10729             el = Roo.get(el);
10730             this.insertBefore(el);
10731             el.remove();
10732             return this;
10733         },
10734
10735         /**
10736          * Inserts an html fragment into this element
10737          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10738          * @param {String} html The HTML fragment
10739          * @param {Boolean} returnEl True to return an Roo.Element
10740          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10741          */
10742         insertHtml : function(where, html, returnEl){
10743             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10744             return returnEl ? Roo.get(el) : el;
10745         },
10746
10747         /**
10748          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10749          * @param {Object} o The object with the attributes
10750          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10751          * @return {Roo.Element} this
10752          */
10753         set : function(o, useSet){
10754             var el = this.dom;
10755             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10756             for(var attr in o){
10757                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10758                 if(attr=="cls"){
10759                     el.className = o["cls"];
10760                 }else{
10761                     if(useSet) {
10762                         el.setAttribute(attr, o[attr]);
10763                     } else {
10764                         el[attr] = o[attr];
10765                     }
10766                 }
10767             }
10768             if(o.style){
10769                 Roo.DomHelper.applyStyles(el, o.style);
10770             }
10771             return this;
10772         },
10773
10774         /**
10775          * Convenience method for constructing a KeyMap
10776          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
10777          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10778          * @param {Function} fn The function to call
10779          * @param {Object} scope (optional) The scope of the function
10780          * @return {Roo.KeyMap} The KeyMap created
10781          */
10782         addKeyListener : function(key, fn, scope){
10783             var config;
10784             if(typeof key != "object" || key instanceof Array){
10785                 config = {
10786                     key: key,
10787                     fn: fn,
10788                     scope: scope
10789                 };
10790             }else{
10791                 config = {
10792                     key : key.key,
10793                     shift : key.shift,
10794                     ctrl : key.ctrl,
10795                     alt : key.alt,
10796                     fn: fn,
10797                     scope: scope
10798                 };
10799             }
10800             return new Roo.KeyMap(this, config);
10801         },
10802
10803         /**
10804          * Creates a KeyMap for this element
10805          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10806          * @return {Roo.KeyMap} The KeyMap created
10807          */
10808         addKeyMap : function(config){
10809             return new Roo.KeyMap(this, config);
10810         },
10811
10812         /**
10813          * Returns true if this element is scrollable.
10814          * @return {Boolean}
10815          */
10816          isScrollable : function(){
10817             var dom = this.dom;
10818             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10819         },
10820
10821         /**
10822          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
10823          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10824          * @param {Number} value The new scroll value
10825          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10826          * @return {Element} this
10827          */
10828
10829         scrollTo : function(side, value, animate){
10830             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10831             if(!animate || !A){
10832                 this.dom[prop] = value;
10833             }else{
10834                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10835                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10836             }
10837             return this;
10838         },
10839
10840         /**
10841          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10842          * within this element's scrollable range.
10843          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10844          * @param {Number} distance How far to scroll the element in pixels
10845          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10846          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10847          * was scrolled as far as it could go.
10848          */
10849          scroll : function(direction, distance, animate){
10850              if(!this.isScrollable()){
10851                  return;
10852              }
10853              var el = this.dom;
10854              var l = el.scrollLeft, t = el.scrollTop;
10855              var w = el.scrollWidth, h = el.scrollHeight;
10856              var cw = el.clientWidth, ch = el.clientHeight;
10857              direction = direction.toLowerCase();
10858              var scrolled = false;
10859              var a = this.preanim(arguments, 2);
10860              switch(direction){
10861                  case "l":
10862                  case "left":
10863                      if(w - l > cw){
10864                          var v = Math.min(l + distance, w-cw);
10865                          this.scrollTo("left", v, a);
10866                          scrolled = true;
10867                      }
10868                      break;
10869                 case "r":
10870                 case "right":
10871                      if(l > 0){
10872                          var v = Math.max(l - distance, 0);
10873                          this.scrollTo("left", v, a);
10874                          scrolled = true;
10875                      }
10876                      break;
10877                 case "t":
10878                 case "top":
10879                 case "up":
10880                      if(t > 0){
10881                          var v = Math.max(t - distance, 0);
10882                          this.scrollTo("top", v, a);
10883                          scrolled = true;
10884                      }
10885                      break;
10886                 case "b":
10887                 case "bottom":
10888                 case "down":
10889                      if(h - t > ch){
10890                          var v = Math.min(t + distance, h-ch);
10891                          this.scrollTo("top", v, a);
10892                          scrolled = true;
10893                      }
10894                      break;
10895              }
10896              return scrolled;
10897         },
10898
10899         /**
10900          * Translates the passed page coordinates into left/top css values for this element
10901          * @param {Number/Array} x The page x or an array containing [x, y]
10902          * @param {Number} y The page y
10903          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10904          */
10905         translatePoints : function(x, y){
10906             if(typeof x == 'object' || x instanceof Array){
10907                 y = x[1]; x = x[0];
10908             }
10909             var p = this.getStyle('position');
10910             var o = this.getXY();
10911
10912             var l = parseInt(this.getStyle('left'), 10);
10913             var t = parseInt(this.getStyle('top'), 10);
10914
10915             if(isNaN(l)){
10916                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10917             }
10918             if(isNaN(t)){
10919                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10920             }
10921
10922             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10923         },
10924
10925         /**
10926          * Returns the current scroll position of the element.
10927          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10928          */
10929         getScroll : function(){
10930             var d = this.dom, doc = document;
10931             if(d == doc || d == doc.body){
10932                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10933                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10934                 return {left: l, top: t};
10935             }else{
10936                 return {left: d.scrollLeft, top: d.scrollTop};
10937             }
10938         },
10939
10940         /**
10941          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10942          * are convert to standard 6 digit hex color.
10943          * @param {String} attr The css attribute
10944          * @param {String} defaultValue The default value to use when a valid color isn't found
10945          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10946          * YUI color anims.
10947          */
10948         getColor : function(attr, defaultValue, prefix){
10949             var v = this.getStyle(attr);
10950             if(!v || v == "transparent" || v == "inherit") {
10951                 return defaultValue;
10952             }
10953             var color = typeof prefix == "undefined" ? "#" : prefix;
10954             if(v.substr(0, 4) == "rgb("){
10955                 var rvs = v.slice(4, v.length -1).split(",");
10956                 for(var i = 0; i < 3; i++){
10957                     var h = parseInt(rvs[i]).toString(16);
10958                     if(h < 16){
10959                         h = "0" + h;
10960                     }
10961                     color += h;
10962                 }
10963             } else {
10964                 if(v.substr(0, 1) == "#"){
10965                     if(v.length == 4) {
10966                         for(var i = 1; i < 4; i++){
10967                             var c = v.charAt(i);
10968                             color +=  c + c;
10969                         }
10970                     }else if(v.length == 7){
10971                         color += v.substr(1);
10972                     }
10973                 }
10974             }
10975             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10976         },
10977
10978         /**
10979          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10980          * gradient background, rounded corners and a 4-way shadow.
10981          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10982          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10983          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10984          * @return {Roo.Element} this
10985          */
10986         boxWrap : function(cls){
10987             cls = cls || 'x-box';
10988             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10989             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10990             return el;
10991         },
10992
10993         /**
10994          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10995          * @param {String} namespace The namespace in which to look for the attribute
10996          * @param {String} name The attribute name
10997          * @return {String} The attribute value
10998          */
10999         getAttributeNS : Roo.isIE ? function(ns, name){
11000             var d = this.dom;
11001             var type = typeof d[ns+":"+name];
11002             if(type != 'undefined' && type != 'unknown'){
11003                 return d[ns+":"+name];
11004             }
11005             return d[name];
11006         } : function(ns, name){
11007             var d = this.dom;
11008             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11009         },
11010         
11011         
11012         /**
11013          * Sets or Returns the value the dom attribute value
11014          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11015          * @param {String} value (optional) The value to set the attribute to
11016          * @return {String} The attribute value
11017          */
11018         attr : function(name){
11019             if (arguments.length > 1) {
11020                 this.dom.setAttribute(name, arguments[1]);
11021                 return arguments[1];
11022             }
11023             if (typeof(name) == 'object') {
11024                 for(var i in name) {
11025                     this.attr(i, name[i]);
11026                 }
11027                 return name;
11028             }
11029             
11030             
11031             if (!this.dom.hasAttribute(name)) {
11032                 return undefined;
11033             }
11034             return this.dom.getAttribute(name);
11035         }
11036         
11037         
11038         
11039     };
11040
11041     var ep = El.prototype;
11042
11043     /**
11044      * Appends an event handler (Shorthand for addListener)
11045      * @param {String}   eventName     The type of event to append
11046      * @param {Function} fn        The method the event invokes
11047      * @param {Object} scope       (optional) The scope (this object) of the fn
11048      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11049      * @method
11050      */
11051     ep.on = ep.addListener;
11052         // backwards compat
11053     ep.mon = ep.addListener;
11054
11055     /**
11056      * Removes an event handler from this element (shorthand for removeListener)
11057      * @param {String} eventName the type of event to remove
11058      * @param {Function} fn the method the event invokes
11059      * @return {Roo.Element} this
11060      * @method
11061      */
11062     ep.un = ep.removeListener;
11063
11064     /**
11065      * true to automatically adjust width and height settings for box-model issues (default to true)
11066      */
11067     ep.autoBoxAdjust = true;
11068
11069     // private
11070     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11071
11072     // private
11073     El.addUnits = function(v, defaultUnit){
11074         if(v === "" || v == "auto"){
11075             return v;
11076         }
11077         if(v === undefined){
11078             return '';
11079         }
11080         if(typeof v == "number" || !El.unitPattern.test(v)){
11081             return v + (defaultUnit || 'px');
11082         }
11083         return v;
11084     };
11085
11086     // special markup used throughout Roo when box wrapping elements
11087     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
11088     /**
11089      * Visibility mode constant - Use visibility to hide element
11090      * @static
11091      * @type Number
11092      */
11093     El.VISIBILITY = 1;
11094     /**
11095      * Visibility mode constant - Use display to hide element
11096      * @static
11097      * @type Number
11098      */
11099     El.DISPLAY = 2;
11100
11101     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11102     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11103     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11104
11105
11106
11107     /**
11108      * @private
11109      */
11110     El.cache = {};
11111
11112     var docEl;
11113
11114     /**
11115      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11116      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11117      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11118      * @return {Element} The Element object
11119      * @static
11120      */
11121     El.get = function(el){
11122         var ex, elm, id;
11123         if(!el){ return null; }
11124         if(typeof el == "string"){ // element id
11125             if(!(elm = document.getElementById(el))){
11126                 return null;
11127             }
11128             if(ex = El.cache[el]){
11129                 ex.dom = elm;
11130             }else{
11131                 ex = El.cache[el] = new El(elm);
11132             }
11133             return ex;
11134         }else if(el.tagName){ // dom element
11135             if(!(id = el.id)){
11136                 id = Roo.id(el);
11137             }
11138             if(ex = El.cache[id]){
11139                 ex.dom = el;
11140             }else{
11141                 ex = El.cache[id] = new El(el);
11142             }
11143             return ex;
11144         }else if(el instanceof El){
11145             if(el != docEl){
11146                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11147                                                               // catch case where it hasn't been appended
11148                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11149             }
11150             return el;
11151         }else if(el.isComposite){
11152             return el;
11153         }else if(el instanceof Array){
11154             return El.select(el);
11155         }else if(el == document){
11156             // create a bogus element object representing the document object
11157             if(!docEl){
11158                 var f = function(){};
11159                 f.prototype = El.prototype;
11160                 docEl = new f();
11161                 docEl.dom = document;
11162             }
11163             return docEl;
11164         }
11165         return null;
11166     };
11167
11168     // private
11169     El.uncache = function(el){
11170         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11171             if(a[i]){
11172                 delete El.cache[a[i].id || a[i]];
11173             }
11174         }
11175     };
11176
11177     // private
11178     // Garbage collection - uncache elements/purge listeners on orphaned elements
11179     // so we don't hold a reference and cause the browser to retain them
11180     El.garbageCollect = function(){
11181         if(!Roo.enableGarbageCollector){
11182             clearInterval(El.collectorThread);
11183             return;
11184         }
11185         for(var eid in El.cache){
11186             var el = El.cache[eid], d = el.dom;
11187             // -------------------------------------------------------
11188             // Determining what is garbage:
11189             // -------------------------------------------------------
11190             // !d
11191             // dom node is null, definitely garbage
11192             // -------------------------------------------------------
11193             // !d.parentNode
11194             // no parentNode == direct orphan, definitely garbage
11195             // -------------------------------------------------------
11196             // !d.offsetParent && !document.getElementById(eid)
11197             // display none elements have no offsetParent so we will
11198             // also try to look it up by it's id. However, check
11199             // offsetParent first so we don't do unneeded lookups.
11200             // This enables collection of elements that are not orphans
11201             // directly, but somewhere up the line they have an orphan
11202             // parent.
11203             // -------------------------------------------------------
11204             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11205                 delete El.cache[eid];
11206                 if(d && Roo.enableListenerCollection){
11207                     E.purgeElement(d);
11208                 }
11209             }
11210         }
11211     }
11212     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11213
11214
11215     // dom is optional
11216     El.Flyweight = function(dom){
11217         this.dom = dom;
11218     };
11219     El.Flyweight.prototype = El.prototype;
11220
11221     El._flyweights = {};
11222     /**
11223      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11224      * the dom node can be overwritten by other code.
11225      * @param {String/HTMLElement} el The dom node or id
11226      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11227      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11228      * @static
11229      * @return {Element} The shared Element object
11230      */
11231     El.fly = function(el, named){
11232         named = named || '_global';
11233         el = Roo.getDom(el);
11234         if(!el){
11235             return null;
11236         }
11237         if(!El._flyweights[named]){
11238             El._flyweights[named] = new El.Flyweight();
11239         }
11240         El._flyweights[named].dom = el;
11241         return El._flyweights[named];
11242     };
11243
11244     /**
11245      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11246      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11247      * Shorthand of {@link Roo.Element#get}
11248      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11249      * @return {Element} The Element object
11250      * @member Roo
11251      * @method get
11252      */
11253     Roo.get = El.get;
11254     /**
11255      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11256      * the dom node can be overwritten by other code.
11257      * Shorthand of {@link Roo.Element#fly}
11258      * @param {String/HTMLElement} el The dom node or id
11259      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11260      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11261      * @static
11262      * @return {Element} The shared Element object
11263      * @member Roo
11264      * @method fly
11265      */
11266     Roo.fly = El.fly;
11267
11268     // speedy lookup for elements never to box adjust
11269     var noBoxAdjust = Roo.isStrict ? {
11270         select:1
11271     } : {
11272         input:1, select:1, textarea:1
11273     };
11274     if(Roo.isIE || Roo.isGecko){
11275         noBoxAdjust['button'] = 1;
11276     }
11277
11278
11279     Roo.EventManager.on(window, 'unload', function(){
11280         delete El.cache;
11281         delete El._flyweights;
11282     });
11283 })();
11284
11285
11286
11287
11288 if(Roo.DomQuery){
11289     Roo.Element.selectorFunction = Roo.DomQuery.select;
11290 }
11291
11292 Roo.Element.select = function(selector, unique, root){
11293     var els;
11294     if(typeof selector == "string"){
11295         els = Roo.Element.selectorFunction(selector, root);
11296     }else if(selector.length !== undefined){
11297         els = selector;
11298     }else{
11299         throw "Invalid selector";
11300     }
11301     if(unique === true){
11302         return new Roo.CompositeElement(els);
11303     }else{
11304         return new Roo.CompositeElementLite(els);
11305     }
11306 };
11307 /**
11308  * Selects elements based on the passed CSS selector to enable working on them as 1.
11309  * @param {String/Array} selector The CSS selector or an array of elements
11310  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11311  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11312  * @return {CompositeElementLite/CompositeElement}
11313  * @member Roo
11314  * @method select
11315  */
11316 Roo.select = Roo.Element.select;
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331 /*
11332  * Based on:
11333  * Ext JS Library 1.1.1
11334  * Copyright(c) 2006-2007, Ext JS, LLC.
11335  *
11336  * Originally Released Under LGPL - original licence link has changed is not relivant.
11337  *
11338  * Fork - LGPL
11339  * <script type="text/javascript">
11340  */
11341
11342
11343
11344 //Notifies Element that fx methods are available
11345 Roo.enableFx = true;
11346
11347 /**
11348  * @class Roo.Fx
11349  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11350  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11351  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11352  * Element effects to work.</p><br/>
11353  *
11354  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11355  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11356  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11357  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11358  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11359  * expected results and should be done with care.</p><br/>
11360  *
11361  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11362  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11363 <pre>
11364 Value  Description
11365 -----  -----------------------------
11366 tl     The top left corner
11367 t      The center of the top edge
11368 tr     The top right corner
11369 l      The center of the left edge
11370 r      The center of the right edge
11371 bl     The bottom left corner
11372 b      The center of the bottom edge
11373 br     The bottom right corner
11374 </pre>
11375  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11376  * below are common options that can be passed to any Fx method.</b>
11377  * @cfg {Function} callback A function called when the effect is finished
11378  * @cfg {Object} scope The scope of the effect function
11379  * @cfg {String} easing A valid Easing value for the effect
11380  * @cfg {String} afterCls A css class to apply after the effect
11381  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11382  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11383  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11384  * effects that end with the element being visually hidden, ignored otherwise)
11385  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11386  * a function which returns such a specification that will be applied to the Element after the effect finishes
11387  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11388  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11389  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11390  */
11391 Roo.Fx = {
11392         /**
11393          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11394          * origin for the slide effect.  This function automatically handles wrapping the element with
11395          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11396          * Usage:
11397          *<pre><code>
11398 // default: slide the element in from the top
11399 el.slideIn();
11400
11401 // custom: slide the element in from the right with a 2-second duration
11402 el.slideIn('r', { duration: 2 });
11403
11404 // common config options shown with default values
11405 el.slideIn('t', {
11406     easing: 'easeOut',
11407     duration: .5
11408 });
11409 </code></pre>
11410          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11411          * @param {Object} options (optional) Object literal with any of the Fx config options
11412          * @return {Roo.Element} The Element
11413          */
11414     slideIn : function(anchor, o){
11415         var el = this.getFxEl();
11416         o = o || {};
11417
11418         el.queueFx(o, function(){
11419
11420             anchor = anchor || "t";
11421
11422             // fix display to visibility
11423             this.fixDisplay();
11424
11425             // restore values after effect
11426             var r = this.getFxRestore();
11427             var b = this.getBox();
11428             // fixed size for slide
11429             this.setSize(b);
11430
11431             // wrap if needed
11432             var wrap = this.fxWrap(r.pos, o, "hidden");
11433
11434             var st = this.dom.style;
11435             st.visibility = "visible";
11436             st.position = "absolute";
11437
11438             // clear out temp styles after slide and unwrap
11439             var after = function(){
11440                 el.fxUnwrap(wrap, r.pos, o);
11441                 st.width = r.width;
11442                 st.height = r.height;
11443                 el.afterFx(o);
11444             };
11445             // time to calc the positions
11446             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11447
11448             switch(anchor.toLowerCase()){
11449                 case "t":
11450                     wrap.setSize(b.width, 0);
11451                     st.left = st.bottom = "0";
11452                     a = {height: bh};
11453                 break;
11454                 case "l":
11455                     wrap.setSize(0, b.height);
11456                     st.right = st.top = "0";
11457                     a = {width: bw};
11458                 break;
11459                 case "r":
11460                     wrap.setSize(0, b.height);
11461                     wrap.setX(b.right);
11462                     st.left = st.top = "0";
11463                     a = {width: bw, points: pt};
11464                 break;
11465                 case "b":
11466                     wrap.setSize(b.width, 0);
11467                     wrap.setY(b.bottom);
11468                     st.left = st.top = "0";
11469                     a = {height: bh, points: pt};
11470                 break;
11471                 case "tl":
11472                     wrap.setSize(0, 0);
11473                     st.right = st.bottom = "0";
11474                     a = {width: bw, height: bh};
11475                 break;
11476                 case "bl":
11477                     wrap.setSize(0, 0);
11478                     wrap.setY(b.y+b.height);
11479                     st.right = st.top = "0";
11480                     a = {width: bw, height: bh, points: pt};
11481                 break;
11482                 case "br":
11483                     wrap.setSize(0, 0);
11484                     wrap.setXY([b.right, b.bottom]);
11485                     st.left = st.top = "0";
11486                     a = {width: bw, height: bh, points: pt};
11487                 break;
11488                 case "tr":
11489                     wrap.setSize(0, 0);
11490                     wrap.setX(b.x+b.width);
11491                     st.left = st.bottom = "0";
11492                     a = {width: bw, height: bh, points: pt};
11493                 break;
11494             }
11495             this.dom.style.visibility = "visible";
11496             wrap.show();
11497
11498             arguments.callee.anim = wrap.fxanim(a,
11499                 o,
11500                 'motion',
11501                 .5,
11502                 'easeOut', after);
11503         });
11504         return this;
11505     },
11506     
11507         /**
11508          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11509          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11510          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11511          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11512          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11513          * Usage:
11514          *<pre><code>
11515 // default: slide the element out to the top
11516 el.slideOut();
11517
11518 // custom: slide the element out to the right with a 2-second duration
11519 el.slideOut('r', { duration: 2 });
11520
11521 // common config options shown with default values
11522 el.slideOut('t', {
11523     easing: 'easeOut',
11524     duration: .5,
11525     remove: false,
11526     useDisplay: false
11527 });
11528 </code></pre>
11529          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11530          * @param {Object} options (optional) Object literal with any of the Fx config options
11531          * @return {Roo.Element} The Element
11532          */
11533     slideOut : function(anchor, o){
11534         var el = this.getFxEl();
11535         o = o || {};
11536
11537         el.queueFx(o, function(){
11538
11539             anchor = anchor || "t";
11540
11541             // restore values after effect
11542             var r = this.getFxRestore();
11543             
11544             var b = this.getBox();
11545             // fixed size for slide
11546             this.setSize(b);
11547
11548             // wrap if needed
11549             var wrap = this.fxWrap(r.pos, o, "visible");
11550
11551             var st = this.dom.style;
11552             st.visibility = "visible";
11553             st.position = "absolute";
11554
11555             wrap.setSize(b);
11556
11557             var after = function(){
11558                 if(o.useDisplay){
11559                     el.setDisplayed(false);
11560                 }else{
11561                     el.hide();
11562                 }
11563
11564                 el.fxUnwrap(wrap, r.pos, o);
11565
11566                 st.width = r.width;
11567                 st.height = r.height;
11568
11569                 el.afterFx(o);
11570             };
11571
11572             var a, zero = {to: 0};
11573             switch(anchor.toLowerCase()){
11574                 case "t":
11575                     st.left = st.bottom = "0";
11576                     a = {height: zero};
11577                 break;
11578                 case "l":
11579                     st.right = st.top = "0";
11580                     a = {width: zero};
11581                 break;
11582                 case "r":
11583                     st.left = st.top = "0";
11584                     a = {width: zero, points: {to:[b.right, b.y]}};
11585                 break;
11586                 case "b":
11587                     st.left = st.top = "0";
11588                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11589                 break;
11590                 case "tl":
11591                     st.right = st.bottom = "0";
11592                     a = {width: zero, height: zero};
11593                 break;
11594                 case "bl":
11595                     st.right = st.top = "0";
11596                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11597                 break;
11598                 case "br":
11599                     st.left = st.top = "0";
11600                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11601                 break;
11602                 case "tr":
11603                     st.left = st.bottom = "0";
11604                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11605                 break;
11606             }
11607
11608             arguments.callee.anim = wrap.fxanim(a,
11609                 o,
11610                 'motion',
11611                 .5,
11612                 "easeOut", after);
11613         });
11614         return this;
11615     },
11616
11617         /**
11618          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11619          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11620          * The element must be removed from the DOM using the 'remove' config option if desired.
11621          * Usage:
11622          *<pre><code>
11623 // default
11624 el.puff();
11625
11626 // common config options shown with default values
11627 el.puff({
11628     easing: 'easeOut',
11629     duration: .5,
11630     remove: false,
11631     useDisplay: false
11632 });
11633 </code></pre>
11634          * @param {Object} options (optional) Object literal with any of the Fx config options
11635          * @return {Roo.Element} The Element
11636          */
11637     puff : function(o){
11638         var el = this.getFxEl();
11639         o = o || {};
11640
11641         el.queueFx(o, function(){
11642             this.clearOpacity();
11643             this.show();
11644
11645             // restore values after effect
11646             var r = this.getFxRestore();
11647             var st = this.dom.style;
11648
11649             var after = function(){
11650                 if(o.useDisplay){
11651                     el.setDisplayed(false);
11652                 }else{
11653                     el.hide();
11654                 }
11655
11656                 el.clearOpacity();
11657
11658                 el.setPositioning(r.pos);
11659                 st.width = r.width;
11660                 st.height = r.height;
11661                 st.fontSize = '';
11662                 el.afterFx(o);
11663             };
11664
11665             var width = this.getWidth();
11666             var height = this.getHeight();
11667
11668             arguments.callee.anim = this.fxanim({
11669                     width : {to: this.adjustWidth(width * 2)},
11670                     height : {to: this.adjustHeight(height * 2)},
11671                     points : {by: [-(width * .5), -(height * .5)]},
11672                     opacity : {to: 0},
11673                     fontSize: {to:200, unit: "%"}
11674                 },
11675                 o,
11676                 'motion',
11677                 .5,
11678                 "easeOut", after);
11679         });
11680         return this;
11681     },
11682
11683         /**
11684          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11685          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11686          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11687          * Usage:
11688          *<pre><code>
11689 // default
11690 el.switchOff();
11691
11692 // all config options shown with default values
11693 el.switchOff({
11694     easing: 'easeIn',
11695     duration: .3,
11696     remove: false,
11697     useDisplay: false
11698 });
11699 </code></pre>
11700          * @param {Object} options (optional) Object literal with any of the Fx config options
11701          * @return {Roo.Element} The Element
11702          */
11703     switchOff : function(o){
11704         var el = this.getFxEl();
11705         o = o || {};
11706
11707         el.queueFx(o, function(){
11708             this.clearOpacity();
11709             this.clip();
11710
11711             // restore values after effect
11712             var r = this.getFxRestore();
11713             var st = this.dom.style;
11714
11715             var after = function(){
11716                 if(o.useDisplay){
11717                     el.setDisplayed(false);
11718                 }else{
11719                     el.hide();
11720                 }
11721
11722                 el.clearOpacity();
11723                 el.setPositioning(r.pos);
11724                 st.width = r.width;
11725                 st.height = r.height;
11726
11727                 el.afterFx(o);
11728             };
11729
11730             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11731                 this.clearOpacity();
11732                 (function(){
11733                     this.fxanim({
11734                         height:{to:1},
11735                         points:{by:[0, this.getHeight() * .5]}
11736                     }, o, 'motion', 0.3, 'easeIn', after);
11737                 }).defer(100, this);
11738             });
11739         });
11740         return this;
11741     },
11742
11743     /**
11744      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11745      * changed using the "attr" config option) and then fading back to the original color. If no original
11746      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11747      * Usage:
11748 <pre><code>
11749 // default: highlight background to yellow
11750 el.highlight();
11751
11752 // custom: highlight foreground text to blue for 2 seconds
11753 el.highlight("0000ff", { attr: 'color', duration: 2 });
11754
11755 // common config options shown with default values
11756 el.highlight("ffff9c", {
11757     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11758     endColor: (current color) or "ffffff",
11759     easing: 'easeIn',
11760     duration: 1
11761 });
11762 </code></pre>
11763      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11764      * @param {Object} options (optional) Object literal with any of the Fx config options
11765      * @return {Roo.Element} The Element
11766      */ 
11767     highlight : function(color, o){
11768         var el = this.getFxEl();
11769         o = o || {};
11770
11771         el.queueFx(o, function(){
11772             color = color || "ffff9c";
11773             attr = o.attr || "backgroundColor";
11774
11775             this.clearOpacity();
11776             this.show();
11777
11778             var origColor = this.getColor(attr);
11779             var restoreColor = this.dom.style[attr];
11780             endColor = (o.endColor || origColor) || "ffffff";
11781
11782             var after = function(){
11783                 el.dom.style[attr] = restoreColor;
11784                 el.afterFx(o);
11785             };
11786
11787             var a = {};
11788             a[attr] = {from: color, to: endColor};
11789             arguments.callee.anim = this.fxanim(a,
11790                 o,
11791                 'color',
11792                 1,
11793                 'easeIn', after);
11794         });
11795         return this;
11796     },
11797
11798    /**
11799     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11800     * Usage:
11801 <pre><code>
11802 // default: a single light blue ripple
11803 el.frame();
11804
11805 // custom: 3 red ripples lasting 3 seconds total
11806 el.frame("ff0000", 3, { duration: 3 });
11807
11808 // common config options shown with default values
11809 el.frame("C3DAF9", 1, {
11810     duration: 1 //duration of entire animation (not each individual ripple)
11811     // Note: Easing is not configurable and will be ignored if included
11812 });
11813 </code></pre>
11814     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11815     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11816     * @param {Object} options (optional) Object literal with any of the Fx config options
11817     * @return {Roo.Element} The Element
11818     */
11819     frame : function(color, count, o){
11820         var el = this.getFxEl();
11821         o = o || {};
11822
11823         el.queueFx(o, function(){
11824             color = color || "#C3DAF9";
11825             if(color.length == 6){
11826                 color = "#" + color;
11827             }
11828             count = count || 1;
11829             duration = o.duration || 1;
11830             this.show();
11831
11832             var b = this.getBox();
11833             var animFn = function(){
11834                 var proxy = this.createProxy({
11835
11836                      style:{
11837                         visbility:"hidden",
11838                         position:"absolute",
11839                         "z-index":"35000", // yee haw
11840                         border:"0px solid " + color
11841                      }
11842                   });
11843                 var scale = Roo.isBorderBox ? 2 : 1;
11844                 proxy.animate({
11845                     top:{from:b.y, to:b.y - 20},
11846                     left:{from:b.x, to:b.x - 20},
11847                     borderWidth:{from:0, to:10},
11848                     opacity:{from:1, to:0},
11849                     height:{from:b.height, to:(b.height + (20*scale))},
11850                     width:{from:b.width, to:(b.width + (20*scale))}
11851                 }, duration, function(){
11852                     proxy.remove();
11853                 });
11854                 if(--count > 0){
11855                      animFn.defer((duration/2)*1000, this);
11856                 }else{
11857                     el.afterFx(o);
11858                 }
11859             };
11860             animFn.call(this);
11861         });
11862         return this;
11863     },
11864
11865    /**
11866     * Creates a pause before any subsequent queued effects begin.  If there are
11867     * no effects queued after the pause it will have no effect.
11868     * Usage:
11869 <pre><code>
11870 el.pause(1);
11871 </code></pre>
11872     * @param {Number} seconds The length of time to pause (in seconds)
11873     * @return {Roo.Element} The Element
11874     */
11875     pause : function(seconds){
11876         var el = this.getFxEl();
11877         var o = {};
11878
11879         el.queueFx(o, function(){
11880             setTimeout(function(){
11881                 el.afterFx(o);
11882             }, seconds * 1000);
11883         });
11884         return this;
11885     },
11886
11887    /**
11888     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11889     * using the "endOpacity" config option.
11890     * Usage:
11891 <pre><code>
11892 // default: fade in from opacity 0 to 100%
11893 el.fadeIn();
11894
11895 // custom: fade in from opacity 0 to 75% over 2 seconds
11896 el.fadeIn({ endOpacity: .75, duration: 2});
11897
11898 // common config options shown with default values
11899 el.fadeIn({
11900     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11901     easing: 'easeOut',
11902     duration: .5
11903 });
11904 </code></pre>
11905     * @param {Object} options (optional) Object literal with any of the Fx config options
11906     * @return {Roo.Element} The Element
11907     */
11908     fadeIn : function(o){
11909         var el = this.getFxEl();
11910         o = o || {};
11911         el.queueFx(o, function(){
11912             this.setOpacity(0);
11913             this.fixDisplay();
11914             this.dom.style.visibility = 'visible';
11915             var to = o.endOpacity || 1;
11916             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11917                 o, null, .5, "easeOut", function(){
11918                 if(to == 1){
11919                     this.clearOpacity();
11920                 }
11921                 el.afterFx(o);
11922             });
11923         });
11924         return this;
11925     },
11926
11927    /**
11928     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11929     * using the "endOpacity" config option.
11930     * Usage:
11931 <pre><code>
11932 // default: fade out from the element's current opacity to 0
11933 el.fadeOut();
11934
11935 // custom: fade out from the element's current opacity to 25% over 2 seconds
11936 el.fadeOut({ endOpacity: .25, duration: 2});
11937
11938 // common config options shown with default values
11939 el.fadeOut({
11940     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11941     easing: 'easeOut',
11942     duration: .5
11943     remove: false,
11944     useDisplay: false
11945 });
11946 </code></pre>
11947     * @param {Object} options (optional) Object literal with any of the Fx config options
11948     * @return {Roo.Element} The Element
11949     */
11950     fadeOut : function(o){
11951         var el = this.getFxEl();
11952         o = o || {};
11953         el.queueFx(o, function(){
11954             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11955                 o, null, .5, "easeOut", function(){
11956                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11957                      this.dom.style.display = "none";
11958                 }else{
11959                      this.dom.style.visibility = "hidden";
11960                 }
11961                 this.clearOpacity();
11962                 el.afterFx(o);
11963             });
11964         });
11965         return this;
11966     },
11967
11968    /**
11969     * Animates the transition of an element's dimensions from a starting height/width
11970     * to an ending height/width.
11971     * Usage:
11972 <pre><code>
11973 // change height and width to 100x100 pixels
11974 el.scale(100, 100);
11975
11976 // common config options shown with default values.  The height and width will default to
11977 // the element's existing values if passed as null.
11978 el.scale(
11979     [element's width],
11980     [element's height], {
11981     easing: 'easeOut',
11982     duration: .35
11983 });
11984 </code></pre>
11985     * @param {Number} width  The new width (pass undefined to keep the original width)
11986     * @param {Number} height  The new height (pass undefined to keep the original height)
11987     * @param {Object} options (optional) Object literal with any of the Fx config options
11988     * @return {Roo.Element} The Element
11989     */
11990     scale : function(w, h, o){
11991         this.shift(Roo.apply({}, o, {
11992             width: w,
11993             height: h
11994         }));
11995         return this;
11996     },
11997
11998    /**
11999     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12000     * Any of these properties not specified in the config object will not be changed.  This effect 
12001     * requires that at least one new dimension, position or opacity setting must be passed in on
12002     * the config object in order for the function to have any effect.
12003     * Usage:
12004 <pre><code>
12005 // slide the element horizontally to x position 200 while changing the height and opacity
12006 el.shift({ x: 200, height: 50, opacity: .8 });
12007
12008 // common config options shown with default values.
12009 el.shift({
12010     width: [element's width],
12011     height: [element's height],
12012     x: [element's x position],
12013     y: [element's y position],
12014     opacity: [element's opacity],
12015     easing: 'easeOut',
12016     duration: .35
12017 });
12018 </code></pre>
12019     * @param {Object} options  Object literal with any of the Fx config options
12020     * @return {Roo.Element} The Element
12021     */
12022     shift : function(o){
12023         var el = this.getFxEl();
12024         o = o || {};
12025         el.queueFx(o, function(){
12026             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12027             if(w !== undefined){
12028                 a.width = {to: this.adjustWidth(w)};
12029             }
12030             if(h !== undefined){
12031                 a.height = {to: this.adjustHeight(h)};
12032             }
12033             if(x !== undefined || y !== undefined){
12034                 a.points = {to: [
12035                     x !== undefined ? x : this.getX(),
12036                     y !== undefined ? y : this.getY()
12037                 ]};
12038             }
12039             if(op !== undefined){
12040                 a.opacity = {to: op};
12041             }
12042             if(o.xy !== undefined){
12043                 a.points = {to: o.xy};
12044             }
12045             arguments.callee.anim = this.fxanim(a,
12046                 o, 'motion', .35, "easeOut", function(){
12047                 el.afterFx(o);
12048             });
12049         });
12050         return this;
12051     },
12052
12053         /**
12054          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12055          * ending point of the effect.
12056          * Usage:
12057          *<pre><code>
12058 // default: slide the element downward while fading out
12059 el.ghost();
12060
12061 // custom: slide the element out to the right with a 2-second duration
12062 el.ghost('r', { duration: 2 });
12063
12064 // common config options shown with default values
12065 el.ghost('b', {
12066     easing: 'easeOut',
12067     duration: .5
12068     remove: false,
12069     useDisplay: false
12070 });
12071 </code></pre>
12072          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12073          * @param {Object} options (optional) Object literal with any of the Fx config options
12074          * @return {Roo.Element} The Element
12075          */
12076     ghost : function(anchor, o){
12077         var el = this.getFxEl();
12078         o = o || {};
12079
12080         el.queueFx(o, function(){
12081             anchor = anchor || "b";
12082
12083             // restore values after effect
12084             var r = this.getFxRestore();
12085             var w = this.getWidth(),
12086                 h = this.getHeight();
12087
12088             var st = this.dom.style;
12089
12090             var after = function(){
12091                 if(o.useDisplay){
12092                     el.setDisplayed(false);
12093                 }else{
12094                     el.hide();
12095                 }
12096
12097                 el.clearOpacity();
12098                 el.setPositioning(r.pos);
12099                 st.width = r.width;
12100                 st.height = r.height;
12101
12102                 el.afterFx(o);
12103             };
12104
12105             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12106             switch(anchor.toLowerCase()){
12107                 case "t":
12108                     pt.by = [0, -h];
12109                 break;
12110                 case "l":
12111                     pt.by = [-w, 0];
12112                 break;
12113                 case "r":
12114                     pt.by = [w, 0];
12115                 break;
12116                 case "b":
12117                     pt.by = [0, h];
12118                 break;
12119                 case "tl":
12120                     pt.by = [-w, -h];
12121                 break;
12122                 case "bl":
12123                     pt.by = [-w, h];
12124                 break;
12125                 case "br":
12126                     pt.by = [w, h];
12127                 break;
12128                 case "tr":
12129                     pt.by = [w, -h];
12130                 break;
12131             }
12132
12133             arguments.callee.anim = this.fxanim(a,
12134                 o,
12135                 'motion',
12136                 .5,
12137                 "easeOut", after);
12138         });
12139         return this;
12140     },
12141
12142         /**
12143          * Ensures that all effects queued after syncFx is called on the element are
12144          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12145          * @return {Roo.Element} The Element
12146          */
12147     syncFx : function(){
12148         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12149             block : false,
12150             concurrent : true,
12151             stopFx : false
12152         });
12153         return this;
12154     },
12155
12156         /**
12157          * Ensures that all effects queued after sequenceFx is called on the element are
12158          * run in sequence.  This is the opposite of {@link #syncFx}.
12159          * @return {Roo.Element} The Element
12160          */
12161     sequenceFx : function(){
12162         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12163             block : false,
12164             concurrent : false,
12165             stopFx : false
12166         });
12167         return this;
12168     },
12169
12170         /* @private */
12171     nextFx : function(){
12172         var ef = this.fxQueue[0];
12173         if(ef){
12174             ef.call(this);
12175         }
12176     },
12177
12178         /**
12179          * Returns true if the element has any effects actively running or queued, else returns false.
12180          * @return {Boolean} True if element has active effects, else false
12181          */
12182     hasActiveFx : function(){
12183         return this.fxQueue && this.fxQueue[0];
12184     },
12185
12186         /**
12187          * Stops any running effects and clears the element's internal effects queue if it contains
12188          * any additional effects that haven't started yet.
12189          * @return {Roo.Element} The Element
12190          */
12191     stopFx : function(){
12192         if(this.hasActiveFx()){
12193             var cur = this.fxQueue[0];
12194             if(cur && cur.anim && cur.anim.isAnimated()){
12195                 this.fxQueue = [cur]; // clear out others
12196                 cur.anim.stop(true);
12197             }
12198         }
12199         return this;
12200     },
12201
12202         /* @private */
12203     beforeFx : function(o){
12204         if(this.hasActiveFx() && !o.concurrent){
12205            if(o.stopFx){
12206                this.stopFx();
12207                return true;
12208            }
12209            return false;
12210         }
12211         return true;
12212     },
12213
12214         /**
12215          * Returns true if the element is currently blocking so that no other effect can be queued
12216          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12217          * used to ensure that an effect initiated by a user action runs to completion prior to the
12218          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12219          * @return {Boolean} True if blocking, else false
12220          */
12221     hasFxBlock : function(){
12222         var q = this.fxQueue;
12223         return q && q[0] && q[0].block;
12224     },
12225
12226         /* @private */
12227     queueFx : function(o, fn){
12228         if(!this.fxQueue){
12229             this.fxQueue = [];
12230         }
12231         if(!this.hasFxBlock()){
12232             Roo.applyIf(o, this.fxDefaults);
12233             if(!o.concurrent){
12234                 var run = this.beforeFx(o);
12235                 fn.block = o.block;
12236                 this.fxQueue.push(fn);
12237                 if(run){
12238                     this.nextFx();
12239                 }
12240             }else{
12241                 fn.call(this);
12242             }
12243         }
12244         return this;
12245     },
12246
12247         /* @private */
12248     fxWrap : function(pos, o, vis){
12249         var wrap;
12250         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12251             var wrapXY;
12252             if(o.fixPosition){
12253                 wrapXY = this.getXY();
12254             }
12255             var div = document.createElement("div");
12256             div.style.visibility = vis;
12257             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12258             wrap.setPositioning(pos);
12259             if(wrap.getStyle("position") == "static"){
12260                 wrap.position("relative");
12261             }
12262             this.clearPositioning('auto');
12263             wrap.clip();
12264             wrap.dom.appendChild(this.dom);
12265             if(wrapXY){
12266                 wrap.setXY(wrapXY);
12267             }
12268         }
12269         return wrap;
12270     },
12271
12272         /* @private */
12273     fxUnwrap : function(wrap, pos, o){
12274         this.clearPositioning();
12275         this.setPositioning(pos);
12276         if(!o.wrap){
12277             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12278             wrap.remove();
12279         }
12280     },
12281
12282         /* @private */
12283     getFxRestore : function(){
12284         var st = this.dom.style;
12285         return {pos: this.getPositioning(), width: st.width, height : st.height};
12286     },
12287
12288         /* @private */
12289     afterFx : function(o){
12290         if(o.afterStyle){
12291             this.applyStyles(o.afterStyle);
12292         }
12293         if(o.afterCls){
12294             this.addClass(o.afterCls);
12295         }
12296         if(o.remove === true){
12297             this.remove();
12298         }
12299         Roo.callback(o.callback, o.scope, [this]);
12300         if(!o.concurrent){
12301             this.fxQueue.shift();
12302             this.nextFx();
12303         }
12304     },
12305
12306         /* @private */
12307     getFxEl : function(){ // support for composite element fx
12308         return Roo.get(this.dom);
12309     },
12310
12311         /* @private */
12312     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12313         animType = animType || 'run';
12314         opt = opt || {};
12315         var anim = Roo.lib.Anim[animType](
12316             this.dom, args,
12317             (opt.duration || defaultDur) || .35,
12318             (opt.easing || defaultEase) || 'easeOut',
12319             function(){
12320                 Roo.callback(cb, this);
12321             },
12322             this
12323         );
12324         opt.anim = anim;
12325         return anim;
12326     }
12327 };
12328
12329 // backwords compat
12330 Roo.Fx.resize = Roo.Fx.scale;
12331
12332 //When included, Roo.Fx is automatically applied to Element so that all basic
12333 //effects are available directly via the Element API
12334 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12335  * Based on:
12336  * Ext JS Library 1.1.1
12337  * Copyright(c) 2006-2007, Ext JS, LLC.
12338  *
12339  * Originally Released Under LGPL - original licence link has changed is not relivant.
12340  *
12341  * Fork - LGPL
12342  * <script type="text/javascript">
12343  */
12344
12345
12346 /**
12347  * @class Roo.CompositeElement
12348  * Standard composite class. Creates a Roo.Element for every element in the collection.
12349  * <br><br>
12350  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12351  * actions will be performed on all the elements in this collection.</b>
12352  * <br><br>
12353  * All methods return <i>this</i> and can be chained.
12354  <pre><code>
12355  var els = Roo.select("#some-el div.some-class", true);
12356  // or select directly from an existing element
12357  var el = Roo.get('some-el');
12358  el.select('div.some-class', true);
12359
12360  els.setWidth(100); // all elements become 100 width
12361  els.hide(true); // all elements fade out and hide
12362  // or
12363  els.setWidth(100).hide(true);
12364  </code></pre>
12365  */
12366 Roo.CompositeElement = function(els){
12367     this.elements = [];
12368     this.addElements(els);
12369 };
12370 Roo.CompositeElement.prototype = {
12371     isComposite: true,
12372     addElements : function(els){
12373         if(!els) {
12374             return this;
12375         }
12376         if(typeof els == "string"){
12377             els = Roo.Element.selectorFunction(els);
12378         }
12379         var yels = this.elements;
12380         var index = yels.length-1;
12381         for(var i = 0, len = els.length; i < len; i++) {
12382                 yels[++index] = Roo.get(els[i]);
12383         }
12384         return this;
12385     },
12386
12387     /**
12388     * Clears this composite and adds the elements returned by the passed selector.
12389     * @param {String/Array} els A string CSS selector, an array of elements or an element
12390     * @return {CompositeElement} this
12391     */
12392     fill : function(els){
12393         this.elements = [];
12394         this.add(els);
12395         return this;
12396     },
12397
12398     /**
12399     * Filters this composite to only elements that match the passed selector.
12400     * @param {String} selector A string CSS selector
12401     * @param {Boolean} inverse return inverse filter (not matches)
12402     * @return {CompositeElement} this
12403     */
12404     filter : function(selector, inverse){
12405         var els = [];
12406         inverse = inverse || false;
12407         this.each(function(el){
12408             var match = inverse ? !el.is(selector) : el.is(selector);
12409             if(match){
12410                 els[els.length] = el.dom;
12411             }
12412         });
12413         this.fill(els);
12414         return this;
12415     },
12416
12417     invoke : function(fn, args){
12418         var els = this.elements;
12419         for(var i = 0, len = els.length; i < len; i++) {
12420                 Roo.Element.prototype[fn].apply(els[i], args);
12421         }
12422         return this;
12423     },
12424     /**
12425     * Adds elements to this composite.
12426     * @param {String/Array} els A string CSS selector, an array of elements or an element
12427     * @return {CompositeElement} this
12428     */
12429     add : function(els){
12430         if(typeof els == "string"){
12431             this.addElements(Roo.Element.selectorFunction(els));
12432         }else if(els.length !== undefined){
12433             this.addElements(els);
12434         }else{
12435             this.addElements([els]);
12436         }
12437         return this;
12438     },
12439     /**
12440     * Calls the passed function passing (el, this, index) for each element in this composite.
12441     * @param {Function} fn The function to call
12442     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12443     * @return {CompositeElement} this
12444     */
12445     each : function(fn, scope){
12446         var els = this.elements;
12447         for(var i = 0, len = els.length; i < len; i++){
12448             if(fn.call(scope || els[i], els[i], this, i) === false) {
12449                 break;
12450             }
12451         }
12452         return this;
12453     },
12454
12455     /**
12456      * Returns the Element object at the specified index
12457      * @param {Number} index
12458      * @return {Roo.Element}
12459      */
12460     item : function(index){
12461         return this.elements[index] || null;
12462     },
12463
12464     /**
12465      * Returns the first Element
12466      * @return {Roo.Element}
12467      */
12468     first : function(){
12469         return this.item(0);
12470     },
12471
12472     /**
12473      * Returns the last Element
12474      * @return {Roo.Element}
12475      */
12476     last : function(){
12477         return this.item(this.elements.length-1);
12478     },
12479
12480     /**
12481      * Returns the number of elements in this composite
12482      * @return Number
12483      */
12484     getCount : function(){
12485         return this.elements.length;
12486     },
12487
12488     /**
12489      * Returns true if this composite contains the passed element
12490      * @return Boolean
12491      */
12492     contains : function(el){
12493         return this.indexOf(el) !== -1;
12494     },
12495
12496     /**
12497      * Returns true if this composite contains the passed element
12498      * @return Boolean
12499      */
12500     indexOf : function(el){
12501         return this.elements.indexOf(Roo.get(el));
12502     },
12503
12504
12505     /**
12506     * Removes the specified element(s).
12507     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12508     * or an array of any of those.
12509     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12510     * @return {CompositeElement} this
12511     */
12512     removeElement : function(el, removeDom){
12513         if(el instanceof Array){
12514             for(var i = 0, len = el.length; i < len; i++){
12515                 this.removeElement(el[i]);
12516             }
12517             return this;
12518         }
12519         var index = typeof el == 'number' ? el : this.indexOf(el);
12520         if(index !== -1){
12521             if(removeDom){
12522                 var d = this.elements[index];
12523                 if(d.dom){
12524                     d.remove();
12525                 }else{
12526                     d.parentNode.removeChild(d);
12527                 }
12528             }
12529             this.elements.splice(index, 1);
12530         }
12531         return this;
12532     },
12533
12534     /**
12535     * Replaces the specified element with the passed element.
12536     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12537     * to replace.
12538     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12539     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12540     * @return {CompositeElement} this
12541     */
12542     replaceElement : function(el, replacement, domReplace){
12543         var index = typeof el == 'number' ? el : this.indexOf(el);
12544         if(index !== -1){
12545             if(domReplace){
12546                 this.elements[index].replaceWith(replacement);
12547             }else{
12548                 this.elements.splice(index, 1, Roo.get(replacement))
12549             }
12550         }
12551         return this;
12552     },
12553
12554     /**
12555      * Removes all elements.
12556      */
12557     clear : function(){
12558         this.elements = [];
12559     }
12560 };
12561 (function(){
12562     Roo.CompositeElement.createCall = function(proto, fnName){
12563         if(!proto[fnName]){
12564             proto[fnName] = function(){
12565                 return this.invoke(fnName, arguments);
12566             };
12567         }
12568     };
12569     for(var fnName in Roo.Element.prototype){
12570         if(typeof Roo.Element.prototype[fnName] == "function"){
12571             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12572         }
12573     };
12574 })();
12575 /*
12576  * Based on:
12577  * Ext JS Library 1.1.1
12578  * Copyright(c) 2006-2007, Ext JS, LLC.
12579  *
12580  * Originally Released Under LGPL - original licence link has changed is not relivant.
12581  *
12582  * Fork - LGPL
12583  * <script type="text/javascript">
12584  */
12585
12586 /**
12587  * @class Roo.CompositeElementLite
12588  * @extends Roo.CompositeElement
12589  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12590  <pre><code>
12591  var els = Roo.select("#some-el div.some-class");
12592  // or select directly from an existing element
12593  var el = Roo.get('some-el');
12594  el.select('div.some-class');
12595
12596  els.setWidth(100); // all elements become 100 width
12597  els.hide(true); // all elements fade out and hide
12598  // or
12599  els.setWidth(100).hide(true);
12600  </code></pre><br><br>
12601  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12602  * actions will be performed on all the elements in this collection.</b>
12603  */
12604 Roo.CompositeElementLite = function(els){
12605     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12606     this.el = new Roo.Element.Flyweight();
12607 };
12608 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12609     addElements : function(els){
12610         if(els){
12611             if(els instanceof Array){
12612                 this.elements = this.elements.concat(els);
12613             }else{
12614                 var yels = this.elements;
12615                 var index = yels.length-1;
12616                 for(var i = 0, len = els.length; i < len; i++) {
12617                     yels[++index] = els[i];
12618                 }
12619             }
12620         }
12621         return this;
12622     },
12623     invoke : function(fn, args){
12624         var els = this.elements;
12625         var el = this.el;
12626         for(var i = 0, len = els.length; i < len; i++) {
12627             el.dom = els[i];
12628                 Roo.Element.prototype[fn].apply(el, args);
12629         }
12630         return this;
12631     },
12632     /**
12633      * Returns a flyweight Element of the dom element object at the specified index
12634      * @param {Number} index
12635      * @return {Roo.Element}
12636      */
12637     item : function(index){
12638         if(!this.elements[index]){
12639             return null;
12640         }
12641         this.el.dom = this.elements[index];
12642         return this.el;
12643     },
12644
12645     // fixes scope with flyweight
12646     addListener : function(eventName, handler, scope, opt){
12647         var els = this.elements;
12648         for(var i = 0, len = els.length; i < len; i++) {
12649             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12650         }
12651         return this;
12652     },
12653
12654     /**
12655     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12656     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12657     * a reference to the dom node, use el.dom.</b>
12658     * @param {Function} fn The function to call
12659     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12660     * @return {CompositeElement} this
12661     */
12662     each : function(fn, scope){
12663         var els = this.elements;
12664         var el = this.el;
12665         for(var i = 0, len = els.length; i < len; i++){
12666             el.dom = els[i];
12667                 if(fn.call(scope || el, el, this, i) === false){
12668                 break;
12669             }
12670         }
12671         return this;
12672     },
12673
12674     indexOf : function(el){
12675         return this.elements.indexOf(Roo.getDom(el));
12676     },
12677
12678     replaceElement : function(el, replacement, domReplace){
12679         var index = typeof el == 'number' ? el : this.indexOf(el);
12680         if(index !== -1){
12681             replacement = Roo.getDom(replacement);
12682             if(domReplace){
12683                 var d = this.elements[index];
12684                 d.parentNode.insertBefore(replacement, d);
12685                 d.parentNode.removeChild(d);
12686             }
12687             this.elements.splice(index, 1, replacement);
12688         }
12689         return this;
12690     }
12691 });
12692 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12693
12694 /*
12695  * Based on:
12696  * Ext JS Library 1.1.1
12697  * Copyright(c) 2006-2007, Ext JS, LLC.
12698  *
12699  * Originally Released Under LGPL - original licence link has changed is not relivant.
12700  *
12701  * Fork - LGPL
12702  * <script type="text/javascript">
12703  */
12704
12705  
12706
12707 /**
12708  * @class Roo.data.Connection
12709  * @extends Roo.util.Observable
12710  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12711  * either to a configured URL, or to a URL specified at request time. 
12712  * 
12713  * Requests made by this class are asynchronous, and will return immediately. No data from
12714  * the server will be available to the statement immediately following the {@link #request} call.
12715  * To process returned data, use a callback in the request options object, or an event listener.
12716  * 
12717  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12718  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12719  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12720  * property and, if present, the IFRAME's XML document as the responseXML property.
12721  * 
12722  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12723  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12724  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12725  * standard DOM methods.
12726  * @constructor
12727  * @param {Object} config a configuration object.
12728  */
12729 Roo.data.Connection = function(config){
12730     Roo.apply(this, config);
12731     this.addEvents({
12732         /**
12733          * @event beforerequest
12734          * Fires before a network request is made to retrieve a data object.
12735          * @param {Connection} conn This Connection object.
12736          * @param {Object} options The options config object passed to the {@link #request} method.
12737          */
12738         "beforerequest" : true,
12739         /**
12740          * @event requestcomplete
12741          * Fires if the request was successfully completed.
12742          * @param {Connection} conn This Connection object.
12743          * @param {Object} response The XHR object containing the response data.
12744          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12745          * @param {Object} options The options config object passed to the {@link #request} method.
12746          */
12747         "requestcomplete" : true,
12748         /**
12749          * @event requestexception
12750          * Fires if an error HTTP status was returned from the server.
12751          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12752          * @param {Connection} conn This Connection object.
12753          * @param {Object} response The XHR object containing the response data.
12754          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12755          * @param {Object} options The options config object passed to the {@link #request} method.
12756          */
12757         "requestexception" : true
12758     });
12759     Roo.data.Connection.superclass.constructor.call(this);
12760 };
12761
12762 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12763     /**
12764      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12765      */
12766     /**
12767      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12768      * extra parameters to each request made by this object. (defaults to undefined)
12769      */
12770     /**
12771      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12772      *  to each request made by this object. (defaults to undefined)
12773      */
12774     /**
12775      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12776      */
12777     /**
12778      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12779      */
12780     timeout : 30000,
12781     /**
12782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12783      * @type Boolean
12784      */
12785     autoAbort:false,
12786
12787     /**
12788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12789      * @type Boolean
12790      */
12791     disableCaching: true,
12792
12793     /**
12794      * Sends an HTTP request to a remote server.
12795      * @param {Object} options An object which may contain the following properties:<ul>
12796      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12797      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12798      * request, a url encoded string or a function to call to get either.</li>
12799      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12800      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12801      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12802      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12803      * <li>options {Object} The parameter to the request call.</li>
12804      * <li>success {Boolean} True if the request succeeded.</li>
12805      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12806      * </ul></li>
12807      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12808      * The callback is passed the following parameters:<ul>
12809      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12810      * <li>options {Object} The parameter to the request call.</li>
12811      * </ul></li>
12812      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12813      * The callback is passed the following parameters:<ul>
12814      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12815      * <li>options {Object} The parameter to the request call.</li>
12816      * </ul></li>
12817      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12818      * for the callback function. Defaults to the browser window.</li>
12819      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12820      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12821      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12822      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12823      * params for the post data. Any params will be appended to the URL.</li>
12824      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12825      * </ul>
12826      * @return {Number} transactionId
12827      */
12828     request : function(o){
12829         if(this.fireEvent("beforerequest", this, o) !== false){
12830             var p = o.params;
12831
12832             if(typeof p == "function"){
12833                 p = p.call(o.scope||window, o);
12834             }
12835             if(typeof p == "object"){
12836                 p = Roo.urlEncode(o.params);
12837             }
12838             if(this.extraParams){
12839                 var extras = Roo.urlEncode(this.extraParams);
12840                 p = p ? (p + '&' + extras) : extras;
12841             }
12842
12843             var url = o.url || this.url;
12844             if(typeof url == 'function'){
12845                 url = url.call(o.scope||window, o);
12846             }
12847
12848             if(o.form){
12849                 var form = Roo.getDom(o.form);
12850                 url = url || form.action;
12851
12852                 var enctype = form.getAttribute("enctype");
12853                 
12854                 if (o.formData) {
12855                     return this.doFormDataUpload(o, url);
12856                 }
12857                 
12858                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12859                     return this.doFormUpload(o, p, url);
12860                 }
12861                 var f = Roo.lib.Ajax.serializeForm(form);
12862                 p = p ? (p + '&' + f) : f;
12863             }
12864             
12865             if (!o.form && o.formData) {
12866                 o.formData = o.formData === true ? new FormData() : o.formData;
12867                 for (var k in o.params) {
12868                     o.formData.append(k,o.params[k]);
12869                 }
12870                     
12871                 return this.doFormDataUpload(o, url);
12872             }
12873             
12874
12875             var hs = o.headers;
12876             if(this.defaultHeaders){
12877                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12878                 if(!o.headers){
12879                     o.headers = hs;
12880                 }
12881             }
12882
12883             var cb = {
12884                 success: this.handleResponse,
12885                 failure: this.handleFailure,
12886                 scope: this,
12887                 argument: {options: o},
12888                 timeout : o.timeout || this.timeout
12889             };
12890
12891             var method = o.method||this.method||(p ? "POST" : "GET");
12892
12893             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12894                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12895             }
12896
12897             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12898                 if(o.autoAbort){
12899                     this.abort();
12900                 }
12901             }else if(this.autoAbort !== false){
12902                 this.abort();
12903             }
12904
12905             if((method == 'GET' && p) || o.xmlData){
12906                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12907                 p = '';
12908             }
12909             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12910             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12911             Roo.lib.Ajax.useDefaultHeader == true;
12912             return this.transId;
12913         }else{
12914             Roo.callback(o.callback, o.scope, [o, null, null]);
12915             return null;
12916         }
12917     },
12918
12919     /**
12920      * Determine whether this object has a request outstanding.
12921      * @param {Number} transactionId (Optional) defaults to the last transaction
12922      * @return {Boolean} True if there is an outstanding request.
12923      */
12924     isLoading : function(transId){
12925         if(transId){
12926             return Roo.lib.Ajax.isCallInProgress(transId);
12927         }else{
12928             return this.transId ? true : false;
12929         }
12930     },
12931
12932     /**
12933      * Aborts any outstanding request.
12934      * @param {Number} transactionId (Optional) defaults to the last transaction
12935      */
12936     abort : function(transId){
12937         if(transId || this.isLoading()){
12938             Roo.lib.Ajax.abort(transId || this.transId);
12939         }
12940     },
12941
12942     // private
12943     handleResponse : function(response){
12944         this.transId = false;
12945         var options = response.argument.options;
12946         response.argument = options ? options.argument : null;
12947         this.fireEvent("requestcomplete", this, response, options);
12948         Roo.callback(options.success, options.scope, [response, options]);
12949         Roo.callback(options.callback, options.scope, [options, true, response]);
12950     },
12951
12952     // private
12953     handleFailure : function(response, e){
12954         this.transId = false;
12955         var options = response.argument.options;
12956         response.argument = options ? options.argument : null;
12957         this.fireEvent("requestexception", this, response, options, e);
12958         Roo.callback(options.failure, options.scope, [response, options]);
12959         Roo.callback(options.callback, options.scope, [options, false, response]);
12960     },
12961
12962     // private
12963     doFormUpload : function(o, ps, url){
12964         var id = Roo.id();
12965         var frame = document.createElement('iframe');
12966         frame.id = id;
12967         frame.name = id;
12968         frame.className = 'x-hidden';
12969         if(Roo.isIE){
12970             frame.src = Roo.SSL_SECURE_URL;
12971         }
12972         document.body.appendChild(frame);
12973
12974         if(Roo.isIE){
12975            document.frames[id].name = id;
12976         }
12977
12978         var form = Roo.getDom(o.form);
12979         form.target = id;
12980         form.method = 'POST';
12981         form.enctype = form.encoding = 'multipart/form-data';
12982         if(url){
12983             form.action = url;
12984         }
12985
12986         var hiddens, hd;
12987         if(ps){ // add dynamic params
12988             hiddens = [];
12989             ps = Roo.urlDecode(ps, false);
12990             for(var k in ps){
12991                 if(ps.hasOwnProperty(k)){
12992                     hd = document.createElement('input');
12993                     hd.type = 'hidden';
12994                     hd.name = k;
12995                     hd.value = ps[k];
12996                     form.appendChild(hd);
12997                     hiddens.push(hd);
12998                 }
12999             }
13000         }
13001
13002         function cb(){
13003             var r = {  // bogus response object
13004                 responseText : '',
13005                 responseXML : null
13006             };
13007
13008             r.argument = o ? o.argument : null;
13009
13010             try { //
13011                 var doc;
13012                 if(Roo.isIE){
13013                     doc = frame.contentWindow.document;
13014                 }else {
13015                     doc = (frame.contentDocument || window.frames[id].document);
13016                 }
13017                 if(doc && doc.body){
13018                     r.responseText = doc.body.innerHTML;
13019                 }
13020                 if(doc && doc.XMLDocument){
13021                     r.responseXML = doc.XMLDocument;
13022                 }else {
13023                     r.responseXML = doc;
13024                 }
13025             }
13026             catch(e) {
13027                 // ignore
13028             }
13029
13030             Roo.EventManager.removeListener(frame, 'load', cb, this);
13031
13032             this.fireEvent("requestcomplete", this, r, o);
13033             Roo.callback(o.success, o.scope, [r, o]);
13034             Roo.callback(o.callback, o.scope, [o, true, r]);
13035
13036             setTimeout(function(){document.body.removeChild(frame);}, 100);
13037         }
13038
13039         Roo.EventManager.on(frame, 'load', cb, this);
13040         form.submit();
13041
13042         if(hiddens){ // remove dynamic params
13043             for(var i = 0, len = hiddens.length; i < len; i++){
13044                 form.removeChild(hiddens[i]);
13045             }
13046         }
13047     },
13048     // this is a 'formdata version???'
13049     
13050     
13051     doFormDataUpload : function(o,  url)
13052     {
13053         var formData;
13054         if (o.form) {
13055             var form =  Roo.getDom(o.form);
13056             form.enctype = form.encoding = 'multipart/form-data';
13057             formData = o.formData === true ? new FormData(form) : o.formData;
13058         } else {
13059             formData = o.formData === true ? new FormData() : o.formData;
13060         }
13061         
13062       
13063         var cb = {
13064             success: this.handleResponse,
13065             failure: this.handleFailure,
13066             scope: this,
13067             argument: {options: o},
13068             timeout : o.timeout || this.timeout
13069         };
13070  
13071         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13072             if(o.autoAbort){
13073                 this.abort();
13074             }
13075         }else if(this.autoAbort !== false){
13076             this.abort();
13077         }
13078
13079         //Roo.lib.Ajax.defaultPostHeader = null;
13080         Roo.lib.Ajax.useDefaultHeader = false;
13081         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13082         Roo.lib.Ajax.useDefaultHeader = true;
13083  
13084          
13085     }
13086     
13087 });
13088 /*
13089  * Based on:
13090  * Ext JS Library 1.1.1
13091  * Copyright(c) 2006-2007, Ext JS, LLC.
13092  *
13093  * Originally Released Under LGPL - original licence link has changed is not relivant.
13094  *
13095  * Fork - LGPL
13096  * <script type="text/javascript">
13097  */
13098  
13099 /**
13100  * Global Ajax request class.
13101  * 
13102  * @class Roo.Ajax
13103  * @extends Roo.data.Connection
13104  * @static
13105  * 
13106  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13107  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13108  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13109  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
13110  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13111  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13112  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13113  */
13114 Roo.Ajax = new Roo.data.Connection({
13115     // fix up the docs
13116     /**
13117      * @scope Roo.Ajax
13118      * @type {Boolear} 
13119      */
13120     autoAbort : false,
13121
13122     /**
13123      * Serialize the passed form into a url encoded string
13124      * @scope Roo.Ajax
13125      * @param {String/HTMLElement} form
13126      * @return {String}
13127      */
13128     serializeForm : function(form){
13129         return Roo.lib.Ajax.serializeForm(form);
13130     }
13131 });/*
13132  * Based on:
13133  * Ext JS Library 1.1.1
13134  * Copyright(c) 2006-2007, Ext JS, LLC.
13135  *
13136  * Originally Released Under LGPL - original licence link has changed is not relivant.
13137  *
13138  * Fork - LGPL
13139  * <script type="text/javascript">
13140  */
13141
13142  
13143 /**
13144  * @class Roo.UpdateManager
13145  * @extends Roo.util.Observable
13146  * Provides AJAX-style update for Element object.<br><br>
13147  * Usage:<br>
13148  * <pre><code>
13149  * // Get it from a Roo.Element object
13150  * var el = Roo.get("foo");
13151  * var mgr = el.getUpdateManager();
13152  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13153  * ...
13154  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13155  * <br>
13156  * // or directly (returns the same UpdateManager instance)
13157  * var mgr = new Roo.UpdateManager("myElementId");
13158  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13159  * mgr.on("update", myFcnNeedsToKnow);
13160  * <br>
13161    // short handed call directly from the element object
13162    Roo.get("foo").load({
13163         url: "bar.php",
13164         scripts:true,
13165         params: "for=bar",
13166         text: "Loading Foo..."
13167    });
13168  * </code></pre>
13169  * @constructor
13170  * Create new UpdateManager directly.
13171  * @param {String/HTMLElement/Roo.Element} el The element to update
13172  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
13173  */
13174 Roo.UpdateManager = function(el, forceNew){
13175     el = Roo.get(el);
13176     if(!forceNew && el.updateManager){
13177         return el.updateManager;
13178     }
13179     /**
13180      * The Element object
13181      * @type Roo.Element
13182      */
13183     this.el = el;
13184     /**
13185      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13186      * @type String
13187      */
13188     this.defaultUrl = null;
13189
13190     this.addEvents({
13191         /**
13192          * @event beforeupdate
13193          * Fired before an update is made, return false from your handler and the update is cancelled.
13194          * @param {Roo.Element} el
13195          * @param {String/Object/Function} url
13196          * @param {String/Object} params
13197          */
13198         "beforeupdate": true,
13199         /**
13200          * @event update
13201          * Fired after successful update is made.
13202          * @param {Roo.Element} el
13203          * @param {Object} oResponseObject The response Object
13204          */
13205         "update": true,
13206         /**
13207          * @event failure
13208          * Fired on update failure.
13209          * @param {Roo.Element} el
13210          * @param {Object} oResponseObject The response Object
13211          */
13212         "failure": true
13213     });
13214     var d = Roo.UpdateManager.defaults;
13215     /**
13216      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13217      * @type String
13218      */
13219     this.sslBlankUrl = d.sslBlankUrl;
13220     /**
13221      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13222      * @type Boolean
13223      */
13224     this.disableCaching = d.disableCaching;
13225     /**
13226      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13227      * @type String
13228      */
13229     this.indicatorText = d.indicatorText;
13230     /**
13231      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13232      * @type String
13233      */
13234     this.showLoadIndicator = d.showLoadIndicator;
13235     /**
13236      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13237      * @type Number
13238      */
13239     this.timeout = d.timeout;
13240
13241     /**
13242      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13243      * @type Boolean
13244      */
13245     this.loadScripts = d.loadScripts;
13246
13247     /**
13248      * Transaction object of current executing transaction
13249      */
13250     this.transaction = null;
13251
13252     /**
13253      * @private
13254      */
13255     this.autoRefreshProcId = null;
13256     /**
13257      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13258      * @type Function
13259      */
13260     this.refreshDelegate = this.refresh.createDelegate(this);
13261     /**
13262      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13263      * @type Function
13264      */
13265     this.updateDelegate = this.update.createDelegate(this);
13266     /**
13267      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13268      * @type Function
13269      */
13270     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13271     /**
13272      * @private
13273      */
13274     this.successDelegate = this.processSuccess.createDelegate(this);
13275     /**
13276      * @private
13277      */
13278     this.failureDelegate = this.processFailure.createDelegate(this);
13279
13280     if(!this.renderer){
13281      /**
13282       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13283       */
13284     this.renderer = new Roo.UpdateManager.BasicRenderer();
13285     }
13286     
13287     Roo.UpdateManager.superclass.constructor.call(this);
13288 };
13289
13290 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13291     /**
13292      * Get the Element this UpdateManager is bound to
13293      * @return {Roo.Element} The element
13294      */
13295     getEl : function(){
13296         return this.el;
13297     },
13298     /**
13299      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13300      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
13301 <pre><code>
13302 um.update({<br/>
13303     url: "your-url.php",<br/>
13304     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13305     callback: yourFunction,<br/>
13306     scope: yourObject, //(optional scope)  <br/>
13307     discardUrl: false, <br/>
13308     nocache: false,<br/>
13309     text: "Loading...",<br/>
13310     timeout: 30,<br/>
13311     scripts: false<br/>
13312 });
13313 </code></pre>
13314      * The only required property is url. The optional properties nocache, text and scripts
13315      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13316      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
13317      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13318      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13319      */
13320     update : function(url, params, callback, discardUrl){
13321         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13322             var method = this.method,
13323                 cfg;
13324             if(typeof url == "object"){ // must be config object
13325                 cfg = url;
13326                 url = cfg.url;
13327                 params = params || cfg.params;
13328                 callback = callback || cfg.callback;
13329                 discardUrl = discardUrl || cfg.discardUrl;
13330                 if(callback && cfg.scope){
13331                     callback = callback.createDelegate(cfg.scope);
13332                 }
13333                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13334                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13335                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13336                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13337                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13338             }
13339             this.showLoading();
13340             if(!discardUrl){
13341                 this.defaultUrl = url;
13342             }
13343             if(typeof url == "function"){
13344                 url = url.call(this);
13345             }
13346
13347             method = method || (params ? "POST" : "GET");
13348             if(method == "GET"){
13349                 url = this.prepareUrl(url);
13350             }
13351
13352             var o = Roo.apply(cfg ||{}, {
13353                 url : url,
13354                 params: params,
13355                 success: this.successDelegate,
13356                 failure: this.failureDelegate,
13357                 callback: undefined,
13358                 timeout: (this.timeout*1000),
13359                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13360             });
13361             Roo.log("updated manager called with timeout of " + o.timeout);
13362             this.transaction = Roo.Ajax.request(o);
13363         }
13364     },
13365
13366     /**
13367      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13368      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13369      * @param {String/HTMLElement} form The form Id or form element
13370      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13371      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13372      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13373      */
13374     formUpdate : function(form, url, reset, callback){
13375         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13376             if(typeof url == "function"){
13377                 url = url.call(this);
13378             }
13379             form = Roo.getDom(form);
13380             this.transaction = Roo.Ajax.request({
13381                 form: form,
13382                 url:url,
13383                 success: this.successDelegate,
13384                 failure: this.failureDelegate,
13385                 timeout: (this.timeout*1000),
13386                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13387             });
13388             this.showLoading.defer(1, this);
13389         }
13390     },
13391
13392     /**
13393      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13394      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13395      */
13396     refresh : function(callback){
13397         if(this.defaultUrl == null){
13398             return;
13399         }
13400         this.update(this.defaultUrl, null, callback, true);
13401     },
13402
13403     /**
13404      * Set this element to auto refresh.
13405      * @param {Number} interval How often to update (in seconds).
13406      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13407      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
13408      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13409      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13410      */
13411     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13412         if(refreshNow){
13413             this.update(url || this.defaultUrl, params, callback, true);
13414         }
13415         if(this.autoRefreshProcId){
13416             clearInterval(this.autoRefreshProcId);
13417         }
13418         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13419     },
13420
13421     /**
13422      * Stop auto refresh on this element.
13423      */
13424      stopAutoRefresh : function(){
13425         if(this.autoRefreshProcId){
13426             clearInterval(this.autoRefreshProcId);
13427             delete this.autoRefreshProcId;
13428         }
13429     },
13430
13431     isAutoRefreshing : function(){
13432        return this.autoRefreshProcId ? true : false;
13433     },
13434     /**
13435      * Called to update the element to "Loading" state. Override to perform custom action.
13436      */
13437     showLoading : function(){
13438         if(this.showLoadIndicator){
13439             this.el.update(this.indicatorText);
13440         }
13441     },
13442
13443     /**
13444      * Adds unique parameter to query string if disableCaching = true
13445      * @private
13446      */
13447     prepareUrl : function(url){
13448         if(this.disableCaching){
13449             var append = "_dc=" + (new Date().getTime());
13450             if(url.indexOf("?") !== -1){
13451                 url += "&" + append;
13452             }else{
13453                 url += "?" + append;
13454             }
13455         }
13456         return url;
13457     },
13458
13459     /**
13460      * @private
13461      */
13462     processSuccess : function(response){
13463         this.transaction = null;
13464         if(response.argument.form && response.argument.reset){
13465             try{ // put in try/catch since some older FF releases had problems with this
13466                 response.argument.form.reset();
13467             }catch(e){}
13468         }
13469         if(this.loadScripts){
13470             this.renderer.render(this.el, response, this,
13471                 this.updateComplete.createDelegate(this, [response]));
13472         }else{
13473             this.renderer.render(this.el, response, this);
13474             this.updateComplete(response);
13475         }
13476     },
13477
13478     updateComplete : function(response){
13479         this.fireEvent("update", this.el, response);
13480         if(typeof response.argument.callback == "function"){
13481             response.argument.callback(this.el, true, response);
13482         }
13483     },
13484
13485     /**
13486      * @private
13487      */
13488     processFailure : function(response){
13489         this.transaction = null;
13490         this.fireEvent("failure", this.el, response);
13491         if(typeof response.argument.callback == "function"){
13492             response.argument.callback(this.el, false, response);
13493         }
13494     },
13495
13496     /**
13497      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13498      * @param {Object} renderer The object implementing the render() method
13499      */
13500     setRenderer : function(renderer){
13501         this.renderer = renderer;
13502     },
13503
13504     getRenderer : function(){
13505        return this.renderer;
13506     },
13507
13508     /**
13509      * Set the defaultUrl used for updates
13510      * @param {String/Function} defaultUrl The url or a function to call to get the url
13511      */
13512     setDefaultUrl : function(defaultUrl){
13513         this.defaultUrl = defaultUrl;
13514     },
13515
13516     /**
13517      * Aborts the executing transaction
13518      */
13519     abort : function(){
13520         if(this.transaction){
13521             Roo.Ajax.abort(this.transaction);
13522         }
13523     },
13524
13525     /**
13526      * Returns true if an update is in progress
13527      * @return {Boolean}
13528      */
13529     isUpdating : function(){
13530         if(this.transaction){
13531             return Roo.Ajax.isLoading(this.transaction);
13532         }
13533         return false;
13534     }
13535 });
13536
13537 /**
13538  * @class Roo.UpdateManager.defaults
13539  * @static (not really - but it helps the doc tool)
13540  * The defaults collection enables customizing the default properties of UpdateManager
13541  */
13542    Roo.UpdateManager.defaults = {
13543        /**
13544          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13545          * @type Number
13546          */
13547          timeout : 30,
13548
13549          /**
13550          * True to process scripts by default (Defaults to false).
13551          * @type Boolean
13552          */
13553         loadScripts : false,
13554
13555         /**
13556         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13557         * @type String
13558         */
13559         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13560         /**
13561          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13562          * @type Boolean
13563          */
13564         disableCaching : false,
13565         /**
13566          * Whether to show indicatorText when loading (Defaults to true).
13567          * @type Boolean
13568          */
13569         showLoadIndicator : true,
13570         /**
13571          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13572          * @type String
13573          */
13574         indicatorText : '<div class="loading-indicator">Loading...</div>'
13575    };
13576
13577 /**
13578  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13579  *Usage:
13580  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13581  * @param {String/HTMLElement/Roo.Element} el The element to update
13582  * @param {String} url The url
13583  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13584  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13585  * @static
13586  * @deprecated
13587  * @member Roo.UpdateManager
13588  */
13589 Roo.UpdateManager.updateElement = function(el, url, params, options){
13590     var um = Roo.get(el, true).getUpdateManager();
13591     Roo.apply(um, options);
13592     um.update(url, params, options ? options.callback : null);
13593 };
13594 // alias for backwards compat
13595 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13596 /**
13597  * @class Roo.UpdateManager.BasicRenderer
13598  * Default Content renderer. Updates the elements innerHTML with the responseText.
13599  */
13600 Roo.UpdateManager.BasicRenderer = function(){};
13601
13602 Roo.UpdateManager.BasicRenderer.prototype = {
13603     /**
13604      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13605      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13606      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13607      * @param {Roo.Element} el The element being rendered
13608      * @param {Object} response The YUI Connect response object
13609      * @param {UpdateManager} updateManager The calling update manager
13610      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13611      */
13612      render : function(el, response, updateManager, callback){
13613         el.update(response.responseText, updateManager.loadScripts, callback);
13614     }
13615 };
13616 /*
13617  * Based on:
13618  * Roo JS
13619  * (c)) Alan Knowles
13620  * Licence : LGPL
13621  */
13622
13623
13624 /**
13625  * @class Roo.DomTemplate
13626  * @extends Roo.Template
13627  * An effort at a dom based template engine..
13628  *
13629  * Similar to XTemplate, except it uses dom parsing to create the template..
13630  *
13631  * Supported features:
13632  *
13633  *  Tags:
13634
13635 <pre><code>
13636       {a_variable} - output encoded.
13637       {a_variable.format:("Y-m-d")} - call a method on the variable
13638       {a_variable:raw} - unencoded output
13639       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13640       {a_variable:this.method_on_template(...)} - call a method on the template object.
13641  
13642 </code></pre>
13643  *  The tpl tag:
13644 <pre><code>
13645         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13646         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13647         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13648         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13649   
13650 </code></pre>
13651  *      
13652  */
13653 Roo.DomTemplate = function()
13654 {
13655      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13656      if (this.html) {
13657         this.compile();
13658      }
13659 };
13660
13661
13662 Roo.extend(Roo.DomTemplate, Roo.Template, {
13663     /**
13664      * id counter for sub templates.
13665      */
13666     id : 0,
13667     /**
13668      * flag to indicate if dom parser is inside a pre,
13669      * it will strip whitespace if not.
13670      */
13671     inPre : false,
13672     
13673     /**
13674      * The various sub templates
13675      */
13676     tpls : false,
13677     
13678     
13679     
13680     /**
13681      *
13682      * basic tag replacing syntax
13683      * WORD:WORD()
13684      *
13685      * // you can fake an object call by doing this
13686      *  x.t:(test,tesT) 
13687      * 
13688      */
13689     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13690     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13691     
13692     iterChild : function (node, method) {
13693         
13694         var oldPre = this.inPre;
13695         if (node.tagName == 'PRE') {
13696             this.inPre = true;
13697         }
13698         for( var i = 0; i < node.childNodes.length; i++) {
13699             method.call(this, node.childNodes[i]);
13700         }
13701         this.inPre = oldPre;
13702     },
13703     
13704     
13705     
13706     /**
13707      * compile the template
13708      *
13709      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13710      *
13711      */
13712     compile: function()
13713     {
13714         var s = this.html;
13715         
13716         // covert the html into DOM...
13717         var doc = false;
13718         var div =false;
13719         try {
13720             doc = document.implementation.createHTMLDocument("");
13721             doc.documentElement.innerHTML =   this.html  ;
13722             div = doc.documentElement;
13723         } catch (e) {
13724             // old IE... - nasty -- it causes all sorts of issues.. with
13725             // images getting pulled from server..
13726             div = document.createElement('div');
13727             div.innerHTML = this.html;
13728         }
13729         //doc.documentElement.innerHTML = htmlBody
13730          
13731         
13732         
13733         this.tpls = [];
13734         var _t = this;
13735         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13736         
13737         var tpls = this.tpls;
13738         
13739         // create a top level template from the snippet..
13740         
13741         //Roo.log(div.innerHTML);
13742         
13743         var tpl = {
13744             uid : 'master',
13745             id : this.id++,
13746             attr : false,
13747             value : false,
13748             body : div.innerHTML,
13749             
13750             forCall : false,
13751             execCall : false,
13752             dom : div,
13753             isTop : true
13754             
13755         };
13756         tpls.unshift(tpl);
13757         
13758         
13759         // compile them...
13760         this.tpls = [];
13761         Roo.each(tpls, function(tp){
13762             this.compileTpl(tp);
13763             this.tpls[tp.id] = tp;
13764         }, this);
13765         
13766         this.master = tpls[0];
13767         return this;
13768         
13769         
13770     },
13771     
13772     compileNode : function(node, istop) {
13773         // test for
13774         //Roo.log(node);
13775         
13776         
13777         // skip anything not a tag..
13778         if (node.nodeType != 1) {
13779             if (node.nodeType == 3 && !this.inPre) {
13780                 // reduce white space..
13781                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13782                 
13783             }
13784             return;
13785         }
13786         
13787         var tpl = {
13788             uid : false,
13789             id : false,
13790             attr : false,
13791             value : false,
13792             body : '',
13793             
13794             forCall : false,
13795             execCall : false,
13796             dom : false,
13797             isTop : istop
13798             
13799             
13800         };
13801         
13802         
13803         switch(true) {
13804             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13805             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13806             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13807             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13808             // no default..
13809         }
13810         
13811         
13812         if (!tpl.attr) {
13813             // just itterate children..
13814             this.iterChild(node,this.compileNode);
13815             return;
13816         }
13817         tpl.uid = this.id++;
13818         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13819         node.removeAttribute('roo-'+ tpl.attr);
13820         if (tpl.attr != 'name') {
13821             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13822             node.parentNode.replaceChild(placeholder,  node);
13823         } else {
13824             
13825             var placeholder =  document.createElement('span');
13826             placeholder.className = 'roo-tpl-' + tpl.value;
13827             node.parentNode.replaceChild(placeholder,  node);
13828         }
13829         
13830         // parent now sees '{domtplXXXX}
13831         this.iterChild(node,this.compileNode);
13832         
13833         // we should now have node body...
13834         var div = document.createElement('div');
13835         div.appendChild(node);
13836         tpl.dom = node;
13837         // this has the unfortunate side effect of converting tagged attributes
13838         // eg. href="{...}" into %7C...%7D
13839         // this has been fixed by searching for those combo's although it's a bit hacky..
13840         
13841         
13842         tpl.body = div.innerHTML;
13843         
13844         
13845          
13846         tpl.id = tpl.uid;
13847         switch(tpl.attr) {
13848             case 'for' :
13849                 switch (tpl.value) {
13850                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13851                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13852                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13853                 }
13854                 break;
13855             
13856             case 'exec':
13857                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13858                 break;
13859             
13860             case 'if':     
13861                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13862                 break;
13863             
13864             case 'name':
13865                 tpl.id  = tpl.value; // replace non characters???
13866                 break;
13867             
13868         }
13869         
13870         
13871         this.tpls.push(tpl);
13872         
13873         
13874         
13875     },
13876     
13877     
13878     
13879     
13880     /**
13881      * Compile a segment of the template into a 'sub-template'
13882      *
13883      * 
13884      * 
13885      *
13886      */
13887     compileTpl : function(tpl)
13888     {
13889         var fm = Roo.util.Format;
13890         var useF = this.disableFormats !== true;
13891         
13892         var sep = Roo.isGecko ? "+\n" : ",\n";
13893         
13894         var undef = function(str) {
13895             Roo.debug && Roo.log("Property not found :"  + str);
13896             return '';
13897         };
13898           
13899         //Roo.log(tpl.body);
13900         
13901         
13902         
13903         var fn = function(m, lbrace, name, format, args)
13904         {
13905             //Roo.log("ARGS");
13906             //Roo.log(arguments);
13907             args = args ? args.replace(/\\'/g,"'") : args;
13908             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13909             if (typeof(format) == 'undefined') {
13910                 format =  'htmlEncode'; 
13911             }
13912             if (format == 'raw' ) {
13913                 format = false;
13914             }
13915             
13916             if(name.substr(0, 6) == 'domtpl'){
13917                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13918             }
13919             
13920             // build an array of options to determine if value is undefined..
13921             
13922             // basically get 'xxxx.yyyy' then do
13923             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13924             //    (function () { Roo.log("Property not found"); return ''; })() :
13925             //    ......
13926             
13927             var udef_ar = [];
13928             var lookfor = '';
13929             Roo.each(name.split('.'), function(st) {
13930                 lookfor += (lookfor.length ? '.': '') + st;
13931                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13932             });
13933             
13934             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13935             
13936             
13937             if(format && useF){
13938                 
13939                 args = args ? ',' + args : "";
13940                  
13941                 if(format.substr(0, 5) != "this."){
13942                     format = "fm." + format + '(';
13943                 }else{
13944                     format = 'this.call("'+ format.substr(5) + '", ';
13945                     args = ", values";
13946                 }
13947                 
13948                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13949             }
13950              
13951             if (args && args.length) {
13952                 // called with xxyx.yuu:(test,test)
13953                 // change to ()
13954                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13955             }
13956             // raw.. - :raw modifier..
13957             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13958             
13959         };
13960         var body;
13961         // branched to use + in gecko and [].join() in others
13962         if(Roo.isGecko){
13963             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13964                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13965                     "';};};";
13966         }else{
13967             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13968             body.push(tpl.body.replace(/(\r\n|\n)/g,
13969                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13970             body.push("'].join('');};};");
13971             body = body.join('');
13972         }
13973         
13974         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13975        
13976         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13977         eval(body);
13978         
13979         return this;
13980     },
13981      
13982     /**
13983      * same as applyTemplate, except it's done to one of the subTemplates
13984      * when using named templates, you can do:
13985      *
13986      * var str = pl.applySubTemplate('your-name', values);
13987      *
13988      * 
13989      * @param {Number} id of the template
13990      * @param {Object} values to apply to template
13991      * @param {Object} parent (normaly the instance of this object)
13992      */
13993     applySubTemplate : function(id, values, parent)
13994     {
13995         
13996         
13997         var t = this.tpls[id];
13998         
13999         
14000         try { 
14001             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14002                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14003                 return '';
14004             }
14005         } catch(e) {
14006             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14007             Roo.log(values);
14008           
14009             return '';
14010         }
14011         try { 
14012             
14013             if(t.execCall && t.execCall.call(this, values, parent)){
14014                 return '';
14015             }
14016         } catch(e) {
14017             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14018             Roo.log(values);
14019             return '';
14020         }
14021         
14022         try {
14023             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14024             parent = t.target ? values : parent;
14025             if(t.forCall && vs instanceof Array){
14026                 var buf = [];
14027                 for(var i = 0, len = vs.length; i < len; i++){
14028                     try {
14029                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14030                     } catch (e) {
14031                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14032                         Roo.log(e.body);
14033                         //Roo.log(t.compiled);
14034                         Roo.log(vs[i]);
14035                     }   
14036                 }
14037                 return buf.join('');
14038             }
14039         } catch (e) {
14040             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14041             Roo.log(values);
14042             return '';
14043         }
14044         try {
14045             return t.compiled.call(this, vs, parent);
14046         } catch (e) {
14047             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14048             Roo.log(e.body);
14049             //Roo.log(t.compiled);
14050             Roo.log(values);
14051             return '';
14052         }
14053     },
14054
14055    
14056
14057     applyTemplate : function(values){
14058         return this.master.compiled.call(this, values, {});
14059         //var s = this.subs;
14060     },
14061
14062     apply : function(){
14063         return this.applyTemplate.apply(this, arguments);
14064     }
14065
14066  });
14067
14068 Roo.DomTemplate.from = function(el){
14069     el = Roo.getDom(el);
14070     return new Roo.Domtemplate(el.value || el.innerHTML);
14071 };/*
14072  * Based on:
14073  * Ext JS Library 1.1.1
14074  * Copyright(c) 2006-2007, Ext JS, LLC.
14075  *
14076  * Originally Released Under LGPL - original licence link has changed is not relivant.
14077  *
14078  * Fork - LGPL
14079  * <script type="text/javascript">
14080  */
14081
14082 /**
14083  * @class Roo.util.DelayedTask
14084  * Provides a convenient method of performing setTimeout where a new
14085  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14086  * You can use this class to buffer
14087  * the keypress events for a certain number of milliseconds, and perform only if they stop
14088  * for that amount of time.
14089  * @constructor The parameters to this constructor serve as defaults and are not required.
14090  * @param {Function} fn (optional) The default function to timeout
14091  * @param {Object} scope (optional) The default scope of that timeout
14092  * @param {Array} args (optional) The default Array of arguments
14093  */
14094 Roo.util.DelayedTask = function(fn, scope, args){
14095     var id = null, d, t;
14096
14097     var call = function(){
14098         var now = new Date().getTime();
14099         if(now - t >= d){
14100             clearInterval(id);
14101             id = null;
14102             fn.apply(scope, args || []);
14103         }
14104     };
14105     /**
14106      * Cancels any pending timeout and queues a new one
14107      * @param {Number} delay The milliseconds to delay
14108      * @param {Function} newFn (optional) Overrides function passed to constructor
14109      * @param {Object} newScope (optional) Overrides scope passed to constructor
14110      * @param {Array} newArgs (optional) Overrides args passed to constructor
14111      */
14112     this.delay = function(delay, newFn, newScope, newArgs){
14113         if(id && delay != d){
14114             this.cancel();
14115         }
14116         d = delay;
14117         t = new Date().getTime();
14118         fn = newFn || fn;
14119         scope = newScope || scope;
14120         args = newArgs || args;
14121         if(!id){
14122             id = setInterval(call, d);
14123         }
14124     };
14125
14126     /**
14127      * Cancel the last queued timeout
14128      */
14129     this.cancel = function(){
14130         if(id){
14131             clearInterval(id);
14132             id = null;
14133         }
14134     };
14135 };/*
14136  * Based on:
14137  * Ext JS Library 1.1.1
14138  * Copyright(c) 2006-2007, Ext JS, LLC.
14139  *
14140  * Originally Released Under LGPL - original licence link has changed is not relivant.
14141  *
14142  * Fork - LGPL
14143  * <script type="text/javascript">
14144  */
14145 /**
14146  * @class Roo.util.TaskRunner
14147  * Manage background tasks - not sure why this is better that setInterval?
14148  * @static
14149  *
14150  */
14151  
14152 Roo.util.TaskRunner = function(interval){
14153     interval = interval || 10;
14154     var tasks = [], removeQueue = [];
14155     var id = 0;
14156     var running = false;
14157
14158     var stopThread = function(){
14159         running = false;
14160         clearInterval(id);
14161         id = 0;
14162     };
14163
14164     var startThread = function(){
14165         if(!running){
14166             running = true;
14167             id = setInterval(runTasks, interval);
14168         }
14169     };
14170
14171     var removeTask = function(task){
14172         removeQueue.push(task);
14173         if(task.onStop){
14174             task.onStop();
14175         }
14176     };
14177
14178     var runTasks = function(){
14179         if(removeQueue.length > 0){
14180             for(var i = 0, len = removeQueue.length; i < len; i++){
14181                 tasks.remove(removeQueue[i]);
14182             }
14183             removeQueue = [];
14184             if(tasks.length < 1){
14185                 stopThread();
14186                 return;
14187             }
14188         }
14189         var now = new Date().getTime();
14190         for(var i = 0, len = tasks.length; i < len; ++i){
14191             var t = tasks[i];
14192             var itime = now - t.taskRunTime;
14193             if(t.interval <= itime){
14194                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14195                 t.taskRunTime = now;
14196                 if(rt === false || t.taskRunCount === t.repeat){
14197                     removeTask(t);
14198                     return;
14199                 }
14200             }
14201             if(t.duration && t.duration <= (now - t.taskStartTime)){
14202                 removeTask(t);
14203             }
14204         }
14205     };
14206
14207     /**
14208      * Queues a new task.
14209      * @param {Object} task
14210      *
14211      * Task property : interval = how frequent to run.
14212      * Task object should implement
14213      * function run()
14214      * Task object may implement
14215      * function onStop()
14216      */
14217     this.start = function(task){
14218         tasks.push(task);
14219         task.taskStartTime = new Date().getTime();
14220         task.taskRunTime = 0;
14221         task.taskRunCount = 0;
14222         startThread();
14223         return task;
14224     };
14225     /**
14226      * Stop  new task.
14227      * @param {Object} task
14228      */
14229     this.stop = function(task){
14230         removeTask(task);
14231         return task;
14232     };
14233     /**
14234      * Stop all Tasks
14235      */
14236     this.stopAll = function(){
14237         stopThread();
14238         for(var i = 0, len = tasks.length; i < len; i++){
14239             if(tasks[i].onStop){
14240                 tasks[i].onStop();
14241             }
14242         }
14243         tasks = [];
14244         removeQueue = [];
14245     };
14246 };
14247
14248 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14249  * Based on:
14250  * Ext JS Library 1.1.1
14251  * Copyright(c) 2006-2007, Ext JS, LLC.
14252  *
14253  * Originally Released Under LGPL - original licence link has changed is not relivant.
14254  *
14255  * Fork - LGPL
14256  * <script type="text/javascript">
14257  */
14258
14259  
14260 /**
14261  * @class Roo.util.MixedCollection
14262  * @extends Roo.util.Observable
14263  * A Collection class that maintains both numeric indexes and keys and exposes events.
14264  * @constructor
14265  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14266  * collection (defaults to false)
14267  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14268  * and return the key value for that item.  This is used when available to look up the key on items that
14269  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14270  * equivalent to providing an implementation for the {@link #getKey} method.
14271  */
14272 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14273     this.items = [];
14274     this.map = {};
14275     this.keys = [];
14276     this.length = 0;
14277     this.addEvents({
14278         /**
14279          * @event clear
14280          * Fires when the collection is cleared.
14281          */
14282         "clear" : true,
14283         /**
14284          * @event add
14285          * Fires when an item is added to the collection.
14286          * @param {Number} index The index at which the item was added.
14287          * @param {Object} o The item added.
14288          * @param {String} key The key associated with the added item.
14289          */
14290         "add" : true,
14291         /**
14292          * @event replace
14293          * Fires when an item is replaced in the collection.
14294          * @param {String} key he key associated with the new added.
14295          * @param {Object} old The item being replaced.
14296          * @param {Object} new The new item.
14297          */
14298         "replace" : true,
14299         /**
14300          * @event remove
14301          * Fires when an item is removed from the collection.
14302          * @param {Object} o The item being removed.
14303          * @param {String} key (optional) The key associated with the removed item.
14304          */
14305         "remove" : true,
14306         "sort" : true
14307     });
14308     this.allowFunctions = allowFunctions === true;
14309     if(keyFn){
14310         this.getKey = keyFn;
14311     }
14312     Roo.util.MixedCollection.superclass.constructor.call(this);
14313 };
14314
14315 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14316     allowFunctions : false,
14317     
14318 /**
14319  * Adds an item to the collection.
14320  * @param {String} key The key to associate with the item
14321  * @param {Object} o The item to add.
14322  * @return {Object} The item added.
14323  */
14324     add : function(key, o){
14325         if(arguments.length == 1){
14326             o = arguments[0];
14327             key = this.getKey(o);
14328         }
14329         if(typeof key == "undefined" || key === null){
14330             this.length++;
14331             this.items.push(o);
14332             this.keys.push(null);
14333         }else{
14334             var old = this.map[key];
14335             if(old){
14336                 return this.replace(key, o);
14337             }
14338             this.length++;
14339             this.items.push(o);
14340             this.map[key] = o;
14341             this.keys.push(key);
14342         }
14343         this.fireEvent("add", this.length-1, o, key);
14344         return o;
14345     },
14346        
14347 /**
14348   * MixedCollection has a generic way to fetch keys if you implement getKey.
14349 <pre><code>
14350 // normal way
14351 var mc = new Roo.util.MixedCollection();
14352 mc.add(someEl.dom.id, someEl);
14353 mc.add(otherEl.dom.id, otherEl);
14354 //and so on
14355
14356 // using getKey
14357 var mc = new Roo.util.MixedCollection();
14358 mc.getKey = function(el){
14359    return el.dom.id;
14360 };
14361 mc.add(someEl);
14362 mc.add(otherEl);
14363
14364 // or via the constructor
14365 var mc = new Roo.util.MixedCollection(false, function(el){
14366    return el.dom.id;
14367 });
14368 mc.add(someEl);
14369 mc.add(otherEl);
14370 </code></pre>
14371  * @param o {Object} The item for which to find the key.
14372  * @return {Object} The key for the passed item.
14373  */
14374     getKey : function(o){
14375          return o.id; 
14376     },
14377    
14378 /**
14379  * Replaces an item in the collection.
14380  * @param {String} key The key associated with the item to replace, or the item to replace.
14381  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14382  * @return {Object}  The new item.
14383  */
14384     replace : function(key, o){
14385         if(arguments.length == 1){
14386             o = arguments[0];
14387             key = this.getKey(o);
14388         }
14389         var old = this.item(key);
14390         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14391              return this.add(key, o);
14392         }
14393         var index = this.indexOfKey(key);
14394         this.items[index] = o;
14395         this.map[key] = o;
14396         this.fireEvent("replace", key, old, o);
14397         return o;
14398     },
14399    
14400 /**
14401  * Adds all elements of an Array or an Object to the collection.
14402  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14403  * an Array of values, each of which are added to the collection.
14404  */
14405     addAll : function(objs){
14406         if(arguments.length > 1 || objs instanceof Array){
14407             var args = arguments.length > 1 ? arguments : objs;
14408             for(var i = 0, len = args.length; i < len; i++){
14409                 this.add(args[i]);
14410             }
14411         }else{
14412             for(var key in objs){
14413                 if(this.allowFunctions || typeof objs[key] != "function"){
14414                     this.add(key, objs[key]);
14415                 }
14416             }
14417         }
14418     },
14419    
14420 /**
14421  * Executes the specified function once for every item in the collection, passing each
14422  * item as the first and only parameter. returning false from the function will stop the iteration.
14423  * @param {Function} fn The function to execute for each item.
14424  * @param {Object} scope (optional) The scope in which to execute the function.
14425  */
14426     each : function(fn, scope){
14427         var items = [].concat(this.items); // each safe for removal
14428         for(var i = 0, len = items.length; i < len; i++){
14429             if(fn.call(scope || items[i], items[i], i, len) === false){
14430                 break;
14431             }
14432         }
14433     },
14434    
14435 /**
14436  * Executes the specified function once for every key in the collection, passing each
14437  * key, and its associated item as the first two parameters.
14438  * @param {Function} fn The function to execute for each item.
14439  * @param {Object} scope (optional) The scope in which to execute the function.
14440  */
14441     eachKey : function(fn, scope){
14442         for(var i = 0, len = this.keys.length; i < len; i++){
14443             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14444         }
14445     },
14446    
14447 /**
14448  * Returns the first item in the collection which elicits a true return value from the
14449  * passed selection function.
14450  * @param {Function} fn The selection function to execute for each item.
14451  * @param {Object} scope (optional) The scope in which to execute the function.
14452  * @return {Object} The first item in the collection which returned true from the selection function.
14453  */
14454     find : function(fn, scope){
14455         for(var i = 0, len = this.items.length; i < len; i++){
14456             if(fn.call(scope || window, this.items[i], this.keys[i])){
14457                 return this.items[i];
14458             }
14459         }
14460         return null;
14461     },
14462    
14463 /**
14464  * Inserts an item at the specified index in the collection.
14465  * @param {Number} index The index to insert the item at.
14466  * @param {String} key The key to associate with the new item, or the item itself.
14467  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14468  * @return {Object} The item inserted.
14469  */
14470     insert : function(index, key, o){
14471         if(arguments.length == 2){
14472             o = arguments[1];
14473             key = this.getKey(o);
14474         }
14475         if(index >= this.length){
14476             return this.add(key, o);
14477         }
14478         this.length++;
14479         this.items.splice(index, 0, o);
14480         if(typeof key != "undefined" && key != null){
14481             this.map[key] = o;
14482         }
14483         this.keys.splice(index, 0, key);
14484         this.fireEvent("add", index, o, key);
14485         return o;
14486     },
14487    
14488 /**
14489  * Removed an item from the collection.
14490  * @param {Object} o The item to remove.
14491  * @return {Object} The item removed.
14492  */
14493     remove : function(o){
14494         return this.removeAt(this.indexOf(o));
14495     },
14496    
14497 /**
14498  * Remove an item from a specified index in the collection.
14499  * @param {Number} index The index within the collection of the item to remove.
14500  */
14501     removeAt : function(index){
14502         if(index < this.length && index >= 0){
14503             this.length--;
14504             var o = this.items[index];
14505             this.items.splice(index, 1);
14506             var key = this.keys[index];
14507             if(typeof key != "undefined"){
14508                 delete this.map[key];
14509             }
14510             this.keys.splice(index, 1);
14511             this.fireEvent("remove", o, key);
14512         }
14513     },
14514    
14515 /**
14516  * Removed an item associated with the passed key fom the collection.
14517  * @param {String} key The key of the item to remove.
14518  */
14519     removeKey : function(key){
14520         return this.removeAt(this.indexOfKey(key));
14521     },
14522    
14523 /**
14524  * Returns the number of items in the collection.
14525  * @return {Number} the number of items in the collection.
14526  */
14527     getCount : function(){
14528         return this.length; 
14529     },
14530    
14531 /**
14532  * Returns index within the collection of the passed Object.
14533  * @param {Object} o The item to find the index of.
14534  * @return {Number} index of the item.
14535  */
14536     indexOf : function(o){
14537         if(!this.items.indexOf){
14538             for(var i = 0, len = this.items.length; i < len; i++){
14539                 if(this.items[i] == o) {
14540                     return i;
14541                 }
14542             }
14543             return -1;
14544         }else{
14545             return this.items.indexOf(o);
14546         }
14547     },
14548    
14549 /**
14550  * Returns index within the collection of the passed key.
14551  * @param {String} key The key to find the index of.
14552  * @return {Number} index of the key.
14553  */
14554     indexOfKey : function(key){
14555         if(!this.keys.indexOf){
14556             for(var i = 0, len = this.keys.length; i < len; i++){
14557                 if(this.keys[i] == key) {
14558                     return i;
14559                 }
14560             }
14561             return -1;
14562         }else{
14563             return this.keys.indexOf(key);
14564         }
14565     },
14566    
14567 /**
14568  * Returns the item associated with the passed key OR index. Key has priority over index.
14569  * @param {String/Number} key The key or index of the item.
14570  * @return {Object} The item associated with the passed key.
14571  */
14572     item : function(key){
14573         if (key === 'length') {
14574             return null;
14575         }
14576         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14577         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14578     },
14579     
14580 /**
14581  * Returns the item at the specified index.
14582  * @param {Number} index The index of the item.
14583  * @return {Object}
14584  */
14585     itemAt : function(index){
14586         return this.items[index];
14587     },
14588     
14589 /**
14590  * Returns the item associated with the passed key.
14591  * @param {String/Number} key The key of the item.
14592  * @return {Object} The item associated with the passed key.
14593  */
14594     key : function(key){
14595         return this.map[key];
14596     },
14597    
14598 /**
14599  * Returns true if the collection contains the passed Object as an item.
14600  * @param {Object} o  The Object to look for in the collection.
14601  * @return {Boolean} True if the collection contains the Object as an item.
14602  */
14603     contains : function(o){
14604         return this.indexOf(o) != -1;
14605     },
14606    
14607 /**
14608  * Returns true if the collection contains the passed Object as a key.
14609  * @param {String} key The key to look for in the collection.
14610  * @return {Boolean} True if the collection contains the Object as a key.
14611  */
14612     containsKey : function(key){
14613         return typeof this.map[key] != "undefined";
14614     },
14615    
14616 /**
14617  * Removes all items from the collection.
14618  */
14619     clear : function(){
14620         this.length = 0;
14621         this.items = [];
14622         this.keys = [];
14623         this.map = {};
14624         this.fireEvent("clear");
14625     },
14626    
14627 /**
14628  * Returns the first item in the collection.
14629  * @return {Object} the first item in the collection..
14630  */
14631     first : function(){
14632         return this.items[0]; 
14633     },
14634    
14635 /**
14636  * Returns the last item in the collection.
14637  * @return {Object} the last item in the collection..
14638  */
14639     last : function(){
14640         return this.items[this.length-1];   
14641     },
14642     
14643     _sort : function(property, dir, fn){
14644         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14645         fn = fn || function(a, b){
14646             return a-b;
14647         };
14648         var c = [], k = this.keys, items = this.items;
14649         for(var i = 0, len = items.length; i < len; i++){
14650             c[c.length] = {key: k[i], value: items[i], index: i};
14651         }
14652         c.sort(function(a, b){
14653             var v = fn(a[property], b[property]) * dsc;
14654             if(v == 0){
14655                 v = (a.index < b.index ? -1 : 1);
14656             }
14657             return v;
14658         });
14659         for(var i = 0, len = c.length; i < len; i++){
14660             items[i] = c[i].value;
14661             k[i] = c[i].key;
14662         }
14663         this.fireEvent("sort", this);
14664     },
14665     
14666     /**
14667      * Sorts this collection with the passed comparison function
14668      * @param {String} direction (optional) "ASC" or "DESC"
14669      * @param {Function} fn (optional) comparison function
14670      */
14671     sort : function(dir, fn){
14672         this._sort("value", dir, fn);
14673     },
14674     
14675     /**
14676      * Sorts this collection by keys
14677      * @param {String} direction (optional) "ASC" or "DESC"
14678      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14679      */
14680     keySort : function(dir, fn){
14681         this._sort("key", dir, fn || function(a, b){
14682             return String(a).toUpperCase()-String(b).toUpperCase();
14683         });
14684     },
14685     
14686     /**
14687      * Returns a range of items in this collection
14688      * @param {Number} startIndex (optional) defaults to 0
14689      * @param {Number} endIndex (optional) default to the last item
14690      * @return {Array} An array of items
14691      */
14692     getRange : function(start, end){
14693         var items = this.items;
14694         if(items.length < 1){
14695             return [];
14696         }
14697         start = start || 0;
14698         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14699         var r = [];
14700         if(start <= end){
14701             for(var i = start; i <= end; i++) {
14702                     r[r.length] = items[i];
14703             }
14704         }else{
14705             for(var i = start; i >= end; i--) {
14706                     r[r.length] = items[i];
14707             }
14708         }
14709         return r;
14710     },
14711         
14712     /**
14713      * Filter the <i>objects</i> in this collection by a specific property. 
14714      * Returns a new collection that has been filtered.
14715      * @param {String} property A property on your objects
14716      * @param {String/RegExp} value Either string that the property values 
14717      * should start with or a RegExp to test against the property
14718      * @return {MixedCollection} The new filtered collection
14719      */
14720     filter : function(property, value){
14721         if(!value.exec){ // not a regex
14722             value = String(value);
14723             if(value.length == 0){
14724                 return this.clone();
14725             }
14726             value = new RegExp("^" + Roo.escapeRe(value), "i");
14727         }
14728         return this.filterBy(function(o){
14729             return o && value.test(o[property]);
14730         });
14731         },
14732     
14733     /**
14734      * Filter by a function. * Returns a new collection that has been filtered.
14735      * The passed function will be called with each 
14736      * object in the collection. If the function returns true, the value is included 
14737      * otherwise it is filtered.
14738      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14739      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14740      * @return {MixedCollection} The new filtered collection
14741      */
14742     filterBy : function(fn, scope){
14743         var r = new Roo.util.MixedCollection();
14744         r.getKey = this.getKey;
14745         var k = this.keys, it = this.items;
14746         for(var i = 0, len = it.length; i < len; i++){
14747             if(fn.call(scope||this, it[i], k[i])){
14748                                 r.add(k[i], it[i]);
14749                         }
14750         }
14751         return r;
14752     },
14753     
14754     /**
14755      * Creates a duplicate of this collection
14756      * @return {MixedCollection}
14757      */
14758     clone : function(){
14759         var r = new Roo.util.MixedCollection();
14760         var k = this.keys, it = this.items;
14761         for(var i = 0, len = it.length; i < len; i++){
14762             r.add(k[i], it[i]);
14763         }
14764         r.getKey = this.getKey;
14765         return r;
14766     }
14767 });
14768 /**
14769  * Returns the item associated with the passed key or index.
14770  * @method
14771  * @param {String/Number} key The key or index of the item.
14772  * @return {Object} The item associated with the passed key.
14773  */
14774 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14775  * Based on:
14776  * Ext JS Library 1.1.1
14777  * Copyright(c) 2006-2007, Ext JS, LLC.
14778  *
14779  * Originally Released Under LGPL - original licence link has changed is not relivant.
14780  *
14781  * Fork - LGPL
14782  * <script type="text/javascript">
14783  */
14784 /**
14785  * @class Roo.util.JSON
14786  * Modified version of Douglas Crockford"s json.js that doesn"t
14787  * mess with the Object prototype 
14788  * http://www.json.org/js.html
14789  * @static
14790  */
14791 Roo.util.JSON = new (function(){
14792     var useHasOwn = {}.hasOwnProperty ? true : false;
14793     
14794     // crashes Safari in some instances
14795     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14796     
14797     var pad = function(n) {
14798         return n < 10 ? "0" + n : n;
14799     };
14800     
14801     var m = {
14802         "\b": '\\b',
14803         "\t": '\\t',
14804         "\n": '\\n',
14805         "\f": '\\f',
14806         "\r": '\\r',
14807         '"' : '\\"',
14808         "\\": '\\\\'
14809     };
14810
14811     var encodeString = function(s){
14812         if (/["\\\x00-\x1f]/.test(s)) {
14813             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14814                 var c = m[b];
14815                 if(c){
14816                     return c;
14817                 }
14818                 c = b.charCodeAt();
14819                 return "\\u00" +
14820                     Math.floor(c / 16).toString(16) +
14821                     (c % 16).toString(16);
14822             }) + '"';
14823         }
14824         return '"' + s + '"';
14825     };
14826     
14827     var encodeArray = function(o){
14828         var a = ["["], b, i, l = o.length, v;
14829             for (i = 0; i < l; i += 1) {
14830                 v = o[i];
14831                 switch (typeof v) {
14832                     case "undefined":
14833                     case "function":
14834                     case "unknown":
14835                         break;
14836                     default:
14837                         if (b) {
14838                             a.push(',');
14839                         }
14840                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14841                         b = true;
14842                 }
14843             }
14844             a.push("]");
14845             return a.join("");
14846     };
14847     
14848     var encodeDate = function(o){
14849         return '"' + o.getFullYear() + "-" +
14850                 pad(o.getMonth() + 1) + "-" +
14851                 pad(o.getDate()) + "T" +
14852                 pad(o.getHours()) + ":" +
14853                 pad(o.getMinutes()) + ":" +
14854                 pad(o.getSeconds()) + '"';
14855     };
14856     
14857     /**
14858      * Encodes an Object, Array or other value
14859      * @param {Mixed} o The variable to encode
14860      * @return {String} The JSON string
14861      */
14862     this.encode = function(o)
14863     {
14864         // should this be extended to fully wrap stringify..
14865         
14866         if(typeof o == "undefined" || o === null){
14867             return "null";
14868         }else if(o instanceof Array){
14869             return encodeArray(o);
14870         }else if(o instanceof Date){
14871             return encodeDate(o);
14872         }else if(typeof o == "string"){
14873             return encodeString(o);
14874         }else if(typeof o == "number"){
14875             return isFinite(o) ? String(o) : "null";
14876         }else if(typeof o == "boolean"){
14877             return String(o);
14878         }else {
14879             var a = ["{"], b, i, v;
14880             for (i in o) {
14881                 if(!useHasOwn || o.hasOwnProperty(i)) {
14882                     v = o[i];
14883                     switch (typeof v) {
14884                     case "undefined":
14885                     case "function":
14886                     case "unknown":
14887                         break;
14888                     default:
14889                         if(b){
14890                             a.push(',');
14891                         }
14892                         a.push(this.encode(i), ":",
14893                                 v === null ? "null" : this.encode(v));
14894                         b = true;
14895                     }
14896                 }
14897             }
14898             a.push("}");
14899             return a.join("");
14900         }
14901     };
14902     
14903     /**
14904      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14905      * @param {String} json The JSON string
14906      * @return {Object} The resulting object
14907      */
14908     this.decode = function(json){
14909         
14910         return  /** eval:var:json */ eval("(" + json + ')');
14911     };
14912 })();
14913 /** 
14914  * Shorthand for {@link Roo.util.JSON#encode}
14915  * @member Roo encode 
14916  * @method */
14917 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14918 /** 
14919  * Shorthand for {@link Roo.util.JSON#decode}
14920  * @member Roo decode 
14921  * @method */
14922 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14923 /*
14924  * Based on:
14925  * Ext JS Library 1.1.1
14926  * Copyright(c) 2006-2007, Ext JS, LLC.
14927  *
14928  * Originally Released Under LGPL - original licence link has changed is not relivant.
14929  *
14930  * Fork - LGPL
14931  * <script type="text/javascript">
14932  */
14933  
14934 /**
14935  * @class Roo.util.Format
14936  * Reusable data formatting functions
14937  * @static
14938  */
14939 Roo.util.Format = function(){
14940     var trimRe = /^\s+|\s+$/g;
14941     return {
14942         /**
14943          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14944          * @param {String} value The string to truncate
14945          * @param {Number} length The maximum length to allow before truncating
14946          * @return {String} The converted text
14947          */
14948         ellipsis : function(value, len){
14949             if(value && value.length > len){
14950                 return value.substr(0, len-3)+"...";
14951             }
14952             return value;
14953         },
14954
14955         /**
14956          * Checks a reference and converts it to empty string if it is undefined
14957          * @param {Mixed} value Reference to check
14958          * @return {Mixed} Empty string if converted, otherwise the original value
14959          */
14960         undef : function(value){
14961             return typeof value != "undefined" ? value : "";
14962         },
14963
14964         /**
14965          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14966          * @param {String} value The string to encode
14967          * @return {String} The encoded text
14968          */
14969         htmlEncode : function(value){
14970             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14971         },
14972
14973         /**
14974          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14975          * @param {String} value The string to decode
14976          * @return {String} The decoded text
14977          */
14978         htmlDecode : function(value){
14979             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14980         },
14981
14982         /**
14983          * Trims any whitespace from either side of a string
14984          * @param {String} value The text to trim
14985          * @return {String} The trimmed text
14986          */
14987         trim : function(value){
14988             return String(value).replace(trimRe, "");
14989         },
14990
14991         /**
14992          * Returns a substring from within an original string
14993          * @param {String} value The original text
14994          * @param {Number} start The start index of the substring
14995          * @param {Number} length The length of the substring
14996          * @return {String} The substring
14997          */
14998         substr : function(value, start, length){
14999             return String(value).substr(start, length);
15000         },
15001
15002         /**
15003          * Converts a string to all lower case letters
15004          * @param {String} value The text to convert
15005          * @return {String} The converted text
15006          */
15007         lowercase : function(value){
15008             return String(value).toLowerCase();
15009         },
15010
15011         /**
15012          * Converts a string to all upper case letters
15013          * @param {String} value The text to convert
15014          * @return {String} The converted text
15015          */
15016         uppercase : function(value){
15017             return String(value).toUpperCase();
15018         },
15019
15020         /**
15021          * Converts the first character only of a string to upper case
15022          * @param {String} value The text to convert
15023          * @return {String} The converted text
15024          */
15025         capitalize : function(value){
15026             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15027         },
15028
15029         // private
15030         call : function(value, fn){
15031             if(arguments.length > 2){
15032                 var args = Array.prototype.slice.call(arguments, 2);
15033                 args.unshift(value);
15034                  
15035                 return /** eval:var:value */  eval(fn).apply(window, args);
15036             }else{
15037                 /** eval:var:value */
15038                 return /** eval:var:value */ eval(fn).call(window, value);
15039             }
15040         },
15041
15042        
15043         /**
15044          * safer version of Math.toFixed..??/
15045          * @param {Number/String} value The numeric value to format
15046          * @param {Number/String} value Decimal places 
15047          * @return {String} The formatted currency string
15048          */
15049         toFixed : function(v, n)
15050         {
15051             // why not use to fixed - precision is buggered???
15052             if (!n) {
15053                 return Math.round(v-0);
15054             }
15055             var fact = Math.pow(10,n+1);
15056             v = (Math.round((v-0)*fact))/fact;
15057             var z = (''+fact).substring(2);
15058             if (v == Math.floor(v)) {
15059                 return Math.floor(v) + '.' + z;
15060             }
15061             
15062             // now just padd decimals..
15063             var ps = String(v).split('.');
15064             var fd = (ps[1] + z);
15065             var r = fd.substring(0,n); 
15066             var rm = fd.substring(n); 
15067             if (rm < 5) {
15068                 return ps[0] + '.' + r;
15069             }
15070             r*=1; // turn it into a number;
15071             r++;
15072             if (String(r).length != n) {
15073                 ps[0]*=1;
15074                 ps[0]++;
15075                 r = String(r).substring(1); // chop the end off.
15076             }
15077             
15078             return ps[0] + '.' + r;
15079              
15080         },
15081         
15082         /**
15083          * Format a number as US currency
15084          * @param {Number/String} value The numeric value to format
15085          * @return {String} The formatted currency string
15086          */
15087         usMoney : function(v){
15088             return '$' + Roo.util.Format.number(v);
15089         },
15090         
15091         /**
15092          * Format a number
15093          * eventually this should probably emulate php's number_format
15094          * @param {Number/String} value The numeric value to format
15095          * @param {Number} decimals number of decimal places
15096          * @param {String} delimiter for thousands (default comma)
15097          * @return {String} The formatted currency string
15098          */
15099         number : function(v, decimals, thousandsDelimiter)
15100         {
15101             // multiply and round.
15102             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15103             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15104             
15105             var mul = Math.pow(10, decimals);
15106             var zero = String(mul).substring(1);
15107             v = (Math.round((v-0)*mul))/mul;
15108             
15109             // if it's '0' number.. then
15110             
15111             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15112             v = String(v);
15113             var ps = v.split('.');
15114             var whole = ps[0];
15115             
15116             var r = /(\d+)(\d{3})/;
15117             // add comma's
15118             
15119             if(thousandsDelimiter.length != 0) {
15120                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15121             } 
15122             
15123             var sub = ps[1] ?
15124                     // has decimals..
15125                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15126                     // does not have decimals
15127                     (decimals ? ('.' + zero) : '');
15128             
15129             
15130             return whole + sub ;
15131         },
15132         
15133         /**
15134          * Parse a value into a formatted date using the specified format pattern.
15135          * @param {Mixed} value The value to format
15136          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15137          * @return {String} The formatted date string
15138          */
15139         date : function(v, format){
15140             if(!v){
15141                 return "";
15142             }
15143             if(!(v instanceof Date)){
15144                 v = new Date(Date.parse(v));
15145             }
15146             return v.dateFormat(format || Roo.util.Format.defaults.date);
15147         },
15148
15149         /**
15150          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15151          * @param {String} format Any valid date format string
15152          * @return {Function} The date formatting function
15153          */
15154         dateRenderer : function(format){
15155             return function(v){
15156                 return Roo.util.Format.date(v, format);  
15157             };
15158         },
15159
15160         // private
15161         stripTagsRE : /<\/?[^>]+>/gi,
15162         
15163         /**
15164          * Strips all HTML tags
15165          * @param {Mixed} value The text from which to strip tags
15166          * @return {String} The stripped text
15167          */
15168         stripTags : function(v){
15169             return !v ? v : String(v).replace(this.stripTagsRE, "");
15170         },
15171         
15172         /**
15173          * Size in Mb,Gb etc.
15174          * @param {Number} value The number to be formated
15175          * @param {number} decimals how many decimal places
15176          * @return {String} the formated string
15177          */
15178         size : function(value, decimals)
15179         {
15180             var sizes = ['b', 'k', 'M', 'G', 'T'];
15181             if (value == 0) {
15182                 return 0;
15183             }
15184             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15185             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15186         }
15187         
15188         
15189         
15190     };
15191 }();
15192 Roo.util.Format.defaults = {
15193     date : 'd/M/Y'
15194 };/*
15195  * Based on:
15196  * Ext JS Library 1.1.1
15197  * Copyright(c) 2006-2007, Ext JS, LLC.
15198  *
15199  * Originally Released Under LGPL - original licence link has changed is not relivant.
15200  *
15201  * Fork - LGPL
15202  * <script type="text/javascript">
15203  */
15204
15205
15206  
15207
15208 /**
15209  * @class Roo.MasterTemplate
15210  * @extends Roo.Template
15211  * Provides a template that can have child templates. The syntax is:
15212 <pre><code>
15213 var t = new Roo.MasterTemplate(
15214         '&lt;select name="{name}"&gt;',
15215                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15216         '&lt;/select&gt;'
15217 );
15218 t.add('options', {value: 'foo', text: 'bar'});
15219 // or you can add multiple child elements in one shot
15220 t.addAll('options', [
15221     {value: 'foo', text: 'bar'},
15222     {value: 'foo2', text: 'bar2'},
15223     {value: 'foo3', text: 'bar3'}
15224 ]);
15225 // then append, applying the master template values
15226 t.append('my-form', {name: 'my-select'});
15227 </code></pre>
15228 * A name attribute for the child template is not required if you have only one child
15229 * template or you want to refer to them by index.
15230  */
15231 Roo.MasterTemplate = function(){
15232     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15233     this.originalHtml = this.html;
15234     var st = {};
15235     var m, re = this.subTemplateRe;
15236     re.lastIndex = 0;
15237     var subIndex = 0;
15238     while(m = re.exec(this.html)){
15239         var name = m[1], content = m[2];
15240         st[subIndex] = {
15241             name: name,
15242             index: subIndex,
15243             buffer: [],
15244             tpl : new Roo.Template(content)
15245         };
15246         if(name){
15247             st[name] = st[subIndex];
15248         }
15249         st[subIndex].tpl.compile();
15250         st[subIndex].tpl.call = this.call.createDelegate(this);
15251         subIndex++;
15252     }
15253     this.subCount = subIndex;
15254     this.subs = st;
15255 };
15256 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15257     /**
15258     * The regular expression used to match sub templates
15259     * @type RegExp
15260     * @property
15261     */
15262     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15263
15264     /**
15265      * Applies the passed values to a child template.
15266      * @param {String/Number} name (optional) The name or index of the child template
15267      * @param {Array/Object} values The values to be applied to the template
15268      * @return {MasterTemplate} this
15269      */
15270      add : function(name, values){
15271         if(arguments.length == 1){
15272             values = arguments[0];
15273             name = 0;
15274         }
15275         var s = this.subs[name];
15276         s.buffer[s.buffer.length] = s.tpl.apply(values);
15277         return this;
15278     },
15279
15280     /**
15281      * Applies all the passed values to a child template.
15282      * @param {String/Number} name (optional) The name or index of the child template
15283      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15284      * @param {Boolean} reset (optional) True to reset the template first
15285      * @return {MasterTemplate} this
15286      */
15287     fill : function(name, values, reset){
15288         var a = arguments;
15289         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15290             values = a[0];
15291             name = 0;
15292             reset = a[1];
15293         }
15294         if(reset){
15295             this.reset();
15296         }
15297         for(var i = 0, len = values.length; i < len; i++){
15298             this.add(name, values[i]);
15299         }
15300         return this;
15301     },
15302
15303     /**
15304      * Resets the template for reuse
15305      * @return {MasterTemplate} this
15306      */
15307      reset : function(){
15308         var s = this.subs;
15309         for(var i = 0; i < this.subCount; i++){
15310             s[i].buffer = [];
15311         }
15312         return this;
15313     },
15314
15315     applyTemplate : function(values){
15316         var s = this.subs;
15317         var replaceIndex = -1;
15318         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15319             return s[++replaceIndex].buffer.join("");
15320         });
15321         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15322     },
15323
15324     apply : function(){
15325         return this.applyTemplate.apply(this, arguments);
15326     },
15327
15328     compile : function(){return this;}
15329 });
15330
15331 /**
15332  * Alias for fill().
15333  * @method
15334  */
15335 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15336  /**
15337  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15338  * var tpl = Roo.MasterTemplate.from('element-id');
15339  * @param {String/HTMLElement} el
15340  * @param {Object} config
15341  * @static
15342  */
15343 Roo.MasterTemplate.from = function(el, config){
15344     el = Roo.getDom(el);
15345     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15346 };/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356
15357  
15358 /**
15359  * @class Roo.util.CSS
15360  * Utility class for manipulating CSS rules
15361  * @static
15362
15363  */
15364 Roo.util.CSS = function(){
15365         var rules = null;
15366         var doc = document;
15367
15368     var camelRe = /(-[a-z])/gi;
15369     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15370
15371    return {
15372    /**
15373     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15374     * tag and appended to the HEAD of the document.
15375     * @param {String|Object} cssText The text containing the css rules
15376     * @param {String} id An id to add to the stylesheet for later removal
15377     * @return {StyleSheet}
15378     */
15379     createStyleSheet : function(cssText, id){
15380         var ss;
15381         var head = doc.getElementsByTagName("head")[0];
15382         var nrules = doc.createElement("style");
15383         nrules.setAttribute("type", "text/css");
15384         if(id){
15385             nrules.setAttribute("id", id);
15386         }
15387         if (typeof(cssText) != 'string') {
15388             // support object maps..
15389             // not sure if this a good idea.. 
15390             // perhaps it should be merged with the general css handling
15391             // and handle js style props.
15392             var cssTextNew = [];
15393             for(var n in cssText) {
15394                 var citems = [];
15395                 for(var k in cssText[n]) {
15396                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15397                 }
15398                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15399                 
15400             }
15401             cssText = cssTextNew.join("\n");
15402             
15403         }
15404        
15405        
15406        if(Roo.isIE){
15407            head.appendChild(nrules);
15408            ss = nrules.styleSheet;
15409            ss.cssText = cssText;
15410        }else{
15411            try{
15412                 nrules.appendChild(doc.createTextNode(cssText));
15413            }catch(e){
15414                nrules.cssText = cssText; 
15415            }
15416            head.appendChild(nrules);
15417            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15418        }
15419        this.cacheStyleSheet(ss);
15420        return ss;
15421    },
15422
15423    /**
15424     * Removes a style or link tag by id
15425     * @param {String} id The id of the tag
15426     */
15427    removeStyleSheet : function(id){
15428        var existing = doc.getElementById(id);
15429        if(existing){
15430            existing.parentNode.removeChild(existing);
15431        }
15432    },
15433
15434    /**
15435     * Dynamically swaps an existing stylesheet reference for a new one
15436     * @param {String} id The id of an existing link tag to remove
15437     * @param {String} url The href of the new stylesheet to include
15438     */
15439    swapStyleSheet : function(id, url){
15440        this.removeStyleSheet(id);
15441        var ss = doc.createElement("link");
15442        ss.setAttribute("rel", "stylesheet");
15443        ss.setAttribute("type", "text/css");
15444        ss.setAttribute("id", id);
15445        ss.setAttribute("href", url);
15446        doc.getElementsByTagName("head")[0].appendChild(ss);
15447    },
15448    
15449    /**
15450     * Refresh the rule cache if you have dynamically added stylesheets
15451     * @return {Object} An object (hash) of rules indexed by selector
15452     */
15453    refreshCache : function(){
15454        return this.getRules(true);
15455    },
15456
15457    // private
15458    cacheStyleSheet : function(stylesheet){
15459        if(!rules){
15460            rules = {};
15461        }
15462        try{// try catch for cross domain access issue
15463            var ssRules = stylesheet.cssRules || stylesheet.rules;
15464            for(var j = ssRules.length-1; j >= 0; --j){
15465                rules[ssRules[j].selectorText] = ssRules[j];
15466            }
15467        }catch(e){}
15468    },
15469    
15470    /**
15471     * Gets all css rules for the document
15472     * @param {Boolean} refreshCache true to refresh the internal cache
15473     * @return {Object} An object (hash) of rules indexed by selector
15474     */
15475    getRules : function(refreshCache){
15476                 if(rules == null || refreshCache){
15477                         rules = {};
15478                         var ds = doc.styleSheets;
15479                         for(var i =0, len = ds.length; i < len; i++){
15480                             try{
15481                         this.cacheStyleSheet(ds[i]);
15482                     }catch(e){} 
15483                 }
15484                 }
15485                 return rules;
15486         },
15487         
15488         /**
15489     * Gets an an individual CSS rule by selector(s)
15490     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15491     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15492     * @return {CSSRule} The CSS rule or null if one is not found
15493     */
15494    getRule : function(selector, refreshCache){
15495                 var rs = this.getRules(refreshCache);
15496                 if(!(selector instanceof Array)){
15497                     return rs[selector];
15498                 }
15499                 for(var i = 0; i < selector.length; i++){
15500                         if(rs[selector[i]]){
15501                                 return rs[selector[i]];
15502                         }
15503                 }
15504                 return null;
15505         },
15506         
15507         
15508         /**
15509     * Updates a rule property
15510     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15511     * @param {String} property The css property
15512     * @param {String} value The new value for the property
15513     * @return {Boolean} true If a rule was found and updated
15514     */
15515    updateRule : function(selector, property, value){
15516                 if(!(selector instanceof Array)){
15517                         var rule = this.getRule(selector);
15518                         if(rule){
15519                                 rule.style[property.replace(camelRe, camelFn)] = value;
15520                                 return true;
15521                         }
15522                 }else{
15523                         for(var i = 0; i < selector.length; i++){
15524                                 if(this.updateRule(selector[i], property, value)){
15525                                         return true;
15526                                 }
15527                         }
15528                 }
15529                 return false;
15530         }
15531    };   
15532 }();/*
15533  * Based on:
15534  * Ext JS Library 1.1.1
15535  * Copyright(c) 2006-2007, Ext JS, LLC.
15536  *
15537  * Originally Released Under LGPL - original licence link has changed is not relivant.
15538  *
15539  * Fork - LGPL
15540  * <script type="text/javascript">
15541  */
15542
15543  
15544
15545 /**
15546  * @class Roo.util.ClickRepeater
15547  * @extends Roo.util.Observable
15548  * 
15549  * A wrapper class which can be applied to any element. Fires a "click" event while the
15550  * mouse is pressed. The interval between firings may be specified in the config but
15551  * defaults to 10 milliseconds.
15552  * 
15553  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15554  * 
15555  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15556  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15557  * Similar to an autorepeat key delay.
15558  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15559  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15560  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15561  *           "interval" and "delay" are ignored. "immediate" is honored.
15562  * @cfg {Boolean} preventDefault True to prevent the default click event
15563  * @cfg {Boolean} stopDefault True to stop the default click event
15564  * 
15565  * @history
15566  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15567  *     2007-02-02 jvs Renamed to ClickRepeater
15568  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15569  *
15570  *  @constructor
15571  * @param {String/HTMLElement/Element} el The element to listen on
15572  * @param {Object} config
15573  **/
15574 Roo.util.ClickRepeater = function(el, config)
15575 {
15576     this.el = Roo.get(el);
15577     this.el.unselectable();
15578
15579     Roo.apply(this, config);
15580
15581     this.addEvents({
15582     /**
15583      * @event mousedown
15584      * Fires when the mouse button is depressed.
15585      * @param {Roo.util.ClickRepeater} this
15586      */
15587         "mousedown" : true,
15588     /**
15589      * @event click
15590      * Fires on a specified interval during the time the element is pressed.
15591      * @param {Roo.util.ClickRepeater} this
15592      */
15593         "click" : true,
15594     /**
15595      * @event mouseup
15596      * Fires when the mouse key is released.
15597      * @param {Roo.util.ClickRepeater} this
15598      */
15599         "mouseup" : true
15600     });
15601
15602     this.el.on("mousedown", this.handleMouseDown, this);
15603     if(this.preventDefault || this.stopDefault){
15604         this.el.on("click", function(e){
15605             if(this.preventDefault){
15606                 e.preventDefault();
15607             }
15608             if(this.stopDefault){
15609                 e.stopEvent();
15610             }
15611         }, this);
15612     }
15613
15614     // allow inline handler
15615     if(this.handler){
15616         this.on("click", this.handler,  this.scope || this);
15617     }
15618
15619     Roo.util.ClickRepeater.superclass.constructor.call(this);
15620 };
15621
15622 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15623     interval : 20,
15624     delay: 250,
15625     preventDefault : true,
15626     stopDefault : false,
15627     timer : 0,
15628
15629     // private
15630     handleMouseDown : function(){
15631         clearTimeout(this.timer);
15632         this.el.blur();
15633         if(this.pressClass){
15634             this.el.addClass(this.pressClass);
15635         }
15636         this.mousedownTime = new Date();
15637
15638         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15639         this.el.on("mouseout", this.handleMouseOut, this);
15640
15641         this.fireEvent("mousedown", this);
15642         this.fireEvent("click", this);
15643         
15644         this.timer = this.click.defer(this.delay || this.interval, this);
15645     },
15646
15647     // private
15648     click : function(){
15649         this.fireEvent("click", this);
15650         this.timer = this.click.defer(this.getInterval(), this);
15651     },
15652
15653     // private
15654     getInterval: function(){
15655         if(!this.accelerate){
15656             return this.interval;
15657         }
15658         var pressTime = this.mousedownTime.getElapsed();
15659         if(pressTime < 500){
15660             return 400;
15661         }else if(pressTime < 1700){
15662             return 320;
15663         }else if(pressTime < 2600){
15664             return 250;
15665         }else if(pressTime < 3500){
15666             return 180;
15667         }else if(pressTime < 4400){
15668             return 140;
15669         }else if(pressTime < 5300){
15670             return 80;
15671         }else if(pressTime < 6200){
15672             return 50;
15673         }else{
15674             return 10;
15675         }
15676     },
15677
15678     // private
15679     handleMouseOut : function(){
15680         clearTimeout(this.timer);
15681         if(this.pressClass){
15682             this.el.removeClass(this.pressClass);
15683         }
15684         this.el.on("mouseover", this.handleMouseReturn, this);
15685     },
15686
15687     // private
15688     handleMouseReturn : function(){
15689         this.el.un("mouseover", this.handleMouseReturn);
15690         if(this.pressClass){
15691             this.el.addClass(this.pressClass);
15692         }
15693         this.click();
15694     },
15695
15696     // private
15697     handleMouseUp : function(){
15698         clearTimeout(this.timer);
15699         this.el.un("mouseover", this.handleMouseReturn);
15700         this.el.un("mouseout", this.handleMouseOut);
15701         Roo.get(document).un("mouseup", this.handleMouseUp);
15702         this.el.removeClass(this.pressClass);
15703         this.fireEvent("mouseup", this);
15704     }
15705 });/**
15706  * @class Roo.util.Clipboard
15707  * @static
15708  * 
15709  * Clipboard UTILS
15710  * 
15711  **/
15712 Roo.util.Clipboard = {
15713     /**
15714      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15715      * @param {String} text to copy to clipboard
15716      */
15717     write : function(text) {
15718         // navigator clipboard api needs a secure context (https)
15719         if (navigator.clipboard && window.isSecureContext) {
15720             // navigator clipboard api method'
15721             navigator.clipboard.writeText(text);
15722             return ;
15723         } 
15724         // text area method
15725         var ta = document.createElement("textarea");
15726         ta.value = text;
15727         // make the textarea out of viewport
15728         ta.style.position = "fixed";
15729         ta.style.left = "-999999px";
15730         ta.style.top = "-999999px";
15731         document.body.appendChild(ta);
15732         ta.focus();
15733         ta.select();
15734         document.execCommand('copy');
15735         (function() {
15736             ta.remove();
15737         }).defer(100);
15738         
15739     }
15740         
15741 }
15742     /*
15743  * Based on:
15744  * Ext JS Library 1.1.1
15745  * Copyright(c) 2006-2007, Ext JS, LLC.
15746  *
15747  * Originally Released Under LGPL - original licence link has changed is not relivant.
15748  *
15749  * Fork - LGPL
15750  * <script type="text/javascript">
15751  */
15752
15753  
15754 /**
15755  * @class Roo.KeyNav
15756  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15757  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15758  * way to implement custom navigation schemes for any UI component.</p>
15759  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15760  * pageUp, pageDown, del, home, end.  Usage:</p>
15761  <pre><code>
15762 var nav = new Roo.KeyNav("my-element", {
15763     "left" : function(e){
15764         this.moveLeft(e.ctrlKey);
15765     },
15766     "right" : function(e){
15767         this.moveRight(e.ctrlKey);
15768     },
15769     "enter" : function(e){
15770         this.save();
15771     },
15772     scope : this
15773 });
15774 </code></pre>
15775  * @constructor
15776  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15777  * @param {Object} config The config
15778  */
15779 Roo.KeyNav = function(el, config){
15780     this.el = Roo.get(el);
15781     Roo.apply(this, config);
15782     if(!this.disabled){
15783         this.disabled = true;
15784         this.enable();
15785     }
15786 };
15787
15788 Roo.KeyNav.prototype = {
15789     /**
15790      * @cfg {Boolean} disabled
15791      * True to disable this KeyNav instance (defaults to false)
15792      */
15793     disabled : false,
15794     /**
15795      * @cfg {String} defaultEventAction
15796      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15797      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15798      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15799      */
15800     defaultEventAction: "stopEvent",
15801     /**
15802      * @cfg {Boolean} forceKeyDown
15803      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15804      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15805      * handle keydown instead of keypress.
15806      */
15807     forceKeyDown : false,
15808
15809     // private
15810     prepareEvent : function(e){
15811         var k = e.getKey();
15812         var h = this.keyToHandler[k];
15813         //if(h && this[h]){
15814         //    e.stopPropagation();
15815         //}
15816         if(Roo.isSafari && h && k >= 37 && k <= 40){
15817             e.stopEvent();
15818         }
15819     },
15820
15821     // private
15822     relay : function(e){
15823         var k = e.getKey();
15824         var h = this.keyToHandler[k];
15825         if(h && this[h]){
15826             if(this.doRelay(e, this[h], h) !== true){
15827                 e[this.defaultEventAction]();
15828             }
15829         }
15830     },
15831
15832     // private
15833     doRelay : function(e, h, hname){
15834         return h.call(this.scope || this, e);
15835     },
15836
15837     // possible handlers
15838     enter : false,
15839     left : false,
15840     right : false,
15841     up : false,
15842     down : false,
15843     tab : false,
15844     esc : false,
15845     pageUp : false,
15846     pageDown : false,
15847     del : false,
15848     home : false,
15849     end : false,
15850
15851     // quick lookup hash
15852     keyToHandler : {
15853         37 : "left",
15854         39 : "right",
15855         38 : "up",
15856         40 : "down",
15857         33 : "pageUp",
15858         34 : "pageDown",
15859         46 : "del",
15860         36 : "home",
15861         35 : "end",
15862         13 : "enter",
15863         27 : "esc",
15864         9  : "tab"
15865     },
15866
15867         /**
15868          * Enable this KeyNav
15869          */
15870         enable: function(){
15871                 if(this.disabled){
15872             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15873             // the EventObject will normalize Safari automatically
15874             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15875                 this.el.on("keydown", this.relay,  this);
15876             }else{
15877                 this.el.on("keydown", this.prepareEvent,  this);
15878                 this.el.on("keypress", this.relay,  this);
15879             }
15880                     this.disabled = false;
15881                 }
15882         },
15883
15884         /**
15885          * Disable this KeyNav
15886          */
15887         disable: function(){
15888                 if(!this.disabled){
15889                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15890                 this.el.un("keydown", this.relay);
15891             }else{
15892                 this.el.un("keydown", this.prepareEvent);
15893                 this.el.un("keypress", this.relay);
15894             }
15895                     this.disabled = true;
15896                 }
15897         }
15898 };/*
15899  * Based on:
15900  * Ext JS Library 1.1.1
15901  * Copyright(c) 2006-2007, Ext JS, LLC.
15902  *
15903  * Originally Released Under LGPL - original licence link has changed is not relivant.
15904  *
15905  * Fork - LGPL
15906  * <script type="text/javascript">
15907  */
15908
15909  
15910 /**
15911  * @class Roo.KeyMap
15912  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15913  * The constructor accepts the same config object as defined by {@link #addBinding}.
15914  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15915  * combination it will call the function with this signature (if the match is a multi-key
15916  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15917  * A KeyMap can also handle a string representation of keys.<br />
15918  * Usage:
15919  <pre><code>
15920 // map one key by key code
15921 var map = new Roo.KeyMap("my-element", {
15922     key: 13, // or Roo.EventObject.ENTER
15923     fn: myHandler,
15924     scope: myObject
15925 });
15926
15927 // map multiple keys to one action by string
15928 var map = new Roo.KeyMap("my-element", {
15929     key: "a\r\n\t",
15930     fn: myHandler,
15931     scope: myObject
15932 });
15933
15934 // map multiple keys to multiple actions by strings and array of codes
15935 var map = new Roo.KeyMap("my-element", [
15936     {
15937         key: [10,13],
15938         fn: function(){ alert("Return was pressed"); }
15939     }, {
15940         key: "abc",
15941         fn: function(){ alert('a, b or c was pressed'); }
15942     }, {
15943         key: "\t",
15944         ctrl:true,
15945         shift:true,
15946         fn: function(){ alert('Control + shift + tab was pressed.'); }
15947     }
15948 ]);
15949 </code></pre>
15950  * <b>Note: A KeyMap starts enabled</b>
15951  * @constructor
15952  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15953  * @param {Object} config The config (see {@link #addBinding})
15954  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15955  */
15956 Roo.KeyMap = function(el, config, eventName){
15957     this.el  = Roo.get(el);
15958     this.eventName = eventName || "keydown";
15959     this.bindings = [];
15960     if(config){
15961         this.addBinding(config);
15962     }
15963     this.enable();
15964 };
15965
15966 Roo.KeyMap.prototype = {
15967     /**
15968      * True to stop the event from bubbling and prevent the default browser action if the
15969      * key was handled by the KeyMap (defaults to false)
15970      * @type Boolean
15971      */
15972     stopEvent : false,
15973
15974     /**
15975      * Add a new binding to this KeyMap. The following config object properties are supported:
15976      * <pre>
15977 Property    Type             Description
15978 ----------  ---------------  ----------------------------------------------------------------------
15979 key         String/Array     A single keycode or an array of keycodes to handle
15980 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15981 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15982 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15983 fn          Function         The function to call when KeyMap finds the expected key combination
15984 scope       Object           The scope of the callback function
15985 </pre>
15986      *
15987      * Usage:
15988      * <pre><code>
15989 // Create a KeyMap
15990 var map = new Roo.KeyMap(document, {
15991     key: Roo.EventObject.ENTER,
15992     fn: handleKey,
15993     scope: this
15994 });
15995
15996 //Add a new binding to the existing KeyMap later
15997 map.addBinding({
15998     key: 'abc',
15999     shift: true,
16000     fn: handleKey,
16001     scope: this
16002 });
16003 </code></pre>
16004      * @param {Object/Array} config A single KeyMap config or an array of configs
16005      */
16006         addBinding : function(config){
16007         if(config instanceof Array){
16008             for(var i = 0, len = config.length; i < len; i++){
16009                 this.addBinding(config[i]);
16010             }
16011             return;
16012         }
16013         var keyCode = config.key,
16014             shift = config.shift, 
16015             ctrl = config.ctrl, 
16016             alt = config.alt,
16017             fn = config.fn,
16018             scope = config.scope;
16019         if(typeof keyCode == "string"){
16020             var ks = [];
16021             var keyString = keyCode.toUpperCase();
16022             for(var j = 0, len = keyString.length; j < len; j++){
16023                 ks.push(keyString.charCodeAt(j));
16024             }
16025             keyCode = ks;
16026         }
16027         var keyArray = keyCode instanceof Array;
16028         var handler = function(e){
16029             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16030                 var k = e.getKey();
16031                 if(keyArray){
16032                     for(var i = 0, len = keyCode.length; i < len; i++){
16033                         if(keyCode[i] == k){
16034                           if(this.stopEvent){
16035                               e.stopEvent();
16036                           }
16037                           fn.call(scope || window, k, e);
16038                           return;
16039                         }
16040                     }
16041                 }else{
16042                     if(k == keyCode){
16043                         if(this.stopEvent){
16044                            e.stopEvent();
16045                         }
16046                         fn.call(scope || window, k, e);
16047                     }
16048                 }
16049             }
16050         };
16051         this.bindings.push(handler);  
16052         },
16053
16054     /**
16055      * Shorthand for adding a single key listener
16056      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16057      * following options:
16058      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16059      * @param {Function} fn The function to call
16060      * @param {Object} scope (optional) The scope of the function
16061      */
16062     on : function(key, fn, scope){
16063         var keyCode, shift, ctrl, alt;
16064         if(typeof key == "object" && !(key instanceof Array)){
16065             keyCode = key.key;
16066             shift = key.shift;
16067             ctrl = key.ctrl;
16068             alt = key.alt;
16069         }else{
16070             keyCode = key;
16071         }
16072         this.addBinding({
16073             key: keyCode,
16074             shift: shift,
16075             ctrl: ctrl,
16076             alt: alt,
16077             fn: fn,
16078             scope: scope
16079         })
16080     },
16081
16082     // private
16083     handleKeyDown : function(e){
16084             if(this.enabled){ //just in case
16085             var b = this.bindings;
16086             for(var i = 0, len = b.length; i < len; i++){
16087                 b[i].call(this, e);
16088             }
16089             }
16090         },
16091         
16092         /**
16093          * Returns true if this KeyMap is enabled
16094          * @return {Boolean} 
16095          */
16096         isEnabled : function(){
16097             return this.enabled;  
16098         },
16099         
16100         /**
16101          * Enables this KeyMap
16102          */
16103         enable: function(){
16104                 if(!this.enabled){
16105                     this.el.on(this.eventName, this.handleKeyDown, this);
16106                     this.enabled = true;
16107                 }
16108         },
16109
16110         /**
16111          * Disable this KeyMap
16112          */
16113         disable: function(){
16114                 if(this.enabled){
16115                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16116                     this.enabled = false;
16117                 }
16118         }
16119 };/*
16120  * Based on:
16121  * Ext JS Library 1.1.1
16122  * Copyright(c) 2006-2007, Ext JS, LLC.
16123  *
16124  * Originally Released Under LGPL - original licence link has changed is not relivant.
16125  *
16126  * Fork - LGPL
16127  * <script type="text/javascript">
16128  */
16129
16130  
16131 /**
16132  * @class Roo.util.TextMetrics
16133  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16134  * wide, in pixels, a given block of text will be.
16135  * @static
16136  */
16137 Roo.util.TextMetrics = function(){
16138     var shared;
16139     return {
16140         /**
16141          * Measures the size of the specified text
16142          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16143          * that can affect the size of the rendered text
16144          * @param {String} text The text to measure
16145          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16146          * in order to accurately measure the text height
16147          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16148          */
16149         measure : function(el, text, fixedWidth){
16150             if(!shared){
16151                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16152             }
16153             shared.bind(el);
16154             shared.setFixedWidth(fixedWidth || 'auto');
16155             return shared.getSize(text);
16156         },
16157
16158         /**
16159          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16160          * the overhead of multiple calls to initialize the style properties on each measurement.
16161          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16162          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16163          * in order to accurately measure the text height
16164          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16165          */
16166         createInstance : function(el, fixedWidth){
16167             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16168         }
16169     };
16170 }();
16171
16172 /**
16173  * @class Roo.util.TextMetrics.Instance
16174  * Instance of  TextMetrics Calcuation
16175  * @constructor
16176  * Create a new TextMetrics Instance
16177  * @param {Object} bindto
16178  * @param {Boolean} fixedWidth
16179  */
16180
16181 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16182 {
16183     var ml = new Roo.Element(document.createElement('div'));
16184     document.body.appendChild(ml.dom);
16185     ml.position('absolute');
16186     ml.setLeftTop(-1000, -1000);
16187     ml.hide();
16188
16189     if(fixedWidth){
16190         ml.setWidth(fixedWidth);
16191     }
16192      
16193     var instance = {
16194         /**
16195          * Returns the size of the specified text based on the internal element's style and width properties
16196          * @param {String} text The text to measure
16197          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16198          */
16199         getSize : function(text){
16200             ml.update(text);
16201             var s = ml.getSize();
16202             ml.update('');
16203             return s;
16204         },
16205
16206         /**
16207          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16208          * that can affect the size of the rendered text
16209          * @param {String/HTMLElement} el The element, dom node or id
16210          */
16211         bind : function(el){
16212             ml.setStyle(
16213                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16214             );
16215         },
16216
16217         /**
16218          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16219          * to set a fixed width in order to accurately measure the text height.
16220          * @param {Number} width The width to set on the element
16221          */
16222         setFixedWidth : function(width){
16223             ml.setWidth(width);
16224         },
16225
16226         /**
16227          * Returns the measured width of the specified text
16228          * @param {String} text The text to measure
16229          * @return {Number} width The width in pixels
16230          */
16231         getWidth : function(text){
16232             ml.dom.style.width = 'auto';
16233             return this.getSize(text).width;
16234         },
16235
16236         /**
16237          * Returns the measured height of the specified text.  For multiline text, be sure to call
16238          * {@link #setFixedWidth} if necessary.
16239          * @param {String} text The text to measure
16240          * @return {Number} height The height in pixels
16241          */
16242         getHeight : function(text){
16243             return this.getSize(text).height;
16244         }
16245     };
16246
16247     instance.bind(bindTo);
16248
16249     return instance;
16250 };
16251
16252 // backwards compat
16253 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16254  * Based on:
16255  * Ext JS Library 1.1.1
16256  * Copyright(c) 2006-2007, Ext JS, LLC.
16257  *
16258  * Originally Released Under LGPL - original licence link has changed is not relivant.
16259  *
16260  * Fork - LGPL
16261  * <script type="text/javascript">
16262  */
16263
16264 /**
16265  * @class Roo.state.Provider
16266  * Abstract base class for state provider implementations. This class provides methods
16267  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16268  * Provider interface.
16269  */
16270 Roo.state.Provider = function(){
16271     /**
16272      * @event statechange
16273      * Fires when a state change occurs.
16274      * @param {Provider} this This state provider
16275      * @param {String} key The state key which was changed
16276      * @param {String} value The encoded value for the state
16277      */
16278     this.addEvents({
16279         "statechange": true
16280     });
16281     this.state = {};
16282     Roo.state.Provider.superclass.constructor.call(this);
16283 };
16284 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16285     /**
16286      * Returns the current value for a key
16287      * @param {String} name The key name
16288      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16289      * @return {Mixed} The state data
16290      */
16291     get : function(name, defaultValue){
16292         return typeof this.state[name] == "undefined" ?
16293             defaultValue : this.state[name];
16294     },
16295     
16296     /**
16297      * Clears a value from the state
16298      * @param {String} name The key name
16299      */
16300     clear : function(name){
16301         delete this.state[name];
16302         this.fireEvent("statechange", this, name, null);
16303     },
16304     
16305     /**
16306      * Sets the value for a key
16307      * @param {String} name The key name
16308      * @param {Mixed} value The value to set
16309      */
16310     set : function(name, value){
16311         this.state[name] = value;
16312         this.fireEvent("statechange", this, name, value);
16313     },
16314     
16315     /**
16316      * Decodes a string previously encoded with {@link #encodeValue}.
16317      * @param {String} value The value to decode
16318      * @return {Mixed} The decoded value
16319      */
16320     decodeValue : function(cookie){
16321         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16322         var matches = re.exec(unescape(cookie));
16323         if(!matches || !matches[1]) {
16324             return; // non state cookie
16325         }
16326         var type = matches[1];
16327         var v = matches[2];
16328         switch(type){
16329             case "n":
16330                 return parseFloat(v);
16331             case "d":
16332                 return new Date(Date.parse(v));
16333             case "b":
16334                 return (v == "1");
16335             case "a":
16336                 var all = [];
16337                 var values = v.split("^");
16338                 for(var i = 0, len = values.length; i < len; i++){
16339                     all.push(this.decodeValue(values[i]));
16340                 }
16341                 return all;
16342            case "o":
16343                 var all = {};
16344                 var values = v.split("^");
16345                 for(var i = 0, len = values.length; i < len; i++){
16346                     var kv = values[i].split("=");
16347                     all[kv[0]] = this.decodeValue(kv[1]);
16348                 }
16349                 return all;
16350            default:
16351                 return v;
16352         }
16353     },
16354     
16355     /**
16356      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16357      * @param {Mixed} value The value to encode
16358      * @return {String} The encoded value
16359      */
16360     encodeValue : function(v){
16361         var enc;
16362         if(typeof v == "number"){
16363             enc = "n:" + v;
16364         }else if(typeof v == "boolean"){
16365             enc = "b:" + (v ? "1" : "0");
16366         }else if(v instanceof Date){
16367             enc = "d:" + v.toGMTString();
16368         }else if(v instanceof Array){
16369             var flat = "";
16370             for(var i = 0, len = v.length; i < len; i++){
16371                 flat += this.encodeValue(v[i]);
16372                 if(i != len-1) {
16373                     flat += "^";
16374                 }
16375             }
16376             enc = "a:" + flat;
16377         }else if(typeof v == "object"){
16378             var flat = "";
16379             for(var key in v){
16380                 if(typeof v[key] != "function"){
16381                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16382                 }
16383             }
16384             enc = "o:" + flat.substring(0, flat.length-1);
16385         }else{
16386             enc = "s:" + v;
16387         }
16388         return escape(enc);        
16389     }
16390 });
16391
16392 /*
16393  * Based on:
16394  * Ext JS Library 1.1.1
16395  * Copyright(c) 2006-2007, Ext JS, LLC.
16396  *
16397  * Originally Released Under LGPL - original licence link has changed is not relivant.
16398  *
16399  * Fork - LGPL
16400  * <script type="text/javascript">
16401  */
16402 /**
16403  * @class Roo.state.Manager
16404  * This is the global state manager. By default all components that are "state aware" check this class
16405  * for state information if you don't pass them a custom state provider. In order for this class
16406  * to be useful, it must be initialized with a provider when your application initializes.
16407  <pre><code>
16408 // in your initialization function
16409 init : function(){
16410    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16411    ...
16412    // supposed you have a {@link Roo.BorderLayout}
16413    var layout = new Roo.BorderLayout(...);
16414    layout.restoreState();
16415    // or a {Roo.BasicDialog}
16416    var dialog = new Roo.BasicDialog(...);
16417    dialog.restoreState();
16418  </code></pre>
16419  * @static
16420  */
16421 Roo.state.Manager = function(){
16422     var provider = new Roo.state.Provider();
16423     
16424     return {
16425         /**
16426          * Configures the default state provider for your application
16427          * @param {Provider} stateProvider The state provider to set
16428          */
16429         setProvider : function(stateProvider){
16430             provider = stateProvider;
16431         },
16432         
16433         /**
16434          * Returns the current value for a key
16435          * @param {String} name The key name
16436          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16437          * @return {Mixed} The state data
16438          */
16439         get : function(key, defaultValue){
16440             return provider.get(key, defaultValue);
16441         },
16442         
16443         /**
16444          * Sets the value for a key
16445          * @param {String} name The key name
16446          * @param {Mixed} value The state data
16447          */
16448          set : function(key, value){
16449             provider.set(key, value);
16450         },
16451         
16452         /**
16453          * Clears a value from the state
16454          * @param {String} name The key name
16455          */
16456         clear : function(key){
16457             provider.clear(key);
16458         },
16459         
16460         /**
16461          * Gets the currently configured state provider
16462          * @return {Provider} The state provider
16463          */
16464         getProvider : function(){
16465             return provider;
16466         }
16467     };
16468 }();
16469 /*
16470  * Based on:
16471  * Ext JS Library 1.1.1
16472  * Copyright(c) 2006-2007, Ext JS, LLC.
16473  *
16474  * Originally Released Under LGPL - original licence link has changed is not relivant.
16475  *
16476  * Fork - LGPL
16477  * <script type="text/javascript">
16478  */
16479 /**
16480  * @class Roo.state.CookieProvider
16481  * @extends Roo.state.Provider
16482  * The default Provider implementation which saves state via cookies.
16483  * <br />Usage:
16484  <pre><code>
16485    var cp = new Roo.state.CookieProvider({
16486        path: "/cgi-bin/",
16487        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16488        domain: "roojs.com"
16489    })
16490    Roo.state.Manager.setProvider(cp);
16491  </code></pre>
16492  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16493  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16494  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16495  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16496  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16497  * domain the page is running on including the 'www' like 'www.roojs.com')
16498  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16499  * @constructor
16500  * Create a new CookieProvider
16501  * @param {Object} config The configuration object
16502  */
16503 Roo.state.CookieProvider = function(config){
16504     Roo.state.CookieProvider.superclass.constructor.call(this);
16505     this.path = "/";
16506     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16507     this.domain = null;
16508     this.secure = false;
16509     Roo.apply(this, config);
16510     this.state = this.readCookies();
16511 };
16512
16513 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16514     // private
16515     set : function(name, value){
16516         if(typeof value == "undefined" || value === null){
16517             this.clear(name);
16518             return;
16519         }
16520         this.setCookie(name, value);
16521         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16522     },
16523
16524     // private
16525     clear : function(name){
16526         this.clearCookie(name);
16527         Roo.state.CookieProvider.superclass.clear.call(this, name);
16528     },
16529
16530     // private
16531     readCookies : function(){
16532         var cookies = {};
16533         var c = document.cookie + ";";
16534         var re = /\s?(.*?)=(.*?);/g;
16535         var matches;
16536         while((matches = re.exec(c)) != null){
16537             var name = matches[1];
16538             var value = matches[2];
16539             if(name && name.substring(0,3) == "ys-"){
16540                 cookies[name.substr(3)] = this.decodeValue(value);
16541             }
16542         }
16543         return cookies;
16544     },
16545
16546     // private
16547     setCookie : function(name, value){
16548         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16549            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16550            ((this.path == null) ? "" : ("; path=" + this.path)) +
16551            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16552            ((this.secure == true) ? "; secure" : "");
16553     },
16554
16555     // private
16556     clearCookie : function(name){
16557         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16558            ((this.path == null) ? "" : ("; path=" + this.path)) +
16559            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16560            ((this.secure == true) ? "; secure" : "");
16561     }
16562 });/*
16563  * Based on:
16564  * Ext JS Library 1.1.1
16565  * Copyright(c) 2006-2007, Ext JS, LLC.
16566  *
16567  * Originally Released Under LGPL - original licence link has changed is not relivant.
16568  *
16569  * Fork - LGPL
16570  * <script type="text/javascript">
16571  */
16572  
16573
16574 /**
16575  * @class Roo.ComponentMgr
16576  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16577  * @static
16578  */
16579 Roo.ComponentMgr = function(){
16580     var all = new Roo.util.MixedCollection();
16581
16582     return {
16583         /**
16584          * Registers a component.
16585          * @param {Roo.Component} c The component
16586          */
16587         register : function(c){
16588             all.add(c);
16589         },
16590
16591         /**
16592          * Unregisters a component.
16593          * @param {Roo.Component} c The component
16594          */
16595         unregister : function(c){
16596             all.remove(c);
16597         },
16598
16599         /**
16600          * Returns a component by id
16601          * @param {String} id The component id
16602          */
16603         get : function(id){
16604             return all.get(id);
16605         },
16606
16607         /**
16608          * Registers a function that will be called when a specified component is added to ComponentMgr
16609          * @param {String} id The component id
16610          * @param {Funtction} fn The callback function
16611          * @param {Object} scope The scope of the callback
16612          */
16613         onAvailable : function(id, fn, scope){
16614             all.on("add", function(index, o){
16615                 if(o.id == id){
16616                     fn.call(scope || o, o);
16617                     all.un("add", fn, scope);
16618                 }
16619             });
16620         }
16621     };
16622 }();/*
16623  * Based on:
16624  * Ext JS Library 1.1.1
16625  * Copyright(c) 2006-2007, Ext JS, LLC.
16626  *
16627  * Originally Released Under LGPL - original licence link has changed is not relivant.
16628  *
16629  * Fork - LGPL
16630  * <script type="text/javascript">
16631  */
16632  
16633 /**
16634  * @class Roo.Component
16635  * @extends Roo.util.Observable
16636  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16637  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16638  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16639  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16640  * All visual components (widgets) that require rendering into a layout should subclass Component.
16641  * @constructor
16642  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16643  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
16644  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16645  */
16646 Roo.Component = function(config){
16647     config = config || {};
16648     if(config.tagName || config.dom || typeof config == "string"){ // element object
16649         config = {el: config, id: config.id || config};
16650     }
16651     this.initialConfig = config;
16652
16653     Roo.apply(this, config);
16654     this.addEvents({
16655         /**
16656          * @event disable
16657          * Fires after the component is disabled.
16658              * @param {Roo.Component} this
16659              */
16660         disable : true,
16661         /**
16662          * @event enable
16663          * Fires after the component is enabled.
16664              * @param {Roo.Component} this
16665              */
16666         enable : true,
16667         /**
16668          * @event beforeshow
16669          * Fires before the component is shown.  Return false to stop the show.
16670              * @param {Roo.Component} this
16671              */
16672         beforeshow : true,
16673         /**
16674          * @event show
16675          * Fires after the component is shown.
16676              * @param {Roo.Component} this
16677              */
16678         show : true,
16679         /**
16680          * @event beforehide
16681          * Fires before the component is hidden. Return false to stop the hide.
16682              * @param {Roo.Component} this
16683              */
16684         beforehide : true,
16685         /**
16686          * @event hide
16687          * Fires after the component is hidden.
16688              * @param {Roo.Component} this
16689              */
16690         hide : true,
16691         /**
16692          * @event beforerender
16693          * Fires before the component is rendered. Return false to stop the render.
16694              * @param {Roo.Component} this
16695              */
16696         beforerender : true,
16697         /**
16698          * @event render
16699          * Fires after the component is rendered.
16700              * @param {Roo.Component} this
16701              */
16702         render : true,
16703         /**
16704          * @event beforedestroy
16705          * Fires before the component is destroyed. Return false to stop the destroy.
16706              * @param {Roo.Component} this
16707              */
16708         beforedestroy : true,
16709         /**
16710          * @event destroy
16711          * Fires after the component is destroyed.
16712              * @param {Roo.Component} this
16713              */
16714         destroy : true
16715     });
16716     if(!this.id){
16717         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16718     }
16719     Roo.ComponentMgr.register(this);
16720     Roo.Component.superclass.constructor.call(this);
16721     this.initComponent();
16722     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16723         this.render(this.renderTo);
16724         delete this.renderTo;
16725     }
16726 };
16727
16728 /** @private */
16729 Roo.Component.AUTO_ID = 1000;
16730
16731 Roo.extend(Roo.Component, Roo.util.Observable, {
16732     /**
16733      * @scope Roo.Component.prototype
16734      * @type {Boolean}
16735      * true if this component is hidden. Read-only.
16736      */
16737     hidden : false,
16738     /**
16739      * @type {Boolean}
16740      * true if this component is disabled. Read-only.
16741      */
16742     disabled : false,
16743     /**
16744      * @type {Boolean}
16745      * true if this component has been rendered. Read-only.
16746      */
16747     rendered : false,
16748     
16749     /** @cfg {String} disableClass
16750      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16751      */
16752     disabledClass : "x-item-disabled",
16753         /** @cfg {Boolean} allowDomMove
16754          * Whether the component can move the Dom node when rendering (defaults to true).
16755          */
16756     allowDomMove : true,
16757     /** @cfg {String} hideMode (display|visibility)
16758      * How this component should hidden. Supported values are
16759      * "visibility" (css visibility), "offsets" (negative offset position) and
16760      * "display" (css display) - defaults to "display".
16761      */
16762     hideMode: 'display',
16763
16764     /** @private */
16765     ctype : "Roo.Component",
16766
16767     /**
16768      * @cfg {String} actionMode 
16769      * which property holds the element that used for  hide() / show() / disable() / enable()
16770      * default is 'el' for forms you probably want to set this to fieldEl 
16771      */
16772     actionMode : "el",
16773
16774     /** @private */
16775     getActionEl : function(){
16776         return this[this.actionMode];
16777     },
16778
16779     initComponent : Roo.emptyFn,
16780     /**
16781      * If this is a lazy rendering component, render it to its container element.
16782      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
16783      */
16784     render : function(container, position){
16785         
16786         if(this.rendered){
16787             return this;
16788         }
16789         
16790         if(this.fireEvent("beforerender", this) === false){
16791             return false;
16792         }
16793         
16794         if(!container && this.el){
16795             this.el = Roo.get(this.el);
16796             container = this.el.dom.parentNode;
16797             this.allowDomMove = false;
16798         }
16799         this.container = Roo.get(container);
16800         this.rendered = true;
16801         if(position !== undefined){
16802             if(typeof position == 'number'){
16803                 position = this.container.dom.childNodes[position];
16804             }else{
16805                 position = Roo.getDom(position);
16806             }
16807         }
16808         this.onRender(this.container, position || null);
16809         if(this.cls){
16810             this.el.addClass(this.cls);
16811             delete this.cls;
16812         }
16813         if(this.style){
16814             this.el.applyStyles(this.style);
16815             delete this.style;
16816         }
16817         this.fireEvent("render", this);
16818         this.afterRender(this.container);
16819         if(this.hidden){
16820             this.hide();
16821         }
16822         if(this.disabled){
16823             this.disable();
16824         }
16825
16826         return this;
16827         
16828     },
16829
16830     /** @private */
16831     // default function is not really useful
16832     onRender : function(ct, position){
16833         if(this.el){
16834             this.el = Roo.get(this.el);
16835             if(this.allowDomMove !== false){
16836                 ct.dom.insertBefore(this.el.dom, position);
16837             }
16838         }
16839     },
16840
16841     /** @private */
16842     getAutoCreate : function(){
16843         var cfg = typeof this.autoCreate == "object" ?
16844                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16845         if(this.id && !cfg.id){
16846             cfg.id = this.id;
16847         }
16848         return cfg;
16849     },
16850
16851     /** @private */
16852     afterRender : Roo.emptyFn,
16853
16854     /**
16855      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16856      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16857      */
16858     destroy : function(){
16859         if(this.fireEvent("beforedestroy", this) !== false){
16860             this.purgeListeners();
16861             this.beforeDestroy();
16862             if(this.rendered){
16863                 this.el.removeAllListeners();
16864                 this.el.remove();
16865                 if(this.actionMode == "container"){
16866                     this.container.remove();
16867                 }
16868             }
16869             this.onDestroy();
16870             Roo.ComponentMgr.unregister(this);
16871             this.fireEvent("destroy", this);
16872         }
16873     },
16874
16875         /** @private */
16876     beforeDestroy : function(){
16877
16878     },
16879
16880         /** @private */
16881         onDestroy : function(){
16882
16883     },
16884
16885     /**
16886      * Returns the underlying {@link Roo.Element}.
16887      * @return {Roo.Element} The element
16888      */
16889     getEl : function(){
16890         return this.el;
16891     },
16892
16893     /**
16894      * Returns the id of this component.
16895      * @return {String}
16896      */
16897     getId : function(){
16898         return this.id;
16899     },
16900
16901     /**
16902      * Try to focus this component.
16903      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16904      * @return {Roo.Component} this
16905      */
16906     focus : function(selectText){
16907         if(this.rendered){
16908             this.el.focus();
16909             if(selectText === true){
16910                 this.el.dom.select();
16911             }
16912         }
16913         return this;
16914     },
16915
16916     /** @private */
16917     blur : function(){
16918         if(this.rendered){
16919             this.el.blur();
16920         }
16921         return this;
16922     },
16923
16924     /**
16925      * Disable this component.
16926      * @return {Roo.Component} this
16927      */
16928     disable : function(){
16929         if(this.rendered){
16930             this.onDisable();
16931         }
16932         this.disabled = true;
16933         this.fireEvent("disable", this);
16934         return this;
16935     },
16936
16937         // private
16938     onDisable : function(){
16939         this.getActionEl().addClass(this.disabledClass);
16940         this.el.dom.disabled = true;
16941     },
16942
16943     /**
16944      * Enable this component.
16945      * @return {Roo.Component} this
16946      */
16947     enable : function(){
16948         if(this.rendered){
16949             this.onEnable();
16950         }
16951         this.disabled = false;
16952         this.fireEvent("enable", this);
16953         return this;
16954     },
16955
16956         // private
16957     onEnable : function(){
16958         this.getActionEl().removeClass(this.disabledClass);
16959         this.el.dom.disabled = false;
16960     },
16961
16962     /**
16963      * Convenience function for setting disabled/enabled by boolean.
16964      * @param {Boolean} disabled
16965      */
16966     setDisabled : function(disabled){
16967         this[disabled ? "disable" : "enable"]();
16968     },
16969
16970     /**
16971      * Show this component.
16972      * @return {Roo.Component} this
16973      */
16974     show: function(){
16975         if(this.fireEvent("beforeshow", this) !== false){
16976             this.hidden = false;
16977             if(this.rendered){
16978                 this.onShow();
16979             }
16980             this.fireEvent("show", this);
16981         }
16982         return this;
16983     },
16984
16985     // private
16986     onShow : function(){
16987         var ae = this.getActionEl();
16988         if(this.hideMode == 'visibility'){
16989             ae.dom.style.visibility = "visible";
16990         }else if(this.hideMode == 'offsets'){
16991             ae.removeClass('x-hidden');
16992         }else{
16993             ae.dom.style.display = "";
16994         }
16995     },
16996
16997     /**
16998      * Hide this component.
16999      * @return {Roo.Component} this
17000      */
17001     hide: function(){
17002         if(this.fireEvent("beforehide", this) !== false){
17003             this.hidden = true;
17004             if(this.rendered){
17005                 this.onHide();
17006             }
17007             this.fireEvent("hide", this);
17008         }
17009         return this;
17010     },
17011
17012     // private
17013     onHide : function(){
17014         var ae = this.getActionEl();
17015         if(this.hideMode == 'visibility'){
17016             ae.dom.style.visibility = "hidden";
17017         }else if(this.hideMode == 'offsets'){
17018             ae.addClass('x-hidden');
17019         }else{
17020             ae.dom.style.display = "none";
17021         }
17022     },
17023
17024     /**
17025      * Convenience function to hide or show this component by boolean.
17026      * @param {Boolean} visible True to show, false to hide
17027      * @return {Roo.Component} this
17028      */
17029     setVisible: function(visible){
17030         if(visible) {
17031             this.show();
17032         }else{
17033             this.hide();
17034         }
17035         return this;
17036     },
17037
17038     /**
17039      * Returns true if this component is visible.
17040      */
17041     isVisible : function(){
17042         return this.getActionEl().isVisible();
17043     },
17044
17045     cloneConfig : function(overrides){
17046         overrides = overrides || {};
17047         var id = overrides.id || Roo.id();
17048         var cfg = Roo.applyIf(overrides, this.initialConfig);
17049         cfg.id = id; // prevent dup id
17050         return new this.constructor(cfg);
17051     }
17052 });/*
17053  * Based on:
17054  * Ext JS Library 1.1.1
17055  * Copyright(c) 2006-2007, Ext JS, LLC.
17056  *
17057  * Originally Released Under LGPL - original licence link has changed is not relivant.
17058  *
17059  * Fork - LGPL
17060  * <script type="text/javascript">
17061  */
17062
17063 /**
17064  * @class Roo.BoxComponent
17065  * @extends Roo.Component
17066  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17067  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17068  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17069  * layout containers.
17070  * @constructor
17071  * @param {Roo.Element/String/Object} config The configuration options.
17072  */
17073 Roo.BoxComponent = function(config){
17074     Roo.Component.call(this, config);
17075     this.addEvents({
17076         /**
17077          * @event resize
17078          * Fires after the component is resized.
17079              * @param {Roo.Component} this
17080              * @param {Number} adjWidth The box-adjusted width that was set
17081              * @param {Number} adjHeight The box-adjusted height that was set
17082              * @param {Number} rawWidth The width that was originally specified
17083              * @param {Number} rawHeight The height that was originally specified
17084              */
17085         resize : true,
17086         /**
17087          * @event move
17088          * Fires after the component is moved.
17089              * @param {Roo.Component} this
17090              * @param {Number} x The new x position
17091              * @param {Number} y The new y position
17092              */
17093         move : true
17094     });
17095 };
17096
17097 Roo.extend(Roo.BoxComponent, Roo.Component, {
17098     // private, set in afterRender to signify that the component has been rendered
17099     boxReady : false,
17100     // private, used to defer height settings to subclasses
17101     deferHeight: false,
17102     /** @cfg {Number} width
17103      * width (optional) size of component
17104      */
17105      /** @cfg {Number} height
17106      * height (optional) size of component
17107      */
17108      
17109     /**
17110      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17111      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17112      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17113      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17114      * @return {Roo.BoxComponent} this
17115      */
17116     setSize : function(w, h){
17117         // support for standard size objects
17118         if(typeof w == 'object'){
17119             h = w.height;
17120             w = w.width;
17121         }
17122         // not rendered
17123         if(!this.boxReady){
17124             this.width = w;
17125             this.height = h;
17126             return this;
17127         }
17128
17129         // prevent recalcs when not needed
17130         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17131             return this;
17132         }
17133         this.lastSize = {width: w, height: h};
17134
17135         var adj = this.adjustSize(w, h);
17136         var aw = adj.width, ah = adj.height;
17137         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17138             var rz = this.getResizeEl();
17139             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17140                 rz.setSize(aw, ah);
17141             }else if(!this.deferHeight && ah !== undefined){
17142                 rz.setHeight(ah);
17143             }else if(aw !== undefined){
17144                 rz.setWidth(aw);
17145             }
17146             this.onResize(aw, ah, w, h);
17147             this.fireEvent('resize', this, aw, ah, w, h);
17148         }
17149         return this;
17150     },
17151
17152     /**
17153      * Gets the current size of the component's underlying element.
17154      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17155      */
17156     getSize : function(){
17157         return this.el.getSize();
17158     },
17159
17160     /**
17161      * Gets the current XY position of the component's underlying element.
17162      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17163      * @return {Array} The XY position of the element (e.g., [100, 200])
17164      */
17165     getPosition : function(local){
17166         if(local === true){
17167             return [this.el.getLeft(true), this.el.getTop(true)];
17168         }
17169         return this.xy || this.el.getXY();
17170     },
17171
17172     /**
17173      * Gets the current box measurements of the component's underlying element.
17174      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17175      * @returns {Object} box An object in the format {x, y, width, height}
17176      */
17177     getBox : function(local){
17178         var s = this.el.getSize();
17179         if(local){
17180             s.x = this.el.getLeft(true);
17181             s.y = this.el.getTop(true);
17182         }else{
17183             var xy = this.xy || this.el.getXY();
17184             s.x = xy[0];
17185             s.y = xy[1];
17186         }
17187         return s;
17188     },
17189
17190     /**
17191      * Sets the current box measurements of the component's underlying element.
17192      * @param {Object} box An object in the format {x, y, width, height}
17193      * @returns {Roo.BoxComponent} this
17194      */
17195     updateBox : function(box){
17196         this.setSize(box.width, box.height);
17197         this.setPagePosition(box.x, box.y);
17198         return this;
17199     },
17200
17201     // protected
17202     getResizeEl : function(){
17203         return this.resizeEl || this.el;
17204     },
17205
17206     // protected
17207     getPositionEl : function(){
17208         return this.positionEl || this.el;
17209     },
17210
17211     /**
17212      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17213      * This method fires the move event.
17214      * @param {Number} left The new left
17215      * @param {Number} top The new top
17216      * @returns {Roo.BoxComponent} this
17217      */
17218     setPosition : function(x, y){
17219         this.x = x;
17220         this.y = y;
17221         if(!this.boxReady){
17222             return this;
17223         }
17224         var adj = this.adjustPosition(x, y);
17225         var ax = adj.x, ay = adj.y;
17226
17227         var el = this.getPositionEl();
17228         if(ax !== undefined || ay !== undefined){
17229             if(ax !== undefined && ay !== undefined){
17230                 el.setLeftTop(ax, ay);
17231             }else if(ax !== undefined){
17232                 el.setLeft(ax);
17233             }else if(ay !== undefined){
17234                 el.setTop(ay);
17235             }
17236             this.onPosition(ax, ay);
17237             this.fireEvent('move', this, ax, ay);
17238         }
17239         return this;
17240     },
17241
17242     /**
17243      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17244      * This method fires the move event.
17245      * @param {Number} x The new x position
17246      * @param {Number} y The new y position
17247      * @returns {Roo.BoxComponent} this
17248      */
17249     setPagePosition : function(x, y){
17250         this.pageX = x;
17251         this.pageY = y;
17252         if(!this.boxReady){
17253             return;
17254         }
17255         if(x === undefined || y === undefined){ // cannot translate undefined points
17256             return;
17257         }
17258         var p = this.el.translatePoints(x, y);
17259         this.setPosition(p.left, p.top);
17260         return this;
17261     },
17262
17263     // private
17264     onRender : function(ct, position){
17265         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17266         if(this.resizeEl){
17267             this.resizeEl = Roo.get(this.resizeEl);
17268         }
17269         if(this.positionEl){
17270             this.positionEl = Roo.get(this.positionEl);
17271         }
17272     },
17273
17274     // private
17275     afterRender : function(){
17276         Roo.BoxComponent.superclass.afterRender.call(this);
17277         this.boxReady = true;
17278         this.setSize(this.width, this.height);
17279         if(this.x || this.y){
17280             this.setPosition(this.x, this.y);
17281         }
17282         if(this.pageX || this.pageY){
17283             this.setPagePosition(this.pageX, this.pageY);
17284         }
17285     },
17286
17287     /**
17288      * Force the component's size to recalculate based on the underlying element's current height and width.
17289      * @returns {Roo.BoxComponent} this
17290      */
17291     syncSize : function(){
17292         delete this.lastSize;
17293         this.setSize(this.el.getWidth(), this.el.getHeight());
17294         return this;
17295     },
17296
17297     /**
17298      * Called after the component is resized, this method is empty by default but can be implemented by any
17299      * subclass that needs to perform custom logic after a resize occurs.
17300      * @param {Number} adjWidth The box-adjusted width that was set
17301      * @param {Number} adjHeight The box-adjusted height that was set
17302      * @param {Number} rawWidth The width that was originally specified
17303      * @param {Number} rawHeight The height that was originally specified
17304      */
17305     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17306
17307     },
17308
17309     /**
17310      * Called after the component is moved, this method is empty by default but can be implemented by any
17311      * subclass that needs to perform custom logic after a move occurs.
17312      * @param {Number} x The new x position
17313      * @param {Number} y The new y position
17314      */
17315     onPosition : function(x, y){
17316
17317     },
17318
17319     // private
17320     adjustSize : function(w, h){
17321         if(this.autoWidth){
17322             w = 'auto';
17323         }
17324         if(this.autoHeight){
17325             h = 'auto';
17326         }
17327         return {width : w, height: h};
17328     },
17329
17330     // private
17331     adjustPosition : function(x, y){
17332         return {x : x, y: y};
17333     }
17334 });/*
17335  * Based on:
17336  * Ext JS Library 1.1.1
17337  * Copyright(c) 2006-2007, Ext JS, LLC.
17338  *
17339  * Originally Released Under LGPL - original licence link has changed is not relivant.
17340  *
17341  * Fork - LGPL
17342  * <script type="text/javascript">
17343  */
17344  (function(){ 
17345 /**
17346  * @class Roo.Layer
17347  * @extends Roo.Element
17348  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17349  * automatic maintaining of shadow/shim positions.
17350  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17351  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17352  * you can pass a string with a CSS class name. False turns off the shadow.
17353  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17354  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17355  * @cfg {String} cls CSS class to add to the element
17356  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17357  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17358  * @constructor
17359  * @param {Object} config An object with config options.
17360  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17361  */
17362
17363 Roo.Layer = function(config, existingEl){
17364     config = config || {};
17365     var dh = Roo.DomHelper;
17366     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17367     if(existingEl){
17368         this.dom = Roo.getDom(existingEl);
17369     }
17370     if(!this.dom){
17371         var o = config.dh || {tag: "div", cls: "x-layer"};
17372         this.dom = dh.append(pel, o);
17373     }
17374     if(config.cls){
17375         this.addClass(config.cls);
17376     }
17377     this.constrain = config.constrain !== false;
17378     this.visibilityMode = Roo.Element.VISIBILITY;
17379     if(config.id){
17380         this.id = this.dom.id = config.id;
17381     }else{
17382         this.id = Roo.id(this.dom);
17383     }
17384     this.zindex = config.zindex || this.getZIndex();
17385     this.position("absolute", this.zindex);
17386     if(config.shadow){
17387         this.shadowOffset = config.shadowOffset || 4;
17388         this.shadow = new Roo.Shadow({
17389             offset : this.shadowOffset,
17390             mode : config.shadow
17391         });
17392     }else{
17393         this.shadowOffset = 0;
17394     }
17395     this.useShim = config.shim !== false && Roo.useShims;
17396     this.useDisplay = config.useDisplay;
17397     this.hide();
17398 };
17399
17400 var supr = Roo.Element.prototype;
17401
17402 // shims are shared among layer to keep from having 100 iframes
17403 var shims = [];
17404
17405 Roo.extend(Roo.Layer, Roo.Element, {
17406
17407     getZIndex : function(){
17408         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17409     },
17410
17411     getShim : function(){
17412         if(!this.useShim){
17413             return null;
17414         }
17415         if(this.shim){
17416             return this.shim;
17417         }
17418         var shim = shims.shift();
17419         if(!shim){
17420             shim = this.createShim();
17421             shim.enableDisplayMode('block');
17422             shim.dom.style.display = 'none';
17423             shim.dom.style.visibility = 'visible';
17424         }
17425         var pn = this.dom.parentNode;
17426         if(shim.dom.parentNode != pn){
17427             pn.insertBefore(shim.dom, this.dom);
17428         }
17429         shim.setStyle('z-index', this.getZIndex()-2);
17430         this.shim = shim;
17431         return shim;
17432     },
17433
17434     hideShim : function(){
17435         if(this.shim){
17436             this.shim.setDisplayed(false);
17437             shims.push(this.shim);
17438             delete this.shim;
17439         }
17440     },
17441
17442     disableShadow : function(){
17443         if(this.shadow){
17444             this.shadowDisabled = true;
17445             this.shadow.hide();
17446             this.lastShadowOffset = this.shadowOffset;
17447             this.shadowOffset = 0;
17448         }
17449     },
17450
17451     enableShadow : function(show){
17452         if(this.shadow){
17453             this.shadowDisabled = false;
17454             this.shadowOffset = this.lastShadowOffset;
17455             delete this.lastShadowOffset;
17456             if(show){
17457                 this.sync(true);
17458             }
17459         }
17460     },
17461
17462     // private
17463     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17464     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17465     sync : function(doShow){
17466         var sw = this.shadow;
17467         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17468             var sh = this.getShim();
17469
17470             var w = this.getWidth(),
17471                 h = this.getHeight();
17472
17473             var l = this.getLeft(true),
17474                 t = this.getTop(true);
17475
17476             if(sw && !this.shadowDisabled){
17477                 if(doShow && !sw.isVisible()){
17478                     sw.show(this);
17479                 }else{
17480                     sw.realign(l, t, w, h);
17481                 }
17482                 if(sh){
17483                     if(doShow){
17484                        sh.show();
17485                     }
17486                     // fit the shim behind the shadow, so it is shimmed too
17487                     var a = sw.adjusts, s = sh.dom.style;
17488                     s.left = (Math.min(l, l+a.l))+"px";
17489                     s.top = (Math.min(t, t+a.t))+"px";
17490                     s.width = (w+a.w)+"px";
17491                     s.height = (h+a.h)+"px";
17492                 }
17493             }else if(sh){
17494                 if(doShow){
17495                    sh.show();
17496                 }
17497                 sh.setSize(w, h);
17498                 sh.setLeftTop(l, t);
17499             }
17500             
17501         }
17502     },
17503
17504     // private
17505     destroy : function(){
17506         this.hideShim();
17507         if(this.shadow){
17508             this.shadow.hide();
17509         }
17510         this.removeAllListeners();
17511         var pn = this.dom.parentNode;
17512         if(pn){
17513             pn.removeChild(this.dom);
17514         }
17515         Roo.Element.uncache(this.id);
17516     },
17517
17518     remove : function(){
17519         this.destroy();
17520     },
17521
17522     // private
17523     beginUpdate : function(){
17524         this.updating = true;
17525     },
17526
17527     // private
17528     endUpdate : function(){
17529         this.updating = false;
17530         this.sync(true);
17531     },
17532
17533     // private
17534     hideUnders : function(negOffset){
17535         if(this.shadow){
17536             this.shadow.hide();
17537         }
17538         this.hideShim();
17539     },
17540
17541     // private
17542     constrainXY : function(){
17543         if(this.constrain){
17544             var vw = Roo.lib.Dom.getViewWidth(),
17545                 vh = Roo.lib.Dom.getViewHeight();
17546             var s = Roo.get(document).getScroll();
17547
17548             var xy = this.getXY();
17549             var x = xy[0], y = xy[1];   
17550             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17551             // only move it if it needs it
17552             var moved = false;
17553             // first validate right/bottom
17554             if((x + w) > vw+s.left){
17555                 x = vw - w - this.shadowOffset;
17556                 moved = true;
17557             }
17558             if((y + h) > vh+s.top){
17559                 y = vh - h - this.shadowOffset;
17560                 moved = true;
17561             }
17562             // then make sure top/left isn't negative
17563             if(x < s.left){
17564                 x = s.left;
17565                 moved = true;
17566             }
17567             if(y < s.top){
17568                 y = s.top;
17569                 moved = true;
17570             }
17571             if(moved){
17572                 if(this.avoidY){
17573                     var ay = this.avoidY;
17574                     if(y <= ay && (y+h) >= ay){
17575                         y = ay-h-5;   
17576                     }
17577                 }
17578                 xy = [x, y];
17579                 this.storeXY(xy);
17580                 supr.setXY.call(this, xy);
17581                 this.sync();
17582             }
17583         }
17584     },
17585
17586     isVisible : function(){
17587         return this.visible;    
17588     },
17589
17590     // private
17591     showAction : function(){
17592         this.visible = true; // track visibility to prevent getStyle calls
17593         if(this.useDisplay === true){
17594             this.setDisplayed("");
17595         }else if(this.lastXY){
17596             supr.setXY.call(this, this.lastXY);
17597         }else if(this.lastLT){
17598             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17599         }
17600     },
17601
17602     // private
17603     hideAction : function(){
17604         this.visible = false;
17605         if(this.useDisplay === true){
17606             this.setDisplayed(false);
17607         }else{
17608             this.setLeftTop(-10000,-10000);
17609         }
17610     },
17611
17612     // overridden Element method
17613     setVisible : function(v, a, d, c, e){
17614         if(v){
17615             this.showAction();
17616         }
17617         if(a && v){
17618             var cb = function(){
17619                 this.sync(true);
17620                 if(c){
17621                     c();
17622                 }
17623             }.createDelegate(this);
17624             supr.setVisible.call(this, true, true, d, cb, e);
17625         }else{
17626             if(!v){
17627                 this.hideUnders(true);
17628             }
17629             var cb = c;
17630             if(a){
17631                 cb = function(){
17632                     this.hideAction();
17633                     if(c){
17634                         c();
17635                     }
17636                 }.createDelegate(this);
17637             }
17638             supr.setVisible.call(this, v, a, d, cb, e);
17639             if(v){
17640                 this.sync(true);
17641             }else if(!a){
17642                 this.hideAction();
17643             }
17644         }
17645     },
17646
17647     storeXY : function(xy){
17648         delete this.lastLT;
17649         this.lastXY = xy;
17650     },
17651
17652     storeLeftTop : function(left, top){
17653         delete this.lastXY;
17654         this.lastLT = [left, top];
17655     },
17656
17657     // private
17658     beforeFx : function(){
17659         this.beforeAction();
17660         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17661     },
17662
17663     // private
17664     afterFx : function(){
17665         Roo.Layer.superclass.afterFx.apply(this, arguments);
17666         this.sync(this.isVisible());
17667     },
17668
17669     // private
17670     beforeAction : function(){
17671         if(!this.updating && this.shadow){
17672             this.shadow.hide();
17673         }
17674     },
17675
17676     // overridden Element method
17677     setLeft : function(left){
17678         this.storeLeftTop(left, this.getTop(true));
17679         supr.setLeft.apply(this, arguments);
17680         this.sync();
17681     },
17682
17683     setTop : function(top){
17684         this.storeLeftTop(this.getLeft(true), top);
17685         supr.setTop.apply(this, arguments);
17686         this.sync();
17687     },
17688
17689     setLeftTop : function(left, top){
17690         this.storeLeftTop(left, top);
17691         supr.setLeftTop.apply(this, arguments);
17692         this.sync();
17693     },
17694
17695     setXY : function(xy, a, d, c, e){
17696         this.fixDisplay();
17697         this.beforeAction();
17698         this.storeXY(xy);
17699         var cb = this.createCB(c);
17700         supr.setXY.call(this, xy, a, d, cb, e);
17701         if(!a){
17702             cb();
17703         }
17704     },
17705
17706     // private
17707     createCB : function(c){
17708         var el = this;
17709         return function(){
17710             el.constrainXY();
17711             el.sync(true);
17712             if(c){
17713                 c();
17714             }
17715         };
17716     },
17717
17718     // overridden Element method
17719     setX : function(x, a, d, c, e){
17720         this.setXY([x, this.getY()], a, d, c, e);
17721     },
17722
17723     // overridden Element method
17724     setY : function(y, a, d, c, e){
17725         this.setXY([this.getX(), y], a, d, c, e);
17726     },
17727
17728     // overridden Element method
17729     setSize : function(w, h, a, d, c, e){
17730         this.beforeAction();
17731         var cb = this.createCB(c);
17732         supr.setSize.call(this, w, h, a, d, cb, e);
17733         if(!a){
17734             cb();
17735         }
17736     },
17737
17738     // overridden Element method
17739     setWidth : function(w, a, d, c, e){
17740         this.beforeAction();
17741         var cb = this.createCB(c);
17742         supr.setWidth.call(this, w, a, d, cb, e);
17743         if(!a){
17744             cb();
17745         }
17746     },
17747
17748     // overridden Element method
17749     setHeight : function(h, a, d, c, e){
17750         this.beforeAction();
17751         var cb = this.createCB(c);
17752         supr.setHeight.call(this, h, a, d, cb, e);
17753         if(!a){
17754             cb();
17755         }
17756     },
17757
17758     // overridden Element method
17759     setBounds : function(x, y, w, h, a, d, c, e){
17760         this.beforeAction();
17761         var cb = this.createCB(c);
17762         if(!a){
17763             this.storeXY([x, y]);
17764             supr.setXY.call(this, [x, y]);
17765             supr.setSize.call(this, w, h, a, d, cb, e);
17766             cb();
17767         }else{
17768             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17769         }
17770         return this;
17771     },
17772     
17773     /**
17774      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17775      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17776      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17777      * @param {Number} zindex The new z-index to set
17778      * @return {this} The Layer
17779      */
17780     setZIndex : function(zindex){
17781         this.zindex = zindex;
17782         this.setStyle("z-index", zindex + 2);
17783         if(this.shadow){
17784             this.shadow.setZIndex(zindex + 1);
17785         }
17786         if(this.shim){
17787             this.shim.setStyle("z-index", zindex);
17788         }
17789     }
17790 });
17791 })();/*
17792  * Original code for Roojs - LGPL
17793  * <script type="text/javascript">
17794  */
17795  
17796 /**
17797  * @class Roo.XComponent
17798  * A delayed Element creator...
17799  * Or a way to group chunks of interface together.
17800  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17801  *  used in conjunction with XComponent.build() it will create an instance of each element,
17802  *  then call addxtype() to build the User interface.
17803  * 
17804  * Mypart.xyx = new Roo.XComponent({
17805
17806     parent : 'Mypart.xyz', // empty == document.element.!!
17807     order : '001',
17808     name : 'xxxx'
17809     region : 'xxxx'
17810     disabled : function() {} 
17811      
17812     tree : function() { // return an tree of xtype declared components
17813         var MODULE = this;
17814         return 
17815         {
17816             xtype : 'NestedLayoutPanel',
17817             // technicall
17818         }
17819      ]
17820  *})
17821  *
17822  *
17823  * It can be used to build a big heiracy, with parent etc.
17824  * or you can just use this to render a single compoent to a dom element
17825  * MYPART.render(Roo.Element | String(id) | dom_element )
17826  *
17827  *
17828  * Usage patterns.
17829  *
17830  * Classic Roo
17831  *
17832  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17833  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17834  *
17835  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17836  *
17837  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17838  * - if mulitple topModules exist, the last one is defined as the top module.
17839  *
17840  * Embeded Roo
17841  * 
17842  * When the top level or multiple modules are to embedded into a existing HTML page,
17843  * the parent element can container '#id' of the element where the module will be drawn.
17844  *
17845  * Bootstrap Roo
17846  *
17847  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17848  * it relies more on a include mechanism, where sub modules are included into an outer page.
17849  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17850  * 
17851  * Bootstrap Roo Included elements
17852  *
17853  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17854  * hence confusing the component builder as it thinks there are multiple top level elements. 
17855  *
17856  * String Over-ride & Translations
17857  *
17858  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17859  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17860  * are needed. @see Roo.XComponent.overlayString  
17861  * 
17862  * 
17863  * 
17864  * @extends Roo.util.Observable
17865  * @constructor
17866  * @param cfg {Object} configuration of component
17867  * 
17868  */
17869 Roo.XComponent = function(cfg) {
17870     Roo.apply(this, cfg);
17871     this.addEvents({ 
17872         /**
17873              * @event built
17874              * Fires when this the componnt is built
17875              * @param {Roo.XComponent} c the component
17876              */
17877         'built' : true
17878         
17879     });
17880     this.region = this.region || 'center'; // default..
17881     Roo.XComponent.register(this);
17882     this.modules = false;
17883     this.el = false; // where the layout goes..
17884     
17885     
17886 }
17887 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17888     /**
17889      * @property el
17890      * The created element (with Roo.factory())
17891      * @type {Roo.Layout}
17892      */
17893     el  : false,
17894     
17895     /**
17896      * @property el
17897      * for BC  - use el in new code
17898      * @type {Roo.Layout}
17899      */
17900     panel : false,
17901     
17902     /**
17903      * @property layout
17904      * for BC  - use el in new code
17905      * @type {Roo.Layout}
17906      */
17907     layout : false,
17908     
17909      /**
17910      * @cfg {Function|boolean} disabled
17911      * If this module is disabled by some rule, return true from the funtion
17912      */
17913     disabled : false,
17914     
17915     /**
17916      * @cfg {String} parent 
17917      * Name of parent element which it get xtype added to..
17918      */
17919     parent: false,
17920     
17921     /**
17922      * @cfg {String} order
17923      * Used to set the order in which elements are created (usefull for multiple tabs)
17924      */
17925     
17926     order : false,
17927     /**
17928      * @cfg {String} name
17929      * String to display while loading.
17930      */
17931     name : false,
17932     /**
17933      * @cfg {String} region
17934      * Region to render component to (defaults to center)
17935      */
17936     region : 'center',
17937     
17938     /**
17939      * @cfg {Array} items
17940      * A single item array - the first element is the root of the tree..
17941      * It's done this way to stay compatible with the Xtype system...
17942      */
17943     items : false,
17944     
17945     /**
17946      * @property _tree
17947      * The method that retuns the tree of parts that make up this compoennt 
17948      * @type {function}
17949      */
17950     _tree  : false,
17951     
17952      /**
17953      * render
17954      * render element to dom or tree
17955      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17956      */
17957     
17958     render : function(el)
17959     {
17960         
17961         el = el || false;
17962         var hp = this.parent ? 1 : 0;
17963         Roo.debug &&  Roo.log(this);
17964         
17965         var tree = this._tree ? this._tree() : this.tree();
17966
17967         
17968         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17969             // if parent is a '#.....' string, then let's use that..
17970             var ename = this.parent.substr(1);
17971             this.parent = false;
17972             Roo.debug && Roo.log(ename);
17973             switch (ename) {
17974                 case 'bootstrap-body':
17975                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17976                         // this is the BorderLayout standard?
17977                        this.parent = { el : true };
17978                        break;
17979                     }
17980                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17981                         // need to insert stuff...
17982                         this.parent =  {
17983                              el : new Roo.bootstrap.layout.Border({
17984                                  el : document.body, 
17985                      
17986                                  center: {
17987                                     titlebar: false,
17988                                     autoScroll:false,
17989                                     closeOnTab: true,
17990                                     tabPosition: 'top',
17991                                       //resizeTabs: true,
17992                                     alwaysShowTabs: true,
17993                                     hideTabs: false
17994                                      //minTabWidth: 140
17995                                  }
17996                              })
17997                         
17998                          };
17999                          break;
18000                     }
18001                          
18002                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18003                         this.parent = { el :  new  Roo.bootstrap.Body() };
18004                         Roo.debug && Roo.log("setting el to doc body");
18005                          
18006                     } else {
18007                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18008                     }
18009                     break;
18010                 case 'bootstrap':
18011                     this.parent = { el : true};
18012                     // fall through
18013                 default:
18014                     el = Roo.get(ename);
18015                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18016                         this.parent = { el : true};
18017                     }
18018                     
18019                     break;
18020             }
18021                 
18022             
18023             if (!el && !this.parent) {
18024                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18025                 return;
18026             }
18027         }
18028         
18029         Roo.debug && Roo.log("EL:");
18030         Roo.debug && Roo.log(el);
18031         Roo.debug && Roo.log("this.parent.el:");
18032         Roo.debug && Roo.log(this.parent.el);
18033         
18034
18035         // altertive root elements ??? - we need a better way to indicate these.
18036         var is_alt = Roo.XComponent.is_alt ||
18037                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18038                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18039                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18040         
18041         
18042         
18043         if (!this.parent && is_alt) {
18044             //el = Roo.get(document.body);
18045             this.parent = { el : true };
18046         }
18047             
18048             
18049         
18050         if (!this.parent) {
18051             
18052             Roo.debug && Roo.log("no parent - creating one");
18053             
18054             el = el ? Roo.get(el) : false;      
18055             
18056             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18057                 
18058                 this.parent =  {
18059                     el : new Roo.bootstrap.layout.Border({
18060                         el: el || document.body,
18061                     
18062                         center: {
18063                             titlebar: false,
18064                             autoScroll:false,
18065                             closeOnTab: true,
18066                             tabPosition: 'top',
18067                              //resizeTabs: true,
18068                             alwaysShowTabs: false,
18069                             hideTabs: true,
18070                             minTabWidth: 140,
18071                             overflow: 'visible'
18072                          }
18073                      })
18074                 };
18075             } else {
18076             
18077                 // it's a top level one..
18078                 this.parent =  {
18079                     el : new Roo.BorderLayout(el || document.body, {
18080                         center: {
18081                             titlebar: false,
18082                             autoScroll:false,
18083                             closeOnTab: true,
18084                             tabPosition: 'top',
18085                              //resizeTabs: true,
18086                             alwaysShowTabs: el && hp? false :  true,
18087                             hideTabs: el || !hp ? true :  false,
18088                             minTabWidth: 140
18089                          }
18090                     })
18091                 };
18092             }
18093         }
18094         
18095         if (!this.parent.el) {
18096                 // probably an old style ctor, which has been disabled.
18097                 return;
18098
18099         }
18100                 // The 'tree' method is  '_tree now' 
18101             
18102         tree.region = tree.region || this.region;
18103         var is_body = false;
18104         if (this.parent.el === true) {
18105             // bootstrap... - body..
18106             if (el) {
18107                 tree.el = el;
18108             }
18109             this.parent.el = Roo.factory(tree);
18110             is_body = true;
18111         }
18112         
18113         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18114         this.fireEvent('built', this);
18115         
18116         this.panel = this.el;
18117         this.layout = this.panel.layout;
18118         this.parentLayout = this.parent.layout  || false;  
18119          
18120     }
18121     
18122 });
18123
18124 Roo.apply(Roo.XComponent, {
18125     /**
18126      * @property  hideProgress
18127      * true to disable the building progress bar.. usefull on single page renders.
18128      * @type Boolean
18129      */
18130     hideProgress : false,
18131     /**
18132      * @property  buildCompleted
18133      * True when the builder has completed building the interface.
18134      * @type Boolean
18135      */
18136     buildCompleted : false,
18137      
18138     /**
18139      * @property  topModule
18140      * the upper most module - uses document.element as it's constructor.
18141      * @type Object
18142      */
18143      
18144     topModule  : false,
18145       
18146     /**
18147      * @property  modules
18148      * array of modules to be created by registration system.
18149      * @type {Array} of Roo.XComponent
18150      */
18151     
18152     modules : [],
18153     /**
18154      * @property  elmodules
18155      * array of modules to be created by which use #ID 
18156      * @type {Array} of Roo.XComponent
18157      */
18158      
18159     elmodules : [],
18160
18161      /**
18162      * @property  is_alt
18163      * Is an alternative Root - normally used by bootstrap or other systems,
18164      *    where the top element in the tree can wrap 'body' 
18165      * @type {boolean}  (default false)
18166      */
18167      
18168     is_alt : false,
18169     /**
18170      * @property  build_from_html
18171      * Build elements from html - used by bootstrap HTML stuff 
18172      *    - this is cleared after build is completed
18173      * @type {boolean}    (default false)
18174      */
18175      
18176     build_from_html : false,
18177     /**
18178      * Register components to be built later.
18179      *
18180      * This solves the following issues
18181      * - Building is not done on page load, but after an authentication process has occured.
18182      * - Interface elements are registered on page load
18183      * - Parent Interface elements may not be loaded before child, so this handles that..
18184      * 
18185      *
18186      * example:
18187      * 
18188      * MyApp.register({
18189           order : '000001',
18190           module : 'Pman.Tab.projectMgr',
18191           region : 'center',
18192           parent : 'Pman.layout',
18193           disabled : false,  // or use a function..
18194         })
18195      
18196      * * @param {Object} details about module
18197      */
18198     register : function(obj) {
18199                 
18200         Roo.XComponent.event.fireEvent('register', obj);
18201         switch(typeof(obj.disabled) ) {
18202                 
18203             case 'undefined':
18204                 break;
18205             
18206             case 'function':
18207                 if ( obj.disabled() ) {
18208                         return;
18209                 }
18210                 break;
18211             
18212             default:
18213                 if (obj.disabled || obj.region == '#disabled') {
18214                         return;
18215                 }
18216                 break;
18217         }
18218                 
18219         this.modules.push(obj);
18220          
18221     },
18222     /**
18223      * convert a string to an object..
18224      * eg. 'AAA.BBB' -> finds AAA.BBB
18225
18226      */
18227     
18228     toObject : function(str)
18229     {
18230         if (!str || typeof(str) == 'object') {
18231             return str;
18232         }
18233         if (str.substring(0,1) == '#') {
18234             return str;
18235         }
18236
18237         var ar = str.split('.');
18238         var rt, o;
18239         rt = ar.shift();
18240             /** eval:var:o */
18241         try {
18242             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18243         } catch (e) {
18244             throw "Module not found : " + str;
18245         }
18246         
18247         if (o === false) {
18248             throw "Module not found : " + str;
18249         }
18250         Roo.each(ar, function(e) {
18251             if (typeof(o[e]) == 'undefined') {
18252                 throw "Module not found : " + str;
18253             }
18254             o = o[e];
18255         });
18256         
18257         return o;
18258         
18259     },
18260     
18261     
18262     /**
18263      * move modules into their correct place in the tree..
18264      * 
18265      */
18266     preBuild : function ()
18267     {
18268         var _t = this;
18269         Roo.each(this.modules , function (obj)
18270         {
18271             Roo.XComponent.event.fireEvent('beforebuild', obj);
18272             
18273             var opar = obj.parent;
18274             try { 
18275                 obj.parent = this.toObject(opar);
18276             } catch(e) {
18277                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18278                 return;
18279             }
18280             
18281             if (!obj.parent) {
18282                 Roo.debug && Roo.log("GOT top level module");
18283                 Roo.debug && Roo.log(obj);
18284                 obj.modules = new Roo.util.MixedCollection(false, 
18285                     function(o) { return o.order + '' }
18286                 );
18287                 this.topModule = obj;
18288                 return;
18289             }
18290                         // parent is a string (usually a dom element name..)
18291             if (typeof(obj.parent) == 'string') {
18292                 this.elmodules.push(obj);
18293                 return;
18294             }
18295             if (obj.parent.constructor != Roo.XComponent) {
18296                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18297             }
18298             if (!obj.parent.modules) {
18299                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18300                     function(o) { return o.order + '' }
18301                 );
18302             }
18303             if (obj.parent.disabled) {
18304                 obj.disabled = true;
18305             }
18306             obj.parent.modules.add(obj);
18307         }, this);
18308     },
18309     
18310      /**
18311      * make a list of modules to build.
18312      * @return {Array} list of modules. 
18313      */ 
18314     
18315     buildOrder : function()
18316     {
18317         var _this = this;
18318         var cmp = function(a,b) {   
18319             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18320         };
18321         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18322             throw "No top level modules to build";
18323         }
18324         
18325         // make a flat list in order of modules to build.
18326         var mods = this.topModule ? [ this.topModule ] : [];
18327                 
18328         
18329         // elmodules (is a list of DOM based modules )
18330         Roo.each(this.elmodules, function(e) {
18331             mods.push(e);
18332             if (!this.topModule &&
18333                 typeof(e.parent) == 'string' &&
18334                 e.parent.substring(0,1) == '#' &&
18335                 Roo.get(e.parent.substr(1))
18336                ) {
18337                 
18338                 _this.topModule = e;
18339             }
18340             
18341         });
18342
18343         
18344         // add modules to their parents..
18345         var addMod = function(m) {
18346             Roo.debug && Roo.log("build Order: add: " + m.name);
18347                 
18348             mods.push(m);
18349             if (m.modules && !m.disabled) {
18350                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18351                 m.modules.keySort('ASC',  cmp );
18352                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18353     
18354                 m.modules.each(addMod);
18355             } else {
18356                 Roo.debug && Roo.log("build Order: no child modules");
18357             }
18358             // not sure if this is used any more..
18359             if (m.finalize) {
18360                 m.finalize.name = m.name + " (clean up) ";
18361                 mods.push(m.finalize);
18362             }
18363             
18364         }
18365         if (this.topModule && this.topModule.modules) { 
18366             this.topModule.modules.keySort('ASC',  cmp );
18367             this.topModule.modules.each(addMod);
18368         } 
18369         return mods;
18370     },
18371     
18372      /**
18373      * Build the registered modules.
18374      * @param {Object} parent element.
18375      * @param {Function} optional method to call after module has been added.
18376      * 
18377      */ 
18378    
18379     build : function(opts) 
18380     {
18381         
18382         if (typeof(opts) != 'undefined') {
18383             Roo.apply(this,opts);
18384         }
18385         
18386         this.preBuild();
18387         var mods = this.buildOrder();
18388       
18389         //this.allmods = mods;
18390         //Roo.debug && Roo.log(mods);
18391         //return;
18392         if (!mods.length) { // should not happen
18393             throw "NO modules!!!";
18394         }
18395         
18396         
18397         var msg = "Building Interface...";
18398         // flash it up as modal - so we store the mask!?
18399         if (!this.hideProgress && Roo.MessageBox) {
18400             Roo.MessageBox.show({ title: 'loading' });
18401             Roo.MessageBox.show({
18402                title: "Please wait...",
18403                msg: msg,
18404                width:450,
18405                progress:true,
18406                buttons : false,
18407                closable:false,
18408                modal: false
18409               
18410             });
18411         }
18412         var total = mods.length;
18413         
18414         var _this = this;
18415         var progressRun = function() {
18416             if (!mods.length) {
18417                 Roo.debug && Roo.log('hide?');
18418                 if (!this.hideProgress && Roo.MessageBox) {
18419                     Roo.MessageBox.hide();
18420                 }
18421                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18422                 
18423                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18424                 
18425                 // THE END...
18426                 return false;   
18427             }
18428             
18429             var m = mods.shift();
18430             
18431             
18432             Roo.debug && Roo.log(m);
18433             // not sure if this is supported any more.. - modules that are are just function
18434             if (typeof(m) == 'function') { 
18435                 m.call(this);
18436                 return progressRun.defer(10, _this);
18437             } 
18438             
18439             
18440             msg = "Building Interface " + (total  - mods.length) + 
18441                     " of " + total + 
18442                     (m.name ? (' - ' + m.name) : '');
18443                         Roo.debug && Roo.log(msg);
18444             if (!_this.hideProgress &&  Roo.MessageBox) { 
18445                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18446             }
18447             
18448          
18449             // is the module disabled?
18450             var disabled = (typeof(m.disabled) == 'function') ?
18451                 m.disabled.call(m.module.disabled) : m.disabled;    
18452             
18453             
18454             if (disabled) {
18455                 return progressRun(); // we do not update the display!
18456             }
18457             
18458             // now build 
18459             
18460                         
18461                         
18462             m.render();
18463             // it's 10 on top level, and 1 on others??? why...
18464             return progressRun.defer(10, _this);
18465              
18466         }
18467         progressRun.defer(1, _this);
18468      
18469         
18470         
18471     },
18472     /**
18473      * Overlay a set of modified strings onto a component
18474      * This is dependant on our builder exporting the strings and 'named strings' elements.
18475      * 
18476      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18477      * @param {Object} associative array of 'named' string and it's new value.
18478      * 
18479      */
18480         overlayStrings : function( component, strings )
18481     {
18482         if (typeof(component['_named_strings']) == 'undefined') {
18483             throw "ERROR: component does not have _named_strings";
18484         }
18485         for ( var k in strings ) {
18486             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18487             if (md !== false) {
18488                 component['_strings'][md] = strings[k];
18489             } else {
18490                 Roo.log('could not find named string: ' + k + ' in');
18491                 Roo.log(component);
18492             }
18493             
18494         }
18495         
18496     },
18497     
18498         
18499         /**
18500          * Event Object.
18501          *
18502          *
18503          */
18504         event: false, 
18505     /**
18506          * wrapper for event.on - aliased later..  
18507          * Typically use to register a event handler for register:
18508          *
18509          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18510          *
18511          */
18512     on : false
18513    
18514     
18515     
18516 });
18517
18518 Roo.XComponent.event = new Roo.util.Observable({
18519                 events : { 
18520                         /**
18521                          * @event register
18522                          * Fires when an Component is registered,
18523                          * set the disable property on the Component to stop registration.
18524                          * @param {Roo.XComponent} c the component being registerd.
18525                          * 
18526                          */
18527                         'register' : true,
18528             /**
18529                          * @event beforebuild
18530                          * Fires before each Component is built
18531                          * can be used to apply permissions.
18532                          * @param {Roo.XComponent} c the component being registerd.
18533                          * 
18534                          */
18535                         'beforebuild' : true,
18536                         /**
18537                          * @event buildcomplete
18538                          * Fires on the top level element when all elements have been built
18539                          * @param {Roo.XComponent} the top level component.
18540                          */
18541                         'buildcomplete' : true
18542                         
18543                 }
18544 });
18545
18546 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18547  //
18548  /**
18549  * marked - a markdown parser
18550  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18551  * https://github.com/chjj/marked
18552  */
18553
18554
18555 /**
18556  *
18557  * Roo.Markdown - is a very crude wrapper around marked..
18558  *
18559  * usage:
18560  * 
18561  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18562  * 
18563  * Note: move the sample code to the bottom of this
18564  * file before uncommenting it.
18565  *
18566  */
18567
18568 Roo.Markdown = {};
18569 Roo.Markdown.toHtml = function(text) {
18570     
18571     var c = new Roo.Markdown.marked.setOptions({
18572             renderer: new Roo.Markdown.marked.Renderer(),
18573             gfm: true,
18574             tables: true,
18575             breaks: false,
18576             pedantic: false,
18577             sanitize: false,
18578             smartLists: true,
18579             smartypants: false
18580           });
18581     // A FEW HACKS!!?
18582     
18583     text = text.replace(/\\\n/g,' ');
18584     return Roo.Markdown.marked(text);
18585 };
18586 //
18587 // converter
18588 //
18589 // Wraps all "globals" so that the only thing
18590 // exposed is makeHtml().
18591 //
18592 (function() {
18593     
18594      /**
18595          * eval:var:escape
18596          * eval:var:unescape
18597          * eval:var:replace
18598          */
18599       
18600     /**
18601      * Helpers
18602      */
18603     
18604     var escape = function (html, encode) {
18605       return html
18606         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18607         .replace(/</g, '&lt;')
18608         .replace(/>/g, '&gt;')
18609         .replace(/"/g, '&quot;')
18610         .replace(/'/g, '&#39;');
18611     }
18612     
18613     var unescape = function (html) {
18614         // explicitly match decimal, hex, and named HTML entities 
18615       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18616         n = n.toLowerCase();
18617         if (n === 'colon') { return ':'; }
18618         if (n.charAt(0) === '#') {
18619           return n.charAt(1) === 'x'
18620             ? String.fromCharCode(parseInt(n.substring(2), 16))
18621             : String.fromCharCode(+n.substring(1));
18622         }
18623         return '';
18624       });
18625     }
18626     
18627     var replace = function (regex, opt) {
18628       regex = regex.source;
18629       opt = opt || '';
18630       return function self(name, val) {
18631         if (!name) { return new RegExp(regex, opt); }
18632         val = val.source || val;
18633         val = val.replace(/(^|[^\[])\^/g, '$1');
18634         regex = regex.replace(name, val);
18635         return self;
18636       };
18637     }
18638
18639
18640          /**
18641          * eval:var:noop
18642     */
18643     var noop = function () {}
18644     noop.exec = noop;
18645     
18646          /**
18647          * eval:var:merge
18648     */
18649     var merge = function (obj) {
18650       var i = 1
18651         , target
18652         , key;
18653     
18654       for (; i < arguments.length; i++) {
18655         target = arguments[i];
18656         for (key in target) {
18657           if (Object.prototype.hasOwnProperty.call(target, key)) {
18658             obj[key] = target[key];
18659           }
18660         }
18661       }
18662     
18663       return obj;
18664     }
18665     
18666     
18667     /**
18668      * Block-Level Grammar
18669      */
18670     
18671     
18672     
18673     
18674     var block = {
18675       newline: /^\n+/,
18676       code: /^( {4}[^\n]+\n*)+/,
18677       fences: noop,
18678       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18679       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18680       nptable: noop,
18681       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18682       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18683       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18684       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18685       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18686       table: noop,
18687       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18688       text: /^[^\n]+/
18689     };
18690     
18691     block.bullet = /(?:[*+-]|\d+\.)/;
18692     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18693     block.item = replace(block.item, 'gm')
18694       (/bull/g, block.bullet)
18695       ();
18696     
18697     block.list = replace(block.list)
18698       (/bull/g, block.bullet)
18699       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18700       ('def', '\\n+(?=' + block.def.source + ')')
18701       ();
18702     
18703     block.blockquote = replace(block.blockquote)
18704       ('def', block.def)
18705       ();
18706     
18707     block._tag = '(?!(?:'
18708       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18709       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18710       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18711     
18712     block.html = replace(block.html)
18713       ('comment', /<!--[\s\S]*?-->/)
18714       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18715       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18716       (/tag/g, block._tag)
18717       ();
18718     
18719     block.paragraph = replace(block.paragraph)
18720       ('hr', block.hr)
18721       ('heading', block.heading)
18722       ('lheading', block.lheading)
18723       ('blockquote', block.blockquote)
18724       ('tag', '<' + block._tag)
18725       ('def', block.def)
18726       ();
18727     
18728     /**
18729      * Normal Block Grammar
18730      */
18731     
18732     block.normal = merge({}, block);
18733     
18734     /**
18735      * GFM Block Grammar
18736      */
18737     
18738     block.gfm = merge({}, block.normal, {
18739       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18740       paragraph: /^/,
18741       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18742     });
18743     
18744     block.gfm.paragraph = replace(block.paragraph)
18745       ('(?!', '(?!'
18746         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18747         + block.list.source.replace('\\1', '\\3') + '|')
18748       ();
18749     
18750     /**
18751      * GFM + Tables Block Grammar
18752      */
18753     
18754     block.tables = merge({}, block.gfm, {
18755       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18756       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18757     });
18758     
18759     /**
18760      * Block Lexer
18761      */
18762     
18763     var Lexer = function (options) {
18764       this.tokens = [];
18765       this.tokens.links = {};
18766       this.options = options || marked.defaults;
18767       this.rules = block.normal;
18768     
18769       if (this.options.gfm) {
18770         if (this.options.tables) {
18771           this.rules = block.tables;
18772         } else {
18773           this.rules = block.gfm;
18774         }
18775       }
18776     }
18777     
18778     /**
18779      * Expose Block Rules
18780      */
18781     
18782     Lexer.rules = block;
18783     
18784     /**
18785      * Static Lex Method
18786      */
18787     
18788     Lexer.lex = function(src, options) {
18789       var lexer = new Lexer(options);
18790       return lexer.lex(src);
18791     };
18792     
18793     /**
18794      * Preprocessing
18795      */
18796     
18797     Lexer.prototype.lex = function(src) {
18798       src = src
18799         .replace(/\r\n|\r/g, '\n')
18800         .replace(/\t/g, '    ')
18801         .replace(/\u00a0/g, ' ')
18802         .replace(/\u2424/g, '\n');
18803     
18804       return this.token(src, true);
18805     };
18806     
18807     /**
18808      * Lexing
18809      */
18810     
18811     Lexer.prototype.token = function(src, top, bq) {
18812       var src = src.replace(/^ +$/gm, '')
18813         , next
18814         , loose
18815         , cap
18816         , bull
18817         , b
18818         , item
18819         , space
18820         , i
18821         , l;
18822     
18823       while (src) {
18824         // newline
18825         if (cap = this.rules.newline.exec(src)) {
18826           src = src.substring(cap[0].length);
18827           if (cap[0].length > 1) {
18828             this.tokens.push({
18829               type: 'space'
18830             });
18831           }
18832         }
18833     
18834         // code
18835         if (cap = this.rules.code.exec(src)) {
18836           src = src.substring(cap[0].length);
18837           cap = cap[0].replace(/^ {4}/gm, '');
18838           this.tokens.push({
18839             type: 'code',
18840             text: !this.options.pedantic
18841               ? cap.replace(/\n+$/, '')
18842               : cap
18843           });
18844           continue;
18845         }
18846     
18847         // fences (gfm)
18848         if (cap = this.rules.fences.exec(src)) {
18849           src = src.substring(cap[0].length);
18850           this.tokens.push({
18851             type: 'code',
18852             lang: cap[2],
18853             text: cap[3] || ''
18854           });
18855           continue;
18856         }
18857     
18858         // heading
18859         if (cap = this.rules.heading.exec(src)) {
18860           src = src.substring(cap[0].length);
18861           this.tokens.push({
18862             type: 'heading',
18863             depth: cap[1].length,
18864             text: cap[2]
18865           });
18866           continue;
18867         }
18868     
18869         // table no leading pipe (gfm)
18870         if (top && (cap = this.rules.nptable.exec(src))) {
18871           src = src.substring(cap[0].length);
18872     
18873           item = {
18874             type: 'table',
18875             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18876             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18877             cells: cap[3].replace(/\n$/, '').split('\n')
18878           };
18879     
18880           for (i = 0; i < item.align.length; i++) {
18881             if (/^ *-+: *$/.test(item.align[i])) {
18882               item.align[i] = 'right';
18883             } else if (/^ *:-+: *$/.test(item.align[i])) {
18884               item.align[i] = 'center';
18885             } else if (/^ *:-+ *$/.test(item.align[i])) {
18886               item.align[i] = 'left';
18887             } else {
18888               item.align[i] = null;
18889             }
18890           }
18891     
18892           for (i = 0; i < item.cells.length; i++) {
18893             item.cells[i] = item.cells[i].split(/ *\| */);
18894           }
18895     
18896           this.tokens.push(item);
18897     
18898           continue;
18899         }
18900     
18901         // lheading
18902         if (cap = this.rules.lheading.exec(src)) {
18903           src = src.substring(cap[0].length);
18904           this.tokens.push({
18905             type: 'heading',
18906             depth: cap[2] === '=' ? 1 : 2,
18907             text: cap[1]
18908           });
18909           continue;
18910         }
18911     
18912         // hr
18913         if (cap = this.rules.hr.exec(src)) {
18914           src = src.substring(cap[0].length);
18915           this.tokens.push({
18916             type: 'hr'
18917           });
18918           continue;
18919         }
18920     
18921         // blockquote
18922         if (cap = this.rules.blockquote.exec(src)) {
18923           src = src.substring(cap[0].length);
18924     
18925           this.tokens.push({
18926             type: 'blockquote_start'
18927           });
18928     
18929           cap = cap[0].replace(/^ *> ?/gm, '');
18930     
18931           // Pass `top` to keep the current
18932           // "toplevel" state. This is exactly
18933           // how markdown.pl works.
18934           this.token(cap, top, true);
18935     
18936           this.tokens.push({
18937             type: 'blockquote_end'
18938           });
18939     
18940           continue;
18941         }
18942     
18943         // list
18944         if (cap = this.rules.list.exec(src)) {
18945           src = src.substring(cap[0].length);
18946           bull = cap[2];
18947     
18948           this.tokens.push({
18949             type: 'list_start',
18950             ordered: bull.length > 1
18951           });
18952     
18953           // Get each top-level item.
18954           cap = cap[0].match(this.rules.item);
18955     
18956           next = false;
18957           l = cap.length;
18958           i = 0;
18959     
18960           for (; i < l; i++) {
18961             item = cap[i];
18962     
18963             // Remove the list item's bullet
18964             // so it is seen as the next token.
18965             space = item.length;
18966             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18967     
18968             // Outdent whatever the
18969             // list item contains. Hacky.
18970             if (~item.indexOf('\n ')) {
18971               space -= item.length;
18972               item = !this.options.pedantic
18973                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18974                 : item.replace(/^ {1,4}/gm, '');
18975             }
18976     
18977             // Determine whether the next list item belongs here.
18978             // Backpedal if it does not belong in this list.
18979             if (this.options.smartLists && i !== l - 1) {
18980               b = block.bullet.exec(cap[i + 1])[0];
18981               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18982                 src = cap.slice(i + 1).join('\n') + src;
18983                 i = l - 1;
18984               }
18985             }
18986     
18987             // Determine whether item is loose or not.
18988             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18989             // for discount behavior.
18990             loose = next || /\n\n(?!\s*$)/.test(item);
18991             if (i !== l - 1) {
18992               next = item.charAt(item.length - 1) === '\n';
18993               if (!loose) { loose = next; }
18994             }
18995     
18996             this.tokens.push({
18997               type: loose
18998                 ? 'loose_item_start'
18999                 : 'list_item_start'
19000             });
19001     
19002             // Recurse.
19003             this.token(item, false, bq);
19004     
19005             this.tokens.push({
19006               type: 'list_item_end'
19007             });
19008           }
19009     
19010           this.tokens.push({
19011             type: 'list_end'
19012           });
19013     
19014           continue;
19015         }
19016     
19017         // html
19018         if (cap = this.rules.html.exec(src)) {
19019           src = src.substring(cap[0].length);
19020           this.tokens.push({
19021             type: this.options.sanitize
19022               ? 'paragraph'
19023               : 'html',
19024             pre: !this.options.sanitizer
19025               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19026             text: cap[0]
19027           });
19028           continue;
19029         }
19030     
19031         // def
19032         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19033           src = src.substring(cap[0].length);
19034           this.tokens.links[cap[1].toLowerCase()] = {
19035             href: cap[2],
19036             title: cap[3]
19037           };
19038           continue;
19039         }
19040     
19041         // table (gfm)
19042         if (top && (cap = this.rules.table.exec(src))) {
19043           src = src.substring(cap[0].length);
19044     
19045           item = {
19046             type: 'table',
19047             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19048             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19049             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19050           };
19051     
19052           for (i = 0; i < item.align.length; i++) {
19053             if (/^ *-+: *$/.test(item.align[i])) {
19054               item.align[i] = 'right';
19055             } else if (/^ *:-+: *$/.test(item.align[i])) {
19056               item.align[i] = 'center';
19057             } else if (/^ *:-+ *$/.test(item.align[i])) {
19058               item.align[i] = 'left';
19059             } else {
19060               item.align[i] = null;
19061             }
19062           }
19063     
19064           for (i = 0; i < item.cells.length; i++) {
19065             item.cells[i] = item.cells[i]
19066               .replace(/^ *\| *| *\| *$/g, '')
19067               .split(/ *\| */);
19068           }
19069     
19070           this.tokens.push(item);
19071     
19072           continue;
19073         }
19074     
19075         // top-level paragraph
19076         if (top && (cap = this.rules.paragraph.exec(src))) {
19077           src = src.substring(cap[0].length);
19078           this.tokens.push({
19079             type: 'paragraph',
19080             text: cap[1].charAt(cap[1].length - 1) === '\n'
19081               ? cap[1].slice(0, -1)
19082               : cap[1]
19083           });
19084           continue;
19085         }
19086     
19087         // text
19088         if (cap = this.rules.text.exec(src)) {
19089           // Top-level should never reach here.
19090           src = src.substring(cap[0].length);
19091           this.tokens.push({
19092             type: 'text',
19093             text: cap[0]
19094           });
19095           continue;
19096         }
19097     
19098         if (src) {
19099           throw new
19100             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19101         }
19102       }
19103     
19104       return this.tokens;
19105     };
19106     
19107     /**
19108      * Inline-Level Grammar
19109      */
19110     
19111     var inline = {
19112       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19113       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19114       url: noop,
19115       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19116       link: /^!?\[(inside)\]\(href\)/,
19117       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19118       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19119       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19120       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19121       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19122       br: /^ {2,}\n(?!\s*$)/,
19123       del: noop,
19124       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19125     };
19126     
19127     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19128     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19129     
19130     inline.link = replace(inline.link)
19131       ('inside', inline._inside)
19132       ('href', inline._href)
19133       ();
19134     
19135     inline.reflink = replace(inline.reflink)
19136       ('inside', inline._inside)
19137       ();
19138     
19139     /**
19140      * Normal Inline Grammar
19141      */
19142     
19143     inline.normal = merge({}, inline);
19144     
19145     /**
19146      * Pedantic Inline Grammar
19147      */
19148     
19149     inline.pedantic = merge({}, inline.normal, {
19150       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19151       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19152     });
19153     
19154     /**
19155      * GFM Inline Grammar
19156      */
19157     
19158     inline.gfm = merge({}, inline.normal, {
19159       escape: replace(inline.escape)('])', '~|])')(),
19160       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19161       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19162       text: replace(inline.text)
19163         (']|', '~]|')
19164         ('|', '|https?://|')
19165         ()
19166     });
19167     
19168     /**
19169      * GFM + Line Breaks Inline Grammar
19170      */
19171     
19172     inline.breaks = merge({}, inline.gfm, {
19173       br: replace(inline.br)('{2,}', '*')(),
19174       text: replace(inline.gfm.text)('{2,}', '*')()
19175     });
19176     
19177     /**
19178      * Inline Lexer & Compiler
19179      */
19180     
19181     var InlineLexer  = function (links, options) {
19182       this.options = options || marked.defaults;
19183       this.links = links;
19184       this.rules = inline.normal;
19185       this.renderer = this.options.renderer || new Renderer;
19186       this.renderer.options = this.options;
19187     
19188       if (!this.links) {
19189         throw new
19190           Error('Tokens array requires a `links` property.');
19191       }
19192     
19193       if (this.options.gfm) {
19194         if (this.options.breaks) {
19195           this.rules = inline.breaks;
19196         } else {
19197           this.rules = inline.gfm;
19198         }
19199       } else if (this.options.pedantic) {
19200         this.rules = inline.pedantic;
19201       }
19202     }
19203     
19204     /**
19205      * Expose Inline Rules
19206      */
19207     
19208     InlineLexer.rules = inline;
19209     
19210     /**
19211      * Static Lexing/Compiling Method
19212      */
19213     
19214     InlineLexer.output = function(src, links, options) {
19215       var inline = new InlineLexer(links, options);
19216       return inline.output(src);
19217     };
19218     
19219     /**
19220      * Lexing/Compiling
19221      */
19222     
19223     InlineLexer.prototype.output = function(src) {
19224       var out = ''
19225         , link
19226         , text
19227         , href
19228         , cap;
19229     
19230       while (src) {
19231         // escape
19232         if (cap = this.rules.escape.exec(src)) {
19233           src = src.substring(cap[0].length);
19234           out += cap[1];
19235           continue;
19236         }
19237     
19238         // autolink
19239         if (cap = this.rules.autolink.exec(src)) {
19240           src = src.substring(cap[0].length);
19241           if (cap[2] === '@') {
19242             text = cap[1].charAt(6) === ':'
19243               ? this.mangle(cap[1].substring(7))
19244               : this.mangle(cap[1]);
19245             href = this.mangle('mailto:') + text;
19246           } else {
19247             text = escape(cap[1]);
19248             href = text;
19249           }
19250           out += this.renderer.link(href, null, text);
19251           continue;
19252         }
19253     
19254         // url (gfm)
19255         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19256           src = src.substring(cap[0].length);
19257           text = escape(cap[1]);
19258           href = text;
19259           out += this.renderer.link(href, null, text);
19260           continue;
19261         }
19262     
19263         // tag
19264         if (cap = this.rules.tag.exec(src)) {
19265           if (!this.inLink && /^<a /i.test(cap[0])) {
19266             this.inLink = true;
19267           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19268             this.inLink = false;
19269           }
19270           src = src.substring(cap[0].length);
19271           out += this.options.sanitize
19272             ? this.options.sanitizer
19273               ? this.options.sanitizer(cap[0])
19274               : escape(cap[0])
19275             : cap[0];
19276           continue;
19277         }
19278     
19279         // link
19280         if (cap = this.rules.link.exec(src)) {
19281           src = src.substring(cap[0].length);
19282           this.inLink = true;
19283           out += this.outputLink(cap, {
19284             href: cap[2],
19285             title: cap[3]
19286           });
19287           this.inLink = false;
19288           continue;
19289         }
19290     
19291         // reflink, nolink
19292         if ((cap = this.rules.reflink.exec(src))
19293             || (cap = this.rules.nolink.exec(src))) {
19294           src = src.substring(cap[0].length);
19295           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19296           link = this.links[link.toLowerCase()];
19297           if (!link || !link.href) {
19298             out += cap[0].charAt(0);
19299             src = cap[0].substring(1) + src;
19300             continue;
19301           }
19302           this.inLink = true;
19303           out += this.outputLink(cap, link);
19304           this.inLink = false;
19305           continue;
19306         }
19307     
19308         // strong
19309         if (cap = this.rules.strong.exec(src)) {
19310           src = src.substring(cap[0].length);
19311           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19312           continue;
19313         }
19314     
19315         // em
19316         if (cap = this.rules.em.exec(src)) {
19317           src = src.substring(cap[0].length);
19318           out += this.renderer.em(this.output(cap[2] || cap[1]));
19319           continue;
19320         }
19321     
19322         // code
19323         if (cap = this.rules.code.exec(src)) {
19324           src = src.substring(cap[0].length);
19325           out += this.renderer.codespan(escape(cap[2], true));
19326           continue;
19327         }
19328     
19329         // br
19330         if (cap = this.rules.br.exec(src)) {
19331           src = src.substring(cap[0].length);
19332           out += this.renderer.br();
19333           continue;
19334         }
19335     
19336         // del (gfm)
19337         if (cap = this.rules.del.exec(src)) {
19338           src = src.substring(cap[0].length);
19339           out += this.renderer.del(this.output(cap[1]));
19340           continue;
19341         }
19342     
19343         // text
19344         if (cap = this.rules.text.exec(src)) {
19345           src = src.substring(cap[0].length);
19346           out += this.renderer.text(escape(this.smartypants(cap[0])));
19347           continue;
19348         }
19349     
19350         if (src) {
19351           throw new
19352             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19353         }
19354       }
19355     
19356       return out;
19357     };
19358     
19359     /**
19360      * Compile Link
19361      */
19362     
19363     InlineLexer.prototype.outputLink = function(cap, link) {
19364       var href = escape(link.href)
19365         , title = link.title ? escape(link.title) : null;
19366     
19367       return cap[0].charAt(0) !== '!'
19368         ? this.renderer.link(href, title, this.output(cap[1]))
19369         : this.renderer.image(href, title, escape(cap[1]));
19370     };
19371     
19372     /**
19373      * Smartypants Transformations
19374      */
19375     
19376     InlineLexer.prototype.smartypants = function(text) {
19377       if (!this.options.smartypants)  { return text; }
19378       return text
19379         // em-dashes
19380         .replace(/---/g, '\u2014')
19381         // en-dashes
19382         .replace(/--/g, '\u2013')
19383         // opening singles
19384         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19385         // closing singles & apostrophes
19386         .replace(/'/g, '\u2019')
19387         // opening doubles
19388         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19389         // closing doubles
19390         .replace(/"/g, '\u201d')
19391         // ellipses
19392         .replace(/\.{3}/g, '\u2026');
19393     };
19394     
19395     /**
19396      * Mangle Links
19397      */
19398     
19399     InlineLexer.prototype.mangle = function(text) {
19400       if (!this.options.mangle) { return text; }
19401       var out = ''
19402         , l = text.length
19403         , i = 0
19404         , ch;
19405     
19406       for (; i < l; i++) {
19407         ch = text.charCodeAt(i);
19408         if (Math.random() > 0.5) {
19409           ch = 'x' + ch.toString(16);
19410         }
19411         out += '&#' + ch + ';';
19412       }
19413     
19414       return out;
19415     };
19416     
19417     /**
19418      * Renderer
19419      */
19420     
19421      /**
19422          * eval:var:Renderer
19423     */
19424     
19425     var Renderer   = function (options) {
19426       this.options = options || {};
19427     }
19428     
19429     Renderer.prototype.code = function(code, lang, escaped) {
19430       if (this.options.highlight) {
19431         var out = this.options.highlight(code, lang);
19432         if (out != null && out !== code) {
19433           escaped = true;
19434           code = out;
19435         }
19436       } else {
19437             // hack!!! - it's already escapeD?
19438             escaped = true;
19439       }
19440     
19441       if (!lang) {
19442         return '<pre><code>'
19443           + (escaped ? code : escape(code, true))
19444           + '\n</code></pre>';
19445       }
19446     
19447       return '<pre><code class="'
19448         + this.options.langPrefix
19449         + escape(lang, true)
19450         + '">'
19451         + (escaped ? code : escape(code, true))
19452         + '\n</code></pre>\n';
19453     };
19454     
19455     Renderer.prototype.blockquote = function(quote) {
19456       return '<blockquote>\n' + quote + '</blockquote>\n';
19457     };
19458     
19459     Renderer.prototype.html = function(html) {
19460       return html;
19461     };
19462     
19463     Renderer.prototype.heading = function(text, level, raw) {
19464       return '<h'
19465         + level
19466         + ' id="'
19467         + this.options.headerPrefix
19468         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19469         + '">'
19470         + text
19471         + '</h'
19472         + level
19473         + '>\n';
19474     };
19475     
19476     Renderer.prototype.hr = function() {
19477       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19478     };
19479     
19480     Renderer.prototype.list = function(body, ordered) {
19481       var type = ordered ? 'ol' : 'ul';
19482       return '<' + type + '>\n' + body + '</' + type + '>\n';
19483     };
19484     
19485     Renderer.prototype.listitem = function(text) {
19486       return '<li>' + text + '</li>\n';
19487     };
19488     
19489     Renderer.prototype.paragraph = function(text) {
19490       return '<p>' + text + '</p>\n';
19491     };
19492     
19493     Renderer.prototype.table = function(header, body) {
19494       return '<table class="table table-striped">\n'
19495         + '<thead>\n'
19496         + header
19497         + '</thead>\n'
19498         + '<tbody>\n'
19499         + body
19500         + '</tbody>\n'
19501         + '</table>\n';
19502     };
19503     
19504     Renderer.prototype.tablerow = function(content) {
19505       return '<tr>\n' + content + '</tr>\n';
19506     };
19507     
19508     Renderer.prototype.tablecell = function(content, flags) {
19509       var type = flags.header ? 'th' : 'td';
19510       var tag = flags.align
19511         ? '<' + type + ' style="text-align:' + flags.align + '">'
19512         : '<' + type + '>';
19513       return tag + content + '</' + type + '>\n';
19514     };
19515     
19516     // span level renderer
19517     Renderer.prototype.strong = function(text) {
19518       return '<strong>' + text + '</strong>';
19519     };
19520     
19521     Renderer.prototype.em = function(text) {
19522       return '<em>' + text + '</em>';
19523     };
19524     
19525     Renderer.prototype.codespan = function(text) {
19526       return '<code>' + text + '</code>';
19527     };
19528     
19529     Renderer.prototype.br = function() {
19530       return this.options.xhtml ? '<br/>' : '<br>';
19531     };
19532     
19533     Renderer.prototype.del = function(text) {
19534       return '<del>' + text + '</del>';
19535     };
19536     
19537     Renderer.prototype.link = function(href, title, text) {
19538       if (this.options.sanitize) {
19539         try {
19540           var prot = decodeURIComponent(unescape(href))
19541             .replace(/[^\w:]/g, '')
19542             .toLowerCase();
19543         } catch (e) {
19544           return '';
19545         }
19546         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19547           return '';
19548         }
19549       }
19550       var out = '<a href="' + href + '"';
19551       if (title) {
19552         out += ' title="' + title + '"';
19553       }
19554       out += '>' + text + '</a>';
19555       return out;
19556     };
19557     
19558     Renderer.prototype.image = function(href, title, text) {
19559       var out = '<img src="' + href + '" alt="' + text + '"';
19560       if (title) {
19561         out += ' title="' + title + '"';
19562       }
19563       out += this.options.xhtml ? '/>' : '>';
19564       return out;
19565     };
19566     
19567     Renderer.prototype.text = function(text) {
19568       return text;
19569     };
19570     
19571     /**
19572      * Parsing & Compiling
19573      */
19574          /**
19575          * eval:var:Parser
19576     */
19577     
19578     var Parser= function (options) {
19579       this.tokens = [];
19580       this.token = null;
19581       this.options = options || marked.defaults;
19582       this.options.renderer = this.options.renderer || new Renderer;
19583       this.renderer = this.options.renderer;
19584       this.renderer.options = this.options;
19585     }
19586     
19587     /**
19588      * Static Parse Method
19589      */
19590     
19591     Parser.parse = function(src, options, renderer) {
19592       var parser = new Parser(options, renderer);
19593       return parser.parse(src);
19594     };
19595     
19596     /**
19597      * Parse Loop
19598      */
19599     
19600     Parser.prototype.parse = function(src) {
19601       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19602       this.tokens = src.reverse();
19603     
19604       var out = '';
19605       while (this.next()) {
19606         out += this.tok();
19607       }
19608     
19609       return out;
19610     };
19611     
19612     /**
19613      * Next Token
19614      */
19615     
19616     Parser.prototype.next = function() {
19617       return this.token = this.tokens.pop();
19618     };
19619     
19620     /**
19621      * Preview Next Token
19622      */
19623     
19624     Parser.prototype.peek = function() {
19625       return this.tokens[this.tokens.length - 1] || 0;
19626     };
19627     
19628     /**
19629      * Parse Text Tokens
19630      */
19631     
19632     Parser.prototype.parseText = function() {
19633       var body = this.token.text;
19634     
19635       while (this.peek().type === 'text') {
19636         body += '\n' + this.next().text;
19637       }
19638     
19639       return this.inline.output(body);
19640     };
19641     
19642     /**
19643      * Parse Current Token
19644      */
19645     
19646     Parser.prototype.tok = function() {
19647       switch (this.token.type) {
19648         case 'space': {
19649           return '';
19650         }
19651         case 'hr': {
19652           return this.renderer.hr();
19653         }
19654         case 'heading': {
19655           return this.renderer.heading(
19656             this.inline.output(this.token.text),
19657             this.token.depth,
19658             this.token.text);
19659         }
19660         case 'code': {
19661           return this.renderer.code(this.token.text,
19662             this.token.lang,
19663             this.token.escaped);
19664         }
19665         case 'table': {
19666           var header = ''
19667             , body = ''
19668             , i
19669             , row
19670             , cell
19671             , flags
19672             , j;
19673     
19674           // header
19675           cell = '';
19676           for (i = 0; i < this.token.header.length; i++) {
19677             flags = { header: true, align: this.token.align[i] };
19678             cell += this.renderer.tablecell(
19679               this.inline.output(this.token.header[i]),
19680               { header: true, align: this.token.align[i] }
19681             );
19682           }
19683           header += this.renderer.tablerow(cell);
19684     
19685           for (i = 0; i < this.token.cells.length; i++) {
19686             row = this.token.cells[i];
19687     
19688             cell = '';
19689             for (j = 0; j < row.length; j++) {
19690               cell += this.renderer.tablecell(
19691                 this.inline.output(row[j]),
19692                 { header: false, align: this.token.align[j] }
19693               );
19694             }
19695     
19696             body += this.renderer.tablerow(cell);
19697           }
19698           return this.renderer.table(header, body);
19699         }
19700         case 'blockquote_start': {
19701           var body = '';
19702     
19703           while (this.next().type !== 'blockquote_end') {
19704             body += this.tok();
19705           }
19706     
19707           return this.renderer.blockquote(body);
19708         }
19709         case 'list_start': {
19710           var body = ''
19711             , ordered = this.token.ordered;
19712     
19713           while (this.next().type !== 'list_end') {
19714             body += this.tok();
19715           }
19716     
19717           return this.renderer.list(body, ordered);
19718         }
19719         case 'list_item_start': {
19720           var body = '';
19721     
19722           while (this.next().type !== 'list_item_end') {
19723             body += this.token.type === 'text'
19724               ? this.parseText()
19725               : this.tok();
19726           }
19727     
19728           return this.renderer.listitem(body);
19729         }
19730         case 'loose_item_start': {
19731           var body = '';
19732     
19733           while (this.next().type !== 'list_item_end') {
19734             body += this.tok();
19735           }
19736     
19737           return this.renderer.listitem(body);
19738         }
19739         case 'html': {
19740           var html = !this.token.pre && !this.options.pedantic
19741             ? this.inline.output(this.token.text)
19742             : this.token.text;
19743           return this.renderer.html(html);
19744         }
19745         case 'paragraph': {
19746           return this.renderer.paragraph(this.inline.output(this.token.text));
19747         }
19748         case 'text': {
19749           return this.renderer.paragraph(this.parseText());
19750         }
19751       }
19752     };
19753   
19754     
19755     /**
19756      * Marked
19757      */
19758          /**
19759          * eval:var:marked
19760     */
19761     var marked = function (src, opt, callback) {
19762       if (callback || typeof opt === 'function') {
19763         if (!callback) {
19764           callback = opt;
19765           opt = null;
19766         }
19767     
19768         opt = merge({}, marked.defaults, opt || {});
19769     
19770         var highlight = opt.highlight
19771           , tokens
19772           , pending
19773           , i = 0;
19774     
19775         try {
19776           tokens = Lexer.lex(src, opt)
19777         } catch (e) {
19778           return callback(e);
19779         }
19780     
19781         pending = tokens.length;
19782          /**
19783          * eval:var:done
19784     */
19785         var done = function(err) {
19786           if (err) {
19787             opt.highlight = highlight;
19788             return callback(err);
19789           }
19790     
19791           var out;
19792     
19793           try {
19794             out = Parser.parse(tokens, opt);
19795           } catch (e) {
19796             err = e;
19797           }
19798     
19799           opt.highlight = highlight;
19800     
19801           return err
19802             ? callback(err)
19803             : callback(null, out);
19804         };
19805     
19806         if (!highlight || highlight.length < 3) {
19807           return done();
19808         }
19809     
19810         delete opt.highlight;
19811     
19812         if (!pending) { return done(); }
19813     
19814         for (; i < tokens.length; i++) {
19815           (function(token) {
19816             if (token.type !== 'code') {
19817               return --pending || done();
19818             }
19819             return highlight(token.text, token.lang, function(err, code) {
19820               if (err) { return done(err); }
19821               if (code == null || code === token.text) {
19822                 return --pending || done();
19823               }
19824               token.text = code;
19825               token.escaped = true;
19826               --pending || done();
19827             });
19828           })(tokens[i]);
19829         }
19830     
19831         return;
19832       }
19833       try {
19834         if (opt) { opt = merge({}, marked.defaults, opt); }
19835         return Parser.parse(Lexer.lex(src, opt), opt);
19836       } catch (e) {
19837         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19838         if ((opt || marked.defaults).silent) {
19839           return '<p>An error occured:</p><pre>'
19840             + escape(e.message + '', true)
19841             + '</pre>';
19842         }
19843         throw e;
19844       }
19845     }
19846     
19847     /**
19848      * Options
19849      */
19850     
19851     marked.options =
19852     marked.setOptions = function(opt) {
19853       merge(marked.defaults, opt);
19854       return marked;
19855     };
19856     
19857     marked.defaults = {
19858       gfm: true,
19859       tables: true,
19860       breaks: false,
19861       pedantic: false,
19862       sanitize: false,
19863       sanitizer: null,
19864       mangle: true,
19865       smartLists: false,
19866       silent: false,
19867       highlight: null,
19868       langPrefix: 'lang-',
19869       smartypants: false,
19870       headerPrefix: '',
19871       renderer: new Renderer,
19872       xhtml: false
19873     };
19874     
19875     /**
19876      * Expose
19877      */
19878     
19879     marked.Parser = Parser;
19880     marked.parser = Parser.parse;
19881     
19882     marked.Renderer = Renderer;
19883     
19884     marked.Lexer = Lexer;
19885     marked.lexer = Lexer.lex;
19886     
19887     marked.InlineLexer = InlineLexer;
19888     marked.inlineLexer = InlineLexer.output;
19889     
19890     marked.parse = marked;
19891     
19892     Roo.Markdown.marked = marked;
19893
19894 })();/*
19895  * Based on:
19896  * Ext JS Library 1.1.1
19897  * Copyright(c) 2006-2007, Ext JS, LLC.
19898  *
19899  * Originally Released Under LGPL - original licence link has changed is not relivant.
19900  *
19901  * Fork - LGPL
19902  * <script type="text/javascript">
19903  */
19904
19905
19906
19907 /*
19908  * These classes are derivatives of the similarly named classes in the YUI Library.
19909  * The original license:
19910  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19911  * Code licensed under the BSD License:
19912  * http://developer.yahoo.net/yui/license.txt
19913  */
19914
19915 (function() {
19916
19917 var Event=Roo.EventManager;
19918 var Dom=Roo.lib.Dom;
19919
19920 /**
19921  * @class Roo.dd.DragDrop
19922  * @extends Roo.util.Observable
19923  * Defines the interface and base operation of items that that can be
19924  * dragged or can be drop targets.  It was designed to be extended, overriding
19925  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19926  * Up to three html elements can be associated with a DragDrop instance:
19927  * <ul>
19928  * <li>linked element: the element that is passed into the constructor.
19929  * This is the element which defines the boundaries for interaction with
19930  * other DragDrop objects.</li>
19931  * <li>handle element(s): The drag operation only occurs if the element that
19932  * was clicked matches a handle element.  By default this is the linked
19933  * element, but there are times that you will want only a portion of the
19934  * linked element to initiate the drag operation, and the setHandleElId()
19935  * method provides a way to define this.</li>
19936  * <li>drag element: this represents the element that would be moved along
19937  * with the cursor during a drag operation.  By default, this is the linked
19938  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19939  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19940  * </li>
19941  * </ul>
19942  * This class should not be instantiated until the onload event to ensure that
19943  * the associated elements are available.
19944  * The following would define a DragDrop obj that would interact with any
19945  * other DragDrop obj in the "group1" group:
19946  * <pre>
19947  *  dd = new Roo.dd.DragDrop("div1", "group1");
19948  * </pre>
19949  * Since none of the event handlers have been implemented, nothing would
19950  * actually happen if you were to run the code above.  Normally you would
19951  * override this class or one of the default implementations, but you can
19952  * also override the methods you want on an instance of the class...
19953  * <pre>
19954  *  dd.onDragDrop = function(e, id) {
19955  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19956  *  }
19957  * </pre>
19958  * @constructor
19959  * @param {String} id of the element that is linked to this instance
19960  * @param {String} sGroup the group of related DragDrop objects
19961  * @param {object} config an object containing configurable attributes
19962  *                Valid properties for DragDrop:
19963  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19964  */
19965 Roo.dd.DragDrop = function(id, sGroup, config) {
19966     if (id) {
19967         this.init(id, sGroup, config);
19968     }
19969     
19970 };
19971
19972 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19973
19974     /**
19975      * The id of the element associated with this object.  This is what we
19976      * refer to as the "linked element" because the size and position of
19977      * this element is used to determine when the drag and drop objects have
19978      * interacted.
19979      * @property id
19980      * @type String
19981      */
19982     id: null,
19983
19984     /**
19985      * Configuration attributes passed into the constructor
19986      * @property config
19987      * @type object
19988      */
19989     config: null,
19990
19991     /**
19992      * The id of the element that will be dragged.  By default this is same
19993      * as the linked element , but could be changed to another element. Ex:
19994      * Roo.dd.DDProxy
19995      * @property dragElId
19996      * @type String
19997      * @private
19998      */
19999     dragElId: null,
20000
20001     /**
20002      * the id of the element that initiates the drag operation.  By default
20003      * this is the linked element, but could be changed to be a child of this
20004      * element.  This lets us do things like only starting the drag when the
20005      * header element within the linked html element is clicked.
20006      * @property handleElId
20007      * @type String
20008      * @private
20009      */
20010     handleElId: null,
20011
20012     /**
20013      * An associative array of HTML tags that will be ignored if clicked.
20014      * @property invalidHandleTypes
20015      * @type {string: string}
20016      */
20017     invalidHandleTypes: null,
20018
20019     /**
20020      * An associative array of ids for elements that will be ignored if clicked
20021      * @property invalidHandleIds
20022      * @type {string: string}
20023      */
20024     invalidHandleIds: null,
20025
20026     /**
20027      * An indexted array of css class names for elements that will be ignored
20028      * if clicked.
20029      * @property invalidHandleClasses
20030      * @type string[]
20031      */
20032     invalidHandleClasses: null,
20033
20034     /**
20035      * The linked element's absolute X position at the time the drag was
20036      * started
20037      * @property startPageX
20038      * @type int
20039      * @private
20040      */
20041     startPageX: 0,
20042
20043     /**
20044      * The linked element's absolute X position at the time the drag was
20045      * started
20046      * @property startPageY
20047      * @type int
20048      * @private
20049      */
20050     startPageY: 0,
20051
20052     /**
20053      * The group defines a logical collection of DragDrop objects that are
20054      * related.  Instances only get events when interacting with other
20055      * DragDrop object in the same group.  This lets us define multiple
20056      * groups using a single DragDrop subclass if we want.
20057      * @property groups
20058      * @type {string: string}
20059      */
20060     groups: null,
20061
20062     /**
20063      * Individual drag/drop instances can be locked.  This will prevent
20064      * onmousedown start drag.
20065      * @property locked
20066      * @type boolean
20067      * @private
20068      */
20069     locked: false,
20070
20071     /**
20072      * Lock this instance
20073      * @method lock
20074      */
20075     lock: function() { this.locked = true; },
20076
20077     /**
20078      * Unlock this instace
20079      * @method unlock
20080      */
20081     unlock: function() { this.locked = false; },
20082
20083     /**
20084      * By default, all insances can be a drop target.  This can be disabled by
20085      * setting isTarget to false.
20086      * @method isTarget
20087      * @type boolean
20088      */
20089     isTarget: true,
20090
20091     /**
20092      * The padding configured for this drag and drop object for calculating
20093      * the drop zone intersection with this object.
20094      * @method padding
20095      * @type int[]
20096      */
20097     padding: null,
20098
20099     /**
20100      * Cached reference to the linked element
20101      * @property _domRef
20102      * @private
20103      */
20104     _domRef: null,
20105
20106     /**
20107      * Internal typeof flag
20108      * @property __ygDragDrop
20109      * @private
20110      */
20111     __ygDragDrop: true,
20112
20113     /**
20114      * Set to true when horizontal contraints are applied
20115      * @property constrainX
20116      * @type boolean
20117      * @private
20118      */
20119     constrainX: false,
20120
20121     /**
20122      * Set to true when vertical contraints are applied
20123      * @property constrainY
20124      * @type boolean
20125      * @private
20126      */
20127     constrainY: false,
20128
20129     /**
20130      * The left constraint
20131      * @property minX
20132      * @type int
20133      * @private
20134      */
20135     minX: 0,
20136
20137     /**
20138      * The right constraint
20139      * @property maxX
20140      * @type int
20141      * @private
20142      */
20143     maxX: 0,
20144
20145     /**
20146      * The up constraint
20147      * @property minY
20148      * @type int
20149      * @type int
20150      * @private
20151      */
20152     minY: 0,
20153
20154     /**
20155      * The down constraint
20156      * @property maxY
20157      * @type int
20158      * @private
20159      */
20160     maxY: 0,
20161
20162     /**
20163      * Maintain offsets when we resetconstraints.  Set to true when you want
20164      * the position of the element relative to its parent to stay the same
20165      * when the page changes
20166      *
20167      * @property maintainOffset
20168      * @type boolean
20169      */
20170     maintainOffset: false,
20171
20172     /**
20173      * Array of pixel locations the element will snap to if we specified a
20174      * horizontal graduation/interval.  This array is generated automatically
20175      * when you define a tick interval.
20176      * @property xTicks
20177      * @type int[]
20178      */
20179     xTicks: null,
20180
20181     /**
20182      * Array of pixel locations the element will snap to if we specified a
20183      * vertical graduation/interval.  This array is generated automatically
20184      * when you define a tick interval.
20185      * @property yTicks
20186      * @type int[]
20187      */
20188     yTicks: null,
20189
20190     /**
20191      * By default the drag and drop instance will only respond to the primary
20192      * button click (left button for a right-handed mouse).  Set to true to
20193      * allow drag and drop to start with any mouse click that is propogated
20194      * by the browser
20195      * @property primaryButtonOnly
20196      * @type boolean
20197      */
20198     primaryButtonOnly: true,
20199
20200     /**
20201      * The availabe property is false until the linked dom element is accessible.
20202      * @property available
20203      * @type boolean
20204      */
20205     available: false,
20206
20207     /**
20208      * By default, drags can only be initiated if the mousedown occurs in the
20209      * region the linked element is.  This is done in part to work around a
20210      * bug in some browsers that mis-report the mousedown if the previous
20211      * mouseup happened outside of the window.  This property is set to true
20212      * if outer handles are defined.
20213      *
20214      * @property hasOuterHandles
20215      * @type boolean
20216      * @default false
20217      */
20218     hasOuterHandles: false,
20219
20220     /**
20221      * Code that executes immediately before the startDrag event
20222      * @method b4StartDrag
20223      * @private
20224      */
20225     b4StartDrag: function(x, y) { },
20226
20227     /**
20228      * Abstract method called after a drag/drop object is clicked
20229      * and the drag or mousedown time thresholds have beeen met.
20230      * @method startDrag
20231      * @param {int} X click location
20232      * @param {int} Y click location
20233      */
20234     startDrag: function(x, y) { /* override this */ },
20235
20236     /**
20237      * Code that executes immediately before the onDrag event
20238      * @method b4Drag
20239      * @private
20240      */
20241     b4Drag: function(e) { },
20242
20243     /**
20244      * Abstract method called during the onMouseMove event while dragging an
20245      * object.
20246      * @method onDrag
20247      * @param {Event} e the mousemove event
20248      */
20249     onDrag: function(e) { /* override this */ },
20250
20251     /**
20252      * Abstract method called when this element fist begins hovering over
20253      * another DragDrop obj
20254      * @method onDragEnter
20255      * @param {Event} e the mousemove event
20256      * @param {String|DragDrop[]} id In POINT mode, the element
20257      * id this is hovering over.  In INTERSECT mode, an array of one or more
20258      * dragdrop items being hovered over.
20259      */
20260     onDragEnter: function(e, id) { /* override this */ },
20261
20262     /**
20263      * Code that executes immediately before the onDragOver event
20264      * @method b4DragOver
20265      * @private
20266      */
20267     b4DragOver: function(e) { },
20268
20269     /**
20270      * Abstract method called when this element is hovering over another
20271      * DragDrop obj
20272      * @method onDragOver
20273      * @param {Event} e the mousemove event
20274      * @param {String|DragDrop[]} id In POINT mode, the element
20275      * id this is hovering over.  In INTERSECT mode, an array of dd items
20276      * being hovered over.
20277      */
20278     onDragOver: function(e, id) { /* override this */ },
20279
20280     /**
20281      * Code that executes immediately before the onDragOut event
20282      * @method b4DragOut
20283      * @private
20284      */
20285     b4DragOut: function(e) { },
20286
20287     /**
20288      * Abstract method called when we are no longer hovering over an element
20289      * @method onDragOut
20290      * @param {Event} e the mousemove event
20291      * @param {String|DragDrop[]} id In POINT mode, the element
20292      * id this was hovering over.  In INTERSECT mode, an array of dd items
20293      * that the mouse is no longer over.
20294      */
20295     onDragOut: function(e, id) { /* override this */ },
20296
20297     /**
20298      * Code that executes immediately before the onDragDrop event
20299      * @method b4DragDrop
20300      * @private
20301      */
20302     b4DragDrop: function(e) { },
20303
20304     /**
20305      * Abstract method called when this item is dropped on another DragDrop
20306      * obj
20307      * @method onDragDrop
20308      * @param {Event} e the mouseup event
20309      * @param {String|DragDrop[]} id In POINT mode, the element
20310      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20311      * was dropped on.
20312      */
20313     onDragDrop: function(e, id) { /* override this */ },
20314
20315     /**
20316      * Abstract method called when this item is dropped on an area with no
20317      * drop target
20318      * @method onInvalidDrop
20319      * @param {Event} e the mouseup event
20320      */
20321     onInvalidDrop: function(e) { /* override this */ },
20322
20323     /**
20324      * Code that executes immediately before the endDrag event
20325      * @method b4EndDrag
20326      * @private
20327      */
20328     b4EndDrag: function(e) { },
20329
20330     /**
20331      * Fired when we are done dragging the object
20332      * @method endDrag
20333      * @param {Event} e the mouseup event
20334      */
20335     endDrag: function(e) { /* override this */ },
20336
20337     /**
20338      * Code executed immediately before the onMouseDown event
20339      * @method b4MouseDown
20340      * @param {Event} e the mousedown event
20341      * @private
20342      */
20343     b4MouseDown: function(e) {  },
20344
20345     /**
20346      * Event handler that fires when a drag/drop obj gets a mousedown
20347      * @method onMouseDown
20348      * @param {Event} e the mousedown event
20349      */
20350     onMouseDown: function(e) { /* override this */ },
20351
20352     /**
20353      * Event handler that fires when a drag/drop obj gets a mouseup
20354      * @method onMouseUp
20355      * @param {Event} e the mouseup event
20356      */
20357     onMouseUp: function(e) { /* override this */ },
20358
20359     /**
20360      * Override the onAvailable method to do what is needed after the initial
20361      * position was determined.
20362      * @method onAvailable
20363      */
20364     onAvailable: function () {
20365     },
20366
20367     /*
20368      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20369      * @type Object
20370      */
20371     defaultPadding : {left:0, right:0, top:0, bottom:0},
20372
20373     /*
20374      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20375  *
20376  * Usage:
20377  <pre><code>
20378  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20379                 { dragElId: "existingProxyDiv" });
20380  dd.startDrag = function(){
20381      this.constrainTo("parent-id");
20382  };
20383  </code></pre>
20384  * Or you can initalize it using the {@link Roo.Element} object:
20385  <pre><code>
20386  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20387      startDrag : function(){
20388          this.constrainTo("parent-id");
20389      }
20390  });
20391  </code></pre>
20392      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20393      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20394      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20395      * an object containing the sides to pad. For example: {right:10, bottom:10}
20396      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20397      */
20398     constrainTo : function(constrainTo, pad, inContent){
20399         if(typeof pad == "number"){
20400             pad = {left: pad, right:pad, top:pad, bottom:pad};
20401         }
20402         pad = pad || this.defaultPadding;
20403         var b = Roo.get(this.getEl()).getBox();
20404         var ce = Roo.get(constrainTo);
20405         var s = ce.getScroll();
20406         var c, cd = ce.dom;
20407         if(cd == document.body){
20408             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20409         }else{
20410             xy = ce.getXY();
20411             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20412         }
20413
20414
20415         var topSpace = b.y - c.y;
20416         var leftSpace = b.x - c.x;
20417
20418         this.resetConstraints();
20419         this.setXConstraint(leftSpace - (pad.left||0), // left
20420                 c.width - leftSpace - b.width - (pad.right||0) //right
20421         );
20422         this.setYConstraint(topSpace - (pad.top||0), //top
20423                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20424         );
20425     },
20426
20427     /**
20428      * Returns a reference to the linked element
20429      * @method getEl
20430      * @return {HTMLElement} the html element
20431      */
20432     getEl: function() {
20433         if (!this._domRef) {
20434             this._domRef = Roo.getDom(this.id);
20435         }
20436
20437         return this._domRef;
20438     },
20439
20440     /**
20441      * Returns a reference to the actual element to drag.  By default this is
20442      * the same as the html element, but it can be assigned to another
20443      * element. An example of this can be found in Roo.dd.DDProxy
20444      * @method getDragEl
20445      * @return {HTMLElement} the html element
20446      */
20447     getDragEl: function() {
20448         return Roo.getDom(this.dragElId);
20449     },
20450
20451     /**
20452      * Sets up the DragDrop object.  Must be called in the constructor of any
20453      * Roo.dd.DragDrop subclass
20454      * @method init
20455      * @param id the id of the linked element
20456      * @param {String} sGroup the group of related items
20457      * @param {object} config configuration attributes
20458      */
20459     init: function(id, sGroup, config) {
20460         this.initTarget(id, sGroup, config);
20461         if (!Roo.isTouch) {
20462             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20463         }
20464         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20465         // Event.on(this.id, "selectstart", Event.preventDefault);
20466     },
20467
20468     /**
20469      * Initializes Targeting functionality only... the object does not
20470      * get a mousedown handler.
20471      * @method initTarget
20472      * @param id the id of the linked element
20473      * @param {String} sGroup the group of related items
20474      * @param {object} config configuration attributes
20475      */
20476     initTarget: function(id, sGroup, config) {
20477
20478         // configuration attributes
20479         this.config = config || {};
20480
20481         // create a local reference to the drag and drop manager
20482         this.DDM = Roo.dd.DDM;
20483         // initialize the groups array
20484         this.groups = {};
20485
20486         // assume that we have an element reference instead of an id if the
20487         // parameter is not a string
20488         if (typeof id !== "string") {
20489             id = Roo.id(id);
20490         }
20491
20492         // set the id
20493         this.id = id;
20494
20495         // add to an interaction group
20496         this.addToGroup((sGroup) ? sGroup : "default");
20497
20498         // We don't want to register this as the handle with the manager
20499         // so we just set the id rather than calling the setter.
20500         this.handleElId = id;
20501
20502         // the linked element is the element that gets dragged by default
20503         this.setDragElId(id);
20504
20505         // by default, clicked anchors will not start drag operations.
20506         this.invalidHandleTypes = { A: "A" };
20507         this.invalidHandleIds = {};
20508         this.invalidHandleClasses = [];
20509
20510         this.applyConfig();
20511
20512         this.handleOnAvailable();
20513     },
20514
20515     /**
20516      * Applies the configuration parameters that were passed into the constructor.
20517      * This is supposed to happen at each level through the inheritance chain.  So
20518      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20519      * DragDrop in order to get all of the parameters that are available in
20520      * each object.
20521      * @method applyConfig
20522      */
20523     applyConfig: function() {
20524
20525         // configurable properties:
20526         //    padding, isTarget, maintainOffset, primaryButtonOnly
20527         this.padding           = this.config.padding || [0, 0, 0, 0];
20528         this.isTarget          = (this.config.isTarget !== false);
20529         this.maintainOffset    = (this.config.maintainOffset);
20530         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20531
20532     },
20533
20534     /**
20535      * Executed when the linked element is available
20536      * @method handleOnAvailable
20537      * @private
20538      */
20539     handleOnAvailable: function() {
20540         this.available = true;
20541         this.resetConstraints();
20542         this.onAvailable();
20543     },
20544
20545      /**
20546      * Configures the padding for the target zone in px.  Effectively expands
20547      * (or reduces) the virtual object size for targeting calculations.
20548      * Supports css-style shorthand; if only one parameter is passed, all sides
20549      * will have that padding, and if only two are passed, the top and bottom
20550      * will have the first param, the left and right the second.
20551      * @method setPadding
20552      * @param {int} iTop    Top pad
20553      * @param {int} iRight  Right pad
20554      * @param {int} iBot    Bot pad
20555      * @param {int} iLeft   Left pad
20556      */
20557     setPadding: function(iTop, iRight, iBot, iLeft) {
20558         // this.padding = [iLeft, iRight, iTop, iBot];
20559         if (!iRight && 0 !== iRight) {
20560             this.padding = [iTop, iTop, iTop, iTop];
20561         } else if (!iBot && 0 !== iBot) {
20562             this.padding = [iTop, iRight, iTop, iRight];
20563         } else {
20564             this.padding = [iTop, iRight, iBot, iLeft];
20565         }
20566     },
20567
20568     /**
20569      * Stores the initial placement of the linked element.
20570      * @method setInitialPosition
20571      * @param {int} diffX   the X offset, default 0
20572      * @param {int} diffY   the Y offset, default 0
20573      */
20574     setInitPosition: function(diffX, diffY) {
20575         var el = this.getEl();
20576
20577         if (!this.DDM.verifyEl(el)) {
20578             return;
20579         }
20580
20581         var dx = diffX || 0;
20582         var dy = diffY || 0;
20583
20584         var p = Dom.getXY( el );
20585
20586         this.initPageX = p[0] - dx;
20587         this.initPageY = p[1] - dy;
20588
20589         this.lastPageX = p[0];
20590         this.lastPageY = p[1];
20591
20592
20593         this.setStartPosition(p);
20594     },
20595
20596     /**
20597      * Sets the start position of the element.  This is set when the obj
20598      * is initialized, the reset when a drag is started.
20599      * @method setStartPosition
20600      * @param pos current position (from previous lookup)
20601      * @private
20602      */
20603     setStartPosition: function(pos) {
20604         var p = pos || Dom.getXY( this.getEl() );
20605         this.deltaSetXY = null;
20606
20607         this.startPageX = p[0];
20608         this.startPageY = p[1];
20609     },
20610
20611     /**
20612      * Add this instance to a group of related drag/drop objects.  All
20613      * instances belong to at least one group, and can belong to as many
20614      * groups as needed.
20615      * @method addToGroup
20616      * @param sGroup {string} the name of the group
20617      */
20618     addToGroup: function(sGroup) {
20619         this.groups[sGroup] = true;
20620         this.DDM.regDragDrop(this, sGroup);
20621     },
20622
20623     /**
20624      * Remove's this instance from the supplied interaction group
20625      * @method removeFromGroup
20626      * @param {string}  sGroup  The group to drop
20627      */
20628     removeFromGroup: function(sGroup) {
20629         if (this.groups[sGroup]) {
20630             delete this.groups[sGroup];
20631         }
20632
20633         this.DDM.removeDDFromGroup(this, sGroup);
20634     },
20635
20636     /**
20637      * Allows you to specify that an element other than the linked element
20638      * will be moved with the cursor during a drag
20639      * @method setDragElId
20640      * @param id {string} the id of the element that will be used to initiate the drag
20641      */
20642     setDragElId: function(id) {
20643         this.dragElId = id;
20644     },
20645
20646     /**
20647      * Allows you to specify a child of the linked element that should be
20648      * used to initiate the drag operation.  An example of this would be if
20649      * you have a content div with text and links.  Clicking anywhere in the
20650      * content area would normally start the drag operation.  Use this method
20651      * to specify that an element inside of the content div is the element
20652      * that starts the drag operation.
20653      * @method setHandleElId
20654      * @param id {string} the id of the element that will be used to
20655      * initiate the drag.
20656      */
20657     setHandleElId: function(id) {
20658         if (typeof id !== "string") {
20659             id = Roo.id(id);
20660         }
20661         this.handleElId = id;
20662         this.DDM.regHandle(this.id, id);
20663     },
20664
20665     /**
20666      * Allows you to set an element outside of the linked element as a drag
20667      * handle
20668      * @method setOuterHandleElId
20669      * @param id the id of the element that will be used to initiate the drag
20670      */
20671     setOuterHandleElId: function(id) {
20672         if (typeof id !== "string") {
20673             id = Roo.id(id);
20674         }
20675         Event.on(id, "mousedown",
20676                 this.handleMouseDown, this);
20677         this.setHandleElId(id);
20678
20679         this.hasOuterHandles = true;
20680     },
20681
20682     /**
20683      * Remove all drag and drop hooks for this element
20684      * @method unreg
20685      */
20686     unreg: function() {
20687         Event.un(this.id, "mousedown",
20688                 this.handleMouseDown);
20689         Event.un(this.id, "touchstart",
20690                 this.handleMouseDown);
20691         this._domRef = null;
20692         this.DDM._remove(this);
20693     },
20694
20695     destroy : function(){
20696         this.unreg();
20697     },
20698
20699     /**
20700      * Returns true if this instance is locked, or the drag drop mgr is locked
20701      * (meaning that all drag/drop is disabled on the page.)
20702      * @method isLocked
20703      * @return {boolean} true if this obj or all drag/drop is locked, else
20704      * false
20705      */
20706     isLocked: function() {
20707         return (this.DDM.isLocked() || this.locked);
20708     },
20709
20710     /**
20711      * Fired when this object is clicked
20712      * @method handleMouseDown
20713      * @param {Event} e
20714      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20715      * @private
20716      */
20717     handleMouseDown: function(e, oDD){
20718      
20719         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20720             //Roo.log('not touch/ button !=0');
20721             return;
20722         }
20723         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20724             return; // double touch..
20725         }
20726         
20727
20728         if (this.isLocked()) {
20729             //Roo.log('locked');
20730             return;
20731         }
20732
20733         this.DDM.refreshCache(this.groups);
20734 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20735         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20736         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20737             //Roo.log('no outer handes or not over target');
20738                 // do nothing.
20739         } else {
20740 //            Roo.log('check validator');
20741             if (this.clickValidator(e)) {
20742 //                Roo.log('validate success');
20743                 // set the initial element position
20744                 this.setStartPosition();
20745
20746
20747                 this.b4MouseDown(e);
20748                 this.onMouseDown(e);
20749
20750                 this.DDM.handleMouseDown(e, this);
20751
20752                 this.DDM.stopEvent(e);
20753             } else {
20754
20755
20756             }
20757         }
20758     },
20759
20760     clickValidator: function(e) {
20761         var target = e.getTarget();
20762         return ( this.isValidHandleChild(target) &&
20763                     (this.id == this.handleElId ||
20764                         this.DDM.handleWasClicked(target, this.id)) );
20765     },
20766
20767     /**
20768      * Allows you to specify a tag name that should not start a drag operation
20769      * when clicked.  This is designed to facilitate embedding links within a
20770      * drag handle that do something other than start the drag.
20771      * @method addInvalidHandleType
20772      * @param {string} tagName the type of element to exclude
20773      */
20774     addInvalidHandleType: function(tagName) {
20775         var type = tagName.toUpperCase();
20776         this.invalidHandleTypes[type] = type;
20777     },
20778
20779     /**
20780      * Lets you to specify an element id for a child of a drag handle
20781      * that should not initiate a drag
20782      * @method addInvalidHandleId
20783      * @param {string} id the element id of the element you wish to ignore
20784      */
20785     addInvalidHandleId: function(id) {
20786         if (typeof id !== "string") {
20787             id = Roo.id(id);
20788         }
20789         this.invalidHandleIds[id] = id;
20790     },
20791
20792     /**
20793      * Lets you specify a css class of elements that will not initiate a drag
20794      * @method addInvalidHandleClass
20795      * @param {string} cssClass the class of the elements you wish to ignore
20796      */
20797     addInvalidHandleClass: function(cssClass) {
20798         this.invalidHandleClasses.push(cssClass);
20799     },
20800
20801     /**
20802      * Unsets an excluded tag name set by addInvalidHandleType
20803      * @method removeInvalidHandleType
20804      * @param {string} tagName the type of element to unexclude
20805      */
20806     removeInvalidHandleType: function(tagName) {
20807         var type = tagName.toUpperCase();
20808         // this.invalidHandleTypes[type] = null;
20809         delete this.invalidHandleTypes[type];
20810     },
20811
20812     /**
20813      * Unsets an invalid handle id
20814      * @method removeInvalidHandleId
20815      * @param {string} id the id of the element to re-enable
20816      */
20817     removeInvalidHandleId: function(id) {
20818         if (typeof id !== "string") {
20819             id = Roo.id(id);
20820         }
20821         delete this.invalidHandleIds[id];
20822     },
20823
20824     /**
20825      * Unsets an invalid css class
20826      * @method removeInvalidHandleClass
20827      * @param {string} cssClass the class of the element(s) you wish to
20828      * re-enable
20829      */
20830     removeInvalidHandleClass: function(cssClass) {
20831         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20832             if (this.invalidHandleClasses[i] == cssClass) {
20833                 delete this.invalidHandleClasses[i];
20834             }
20835         }
20836     },
20837
20838     /**
20839      * Checks the tag exclusion list to see if this click should be ignored
20840      * @method isValidHandleChild
20841      * @param {HTMLElement} node the HTMLElement to evaluate
20842      * @return {boolean} true if this is a valid tag type, false if not
20843      */
20844     isValidHandleChild: function(node) {
20845
20846         var valid = true;
20847         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20848         var nodeName;
20849         try {
20850             nodeName = node.nodeName.toUpperCase();
20851         } catch(e) {
20852             nodeName = node.nodeName;
20853         }
20854         valid = valid && !this.invalidHandleTypes[nodeName];
20855         valid = valid && !this.invalidHandleIds[node.id];
20856
20857         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20858             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20859         }
20860
20861
20862         return valid;
20863
20864     },
20865
20866     /**
20867      * Create the array of horizontal tick marks if an interval was specified
20868      * in setXConstraint().
20869      * @method setXTicks
20870      * @private
20871      */
20872     setXTicks: function(iStartX, iTickSize) {
20873         this.xTicks = [];
20874         this.xTickSize = iTickSize;
20875
20876         var tickMap = {};
20877
20878         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20879             if (!tickMap[i]) {
20880                 this.xTicks[this.xTicks.length] = i;
20881                 tickMap[i] = true;
20882             }
20883         }
20884
20885         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20886             if (!tickMap[i]) {
20887                 this.xTicks[this.xTicks.length] = i;
20888                 tickMap[i] = true;
20889             }
20890         }
20891
20892         this.xTicks.sort(this.DDM.numericSort) ;
20893     },
20894
20895     /**
20896      * Create the array of vertical tick marks if an interval was specified in
20897      * setYConstraint().
20898      * @method setYTicks
20899      * @private
20900      */
20901     setYTicks: function(iStartY, iTickSize) {
20902         this.yTicks = [];
20903         this.yTickSize = iTickSize;
20904
20905         var tickMap = {};
20906
20907         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20908             if (!tickMap[i]) {
20909                 this.yTicks[this.yTicks.length] = i;
20910                 tickMap[i] = true;
20911             }
20912         }
20913
20914         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20915             if (!tickMap[i]) {
20916                 this.yTicks[this.yTicks.length] = i;
20917                 tickMap[i] = true;
20918             }
20919         }
20920
20921         this.yTicks.sort(this.DDM.numericSort) ;
20922     },
20923
20924     /**
20925      * By default, the element can be dragged any place on the screen.  Use
20926      * this method to limit the horizontal travel of the element.  Pass in
20927      * 0,0 for the parameters if you want to lock the drag to the y axis.
20928      * @method setXConstraint
20929      * @param {int} iLeft the number of pixels the element can move to the left
20930      * @param {int} iRight the number of pixels the element can move to the
20931      * right
20932      * @param {int} iTickSize optional parameter for specifying that the
20933      * element
20934      * should move iTickSize pixels at a time.
20935      */
20936     setXConstraint: function(iLeft, iRight, iTickSize) {
20937         this.leftConstraint = iLeft;
20938         this.rightConstraint = iRight;
20939
20940         this.minX = this.initPageX - iLeft;
20941         this.maxX = this.initPageX + iRight;
20942         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20943
20944         this.constrainX = true;
20945     },
20946
20947     /**
20948      * Clears any constraints applied to this instance.  Also clears ticks
20949      * since they can't exist independent of a constraint at this time.
20950      * @method clearConstraints
20951      */
20952     clearConstraints: function() {
20953         this.constrainX = false;
20954         this.constrainY = false;
20955         this.clearTicks();
20956     },
20957
20958     /**
20959      * Clears any tick interval defined for this instance
20960      * @method clearTicks
20961      */
20962     clearTicks: function() {
20963         this.xTicks = null;
20964         this.yTicks = null;
20965         this.xTickSize = 0;
20966         this.yTickSize = 0;
20967     },
20968
20969     /**
20970      * By default, the element can be dragged any place on the screen.  Set
20971      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20972      * parameters if you want to lock the drag to the x axis.
20973      * @method setYConstraint
20974      * @param {int} iUp the number of pixels the element can move up
20975      * @param {int} iDown the number of pixels the element can move down
20976      * @param {int} iTickSize optional parameter for specifying that the
20977      * element should move iTickSize pixels at a time.
20978      */
20979     setYConstraint: function(iUp, iDown, iTickSize) {
20980         this.topConstraint = iUp;
20981         this.bottomConstraint = iDown;
20982
20983         this.minY = this.initPageY - iUp;
20984         this.maxY = this.initPageY + iDown;
20985         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20986
20987         this.constrainY = true;
20988
20989     },
20990
20991     /**
20992      * resetConstraints must be called if you manually reposition a dd element.
20993      * @method resetConstraints
20994      * @param {boolean} maintainOffset
20995      */
20996     resetConstraints: function() {
20997
20998
20999         // Maintain offsets if necessary
21000         if (this.initPageX || this.initPageX === 0) {
21001             // figure out how much this thing has moved
21002             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21003             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21004
21005             this.setInitPosition(dx, dy);
21006
21007         // This is the first time we have detected the element's position
21008         } else {
21009             this.setInitPosition();
21010         }
21011
21012         if (this.constrainX) {
21013             this.setXConstraint( this.leftConstraint,
21014                                  this.rightConstraint,
21015                                  this.xTickSize        );
21016         }
21017
21018         if (this.constrainY) {
21019             this.setYConstraint( this.topConstraint,
21020                                  this.bottomConstraint,
21021                                  this.yTickSize         );
21022         }
21023     },
21024
21025     /**
21026      * Normally the drag element is moved pixel by pixel, but we can specify
21027      * that it move a number of pixels at a time.  This method resolves the
21028      * location when we have it set up like this.
21029      * @method getTick
21030      * @param {int} val where we want to place the object
21031      * @param {int[]} tickArray sorted array of valid points
21032      * @return {int} the closest tick
21033      * @private
21034      */
21035     getTick: function(val, tickArray) {
21036
21037         if (!tickArray) {
21038             // If tick interval is not defined, it is effectively 1 pixel,
21039             // so we return the value passed to us.
21040             return val;
21041         } else if (tickArray[0] >= val) {
21042             // The value is lower than the first tick, so we return the first
21043             // tick.
21044             return tickArray[0];
21045         } else {
21046             for (var i=0, len=tickArray.length; i<len; ++i) {
21047                 var next = i + 1;
21048                 if (tickArray[next] && tickArray[next] >= val) {
21049                     var diff1 = val - tickArray[i];
21050                     var diff2 = tickArray[next] - val;
21051                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21052                 }
21053             }
21054
21055             // The value is larger than the last tick, so we return the last
21056             // tick.
21057             return tickArray[tickArray.length - 1];
21058         }
21059     },
21060
21061     /**
21062      * toString method
21063      * @method toString
21064      * @return {string} string representation of the dd obj
21065      */
21066     toString: function() {
21067         return ("DragDrop " + this.id);
21068     }
21069
21070 });
21071
21072 })();
21073 /*
21074  * Based on:
21075  * Ext JS Library 1.1.1
21076  * Copyright(c) 2006-2007, Ext JS, LLC.
21077  *
21078  * Originally Released Under LGPL - original licence link has changed is not relivant.
21079  *
21080  * Fork - LGPL
21081  * <script type="text/javascript">
21082  */
21083
21084
21085 /**
21086  * The drag and drop utility provides a framework for building drag and drop
21087  * applications.  In addition to enabling drag and drop for specific elements,
21088  * the drag and drop elements are tracked by the manager class, and the
21089  * interactions between the various elements are tracked during the drag and
21090  * the implementing code is notified about these important moments.
21091  */
21092
21093 // Only load the library once.  Rewriting the manager class would orphan
21094 // existing drag and drop instances.
21095 if (!Roo.dd.DragDropMgr) {
21096
21097 /**
21098  * @class Roo.dd.DragDropMgr
21099  * DragDropMgr is a singleton that tracks the element interaction for
21100  * all DragDrop items in the window.  Generally, you will not call
21101  * this class directly, but it does have helper methods that could
21102  * be useful in your DragDrop implementations.
21103  * @static
21104  */
21105 Roo.dd.DragDropMgr = function() {
21106
21107     var Event = Roo.EventManager;
21108
21109     return {
21110
21111         /**
21112          * Two dimensional Array of registered DragDrop objects.  The first
21113          * dimension is the DragDrop item group, the second the DragDrop
21114          * object.
21115          * @property ids
21116          * @type {string: string}
21117          * @private
21118          * @static
21119          */
21120         ids: {},
21121
21122         /**
21123          * Array of element ids defined as drag handles.  Used to determine
21124          * if the element that generated the mousedown event is actually the
21125          * handle and not the html element itself.
21126          * @property handleIds
21127          * @type {string: string}
21128          * @private
21129          * @static
21130          */
21131         handleIds: {},
21132
21133         /**
21134          * the DragDrop object that is currently being dragged
21135          * @property dragCurrent
21136          * @type DragDrop
21137          * @private
21138          * @static
21139          **/
21140         dragCurrent: null,
21141
21142         /**
21143          * the DragDrop object(s) that are being hovered over
21144          * @property dragOvers
21145          * @type Array
21146          * @private
21147          * @static
21148          */
21149         dragOvers: {},
21150
21151         /**
21152          * the X distance between the cursor and the object being dragged
21153          * @property deltaX
21154          * @type int
21155          * @private
21156          * @static
21157          */
21158         deltaX: 0,
21159
21160         /**
21161          * the Y distance between the cursor and the object being dragged
21162          * @property deltaY
21163          * @type int
21164          * @private
21165          * @static
21166          */
21167         deltaY: 0,
21168
21169         /**
21170          * Flag to determine if we should prevent the default behavior of the
21171          * events we define. By default this is true, but this can be set to
21172          * false if you need the default behavior (not recommended)
21173          * @property preventDefault
21174          * @type boolean
21175          * @static
21176          */
21177         preventDefault: true,
21178
21179         /**
21180          * Flag to determine if we should stop the propagation of the events
21181          * we generate. This is true by default but you may want to set it to
21182          * false if the html element contains other features that require the
21183          * mouse click.
21184          * @property stopPropagation
21185          * @type boolean
21186          * @static
21187          */
21188         stopPropagation: true,
21189
21190         /**
21191          * Internal flag that is set to true when drag and drop has been
21192          * intialized
21193          * @property initialized
21194          * @private
21195          * @static
21196          */
21197         initalized: false,
21198
21199         /**
21200          * All drag and drop can be disabled.
21201          * @property locked
21202          * @private
21203          * @static
21204          */
21205         locked: false,
21206
21207         /**
21208          * Called the first time an element is registered.
21209          * @method init
21210          * @private
21211          * @static
21212          */
21213         init: function() {
21214             this.initialized = true;
21215         },
21216
21217         /**
21218          * In point mode, drag and drop interaction is defined by the
21219          * location of the cursor during the drag/drop
21220          * @property POINT
21221          * @type int
21222          * @static
21223          */
21224         POINT: 0,
21225
21226         /**
21227          * In intersect mode, drag and drop interactio nis defined by the
21228          * overlap of two or more drag and drop objects.
21229          * @property INTERSECT
21230          * @type int
21231          * @static
21232          */
21233         INTERSECT: 1,
21234
21235         /**
21236          * The current drag and drop mode.  Default: POINT
21237          * @property mode
21238          * @type int
21239          * @static
21240          */
21241         mode: 0,
21242
21243         /**
21244          * Runs method on all drag and drop objects
21245          * @method _execOnAll
21246          * @private
21247          * @static
21248          */
21249         _execOnAll: function(sMethod, args) {
21250             for (var i in this.ids) {
21251                 for (var j in this.ids[i]) {
21252                     var oDD = this.ids[i][j];
21253                     if (! this.isTypeOfDD(oDD)) {
21254                         continue;
21255                     }
21256                     oDD[sMethod].apply(oDD, args);
21257                 }
21258             }
21259         },
21260
21261         /**
21262          * Drag and drop initialization.  Sets up the global event handlers
21263          * @method _onLoad
21264          * @private
21265          * @static
21266          */
21267         _onLoad: function() {
21268
21269             this.init();
21270
21271             if (!Roo.isTouch) {
21272                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21273                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21274             }
21275             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21276             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21277             
21278             Event.on(window,   "unload",    this._onUnload, this, true);
21279             Event.on(window,   "resize",    this._onResize, this, true);
21280             // Event.on(window,   "mouseout",    this._test);
21281
21282         },
21283
21284         /**
21285          * Reset constraints on all drag and drop objs
21286          * @method _onResize
21287          * @private
21288          * @static
21289          */
21290         _onResize: function(e) {
21291             this._execOnAll("resetConstraints", []);
21292         },
21293
21294         /**
21295          * Lock all drag and drop functionality
21296          * @method lock
21297          * @static
21298          */
21299         lock: function() { this.locked = true; },
21300
21301         /**
21302          * Unlock all drag and drop functionality
21303          * @method unlock
21304          * @static
21305          */
21306         unlock: function() { this.locked = false; },
21307
21308         /**
21309          * Is drag and drop locked?
21310          * @method isLocked
21311          * @return {boolean} True if drag and drop is locked, false otherwise.
21312          * @static
21313          */
21314         isLocked: function() { return this.locked; },
21315
21316         /**
21317          * Location cache that is set for all drag drop objects when a drag is
21318          * initiated, cleared when the drag is finished.
21319          * @property locationCache
21320          * @private
21321          * @static
21322          */
21323         locationCache: {},
21324
21325         /**
21326          * Set useCache to false if you want to force object the lookup of each
21327          * drag and drop linked element constantly during a drag.
21328          * @property useCache
21329          * @type boolean
21330          * @static
21331          */
21332         useCache: true,
21333
21334         /**
21335          * The number of pixels that the mouse needs to move after the
21336          * mousedown before the drag is initiated.  Default=3;
21337          * @property clickPixelThresh
21338          * @type int
21339          * @static
21340          */
21341         clickPixelThresh: 3,
21342
21343         /**
21344          * The number of milliseconds after the mousedown event to initiate the
21345          * drag if we don't get a mouseup event. Default=1000
21346          * @property clickTimeThresh
21347          * @type int
21348          * @static
21349          */
21350         clickTimeThresh: 350,
21351
21352         /**
21353          * Flag that indicates that either the drag pixel threshold or the
21354          * mousdown time threshold has been met
21355          * @property dragThreshMet
21356          * @type boolean
21357          * @private
21358          * @static
21359          */
21360         dragThreshMet: false,
21361
21362         /**
21363          * Timeout used for the click time threshold
21364          * @property clickTimeout
21365          * @type Object
21366          * @private
21367          * @static
21368          */
21369         clickTimeout: null,
21370
21371         /**
21372          * The X position of the mousedown event stored for later use when a
21373          * drag threshold is met.
21374          * @property startX
21375          * @type int
21376          * @private
21377          * @static
21378          */
21379         startX: 0,
21380
21381         /**
21382          * The Y position of the mousedown event stored for later use when a
21383          * drag threshold is met.
21384          * @property startY
21385          * @type int
21386          * @private
21387          * @static
21388          */
21389         startY: 0,
21390
21391         /**
21392          * Each DragDrop instance must be registered with the DragDropMgr.
21393          * This is executed in DragDrop.init()
21394          * @method regDragDrop
21395          * @param {DragDrop} oDD the DragDrop object to register
21396          * @param {String} sGroup the name of the group this element belongs to
21397          * @static
21398          */
21399         regDragDrop: function(oDD, sGroup) {
21400             if (!this.initialized) { this.init(); }
21401
21402             if (!this.ids[sGroup]) {
21403                 this.ids[sGroup] = {};
21404             }
21405             this.ids[sGroup][oDD.id] = oDD;
21406         },
21407
21408         /**
21409          * Removes the supplied dd instance from the supplied group. Executed
21410          * by DragDrop.removeFromGroup, so don't call this function directly.
21411          * @method removeDDFromGroup
21412          * @private
21413          * @static
21414          */
21415         removeDDFromGroup: function(oDD, sGroup) {
21416             if (!this.ids[sGroup]) {
21417                 this.ids[sGroup] = {};
21418             }
21419
21420             var obj = this.ids[sGroup];
21421             if (obj && obj[oDD.id]) {
21422                 delete obj[oDD.id];
21423             }
21424         },
21425
21426         /**
21427          * Unregisters a drag and drop item.  This is executed in
21428          * DragDrop.unreg, use that method instead of calling this directly.
21429          * @method _remove
21430          * @private
21431          * @static
21432          */
21433         _remove: function(oDD) {
21434             for (var g in oDD.groups) {
21435                 if (g && this.ids[g][oDD.id]) {
21436                     delete this.ids[g][oDD.id];
21437                 }
21438             }
21439             delete this.handleIds[oDD.id];
21440         },
21441
21442         /**
21443          * Each DragDrop handle element must be registered.  This is done
21444          * automatically when executing DragDrop.setHandleElId()
21445          * @method regHandle
21446          * @param {String} sDDId the DragDrop id this element is a handle for
21447          * @param {String} sHandleId the id of the element that is the drag
21448          * handle
21449          * @static
21450          */
21451         regHandle: function(sDDId, sHandleId) {
21452             if (!this.handleIds[sDDId]) {
21453                 this.handleIds[sDDId] = {};
21454             }
21455             this.handleIds[sDDId][sHandleId] = sHandleId;
21456         },
21457
21458         /**
21459          * Utility function to determine if a given element has been
21460          * registered as a drag drop item.
21461          * @method isDragDrop
21462          * @param {String} id the element id to check
21463          * @return {boolean} true if this element is a DragDrop item,
21464          * false otherwise
21465          * @static
21466          */
21467         isDragDrop: function(id) {
21468             return ( this.getDDById(id) ) ? true : false;
21469         },
21470
21471         /**
21472          * Returns the drag and drop instances that are in all groups the
21473          * passed in instance belongs to.
21474          * @method getRelated
21475          * @param {DragDrop} p_oDD the obj to get related data for
21476          * @param {boolean} bTargetsOnly if true, only return targetable objs
21477          * @return {DragDrop[]} the related instances
21478          * @static
21479          */
21480         getRelated: function(p_oDD, bTargetsOnly) {
21481             var oDDs = [];
21482             for (var i in p_oDD.groups) {
21483                 for (j in this.ids[i]) {
21484                     var dd = this.ids[i][j];
21485                     if (! this.isTypeOfDD(dd)) {
21486                         continue;
21487                     }
21488                     if (!bTargetsOnly || dd.isTarget) {
21489                         oDDs[oDDs.length] = dd;
21490                     }
21491                 }
21492             }
21493
21494             return oDDs;
21495         },
21496
21497         /**
21498          * Returns true if the specified dd target is a legal target for
21499          * the specifice drag obj
21500          * @method isLegalTarget
21501          * @param {DragDrop} the drag obj
21502          * @param {DragDrop} the target
21503          * @return {boolean} true if the target is a legal target for the
21504          * dd obj
21505          * @static
21506          */
21507         isLegalTarget: function (oDD, oTargetDD) {
21508             var targets = this.getRelated(oDD, true);
21509             for (var i=0, len=targets.length;i<len;++i) {
21510                 if (targets[i].id == oTargetDD.id) {
21511                     return true;
21512                 }
21513             }
21514
21515             return false;
21516         },
21517
21518         /**
21519          * My goal is to be able to transparently determine if an object is
21520          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21521          * returns "object", oDD.constructor.toString() always returns
21522          * "DragDrop" and not the name of the subclass.  So for now it just
21523          * evaluates a well-known variable in DragDrop.
21524          * @method isTypeOfDD
21525          * @param {Object} the object to evaluate
21526          * @return {boolean} true if typeof oDD = DragDrop
21527          * @static
21528          */
21529         isTypeOfDD: function (oDD) {
21530             return (oDD && oDD.__ygDragDrop);
21531         },
21532
21533         /**
21534          * Utility function to determine if a given element has been
21535          * registered as a drag drop handle for the given Drag Drop object.
21536          * @method isHandle
21537          * @param {String} id the element id to check
21538          * @return {boolean} true if this element is a DragDrop handle, false
21539          * otherwise
21540          * @static
21541          */
21542         isHandle: function(sDDId, sHandleId) {
21543             return ( this.handleIds[sDDId] &&
21544                             this.handleIds[sDDId][sHandleId] );
21545         },
21546
21547         /**
21548          * Returns the DragDrop instance for a given id
21549          * @method getDDById
21550          * @param {String} id the id of the DragDrop object
21551          * @return {DragDrop} the drag drop object, null if it is not found
21552          * @static
21553          */
21554         getDDById: function(id) {
21555             for (var i in this.ids) {
21556                 if (this.ids[i][id]) {
21557                     return this.ids[i][id];
21558                 }
21559             }
21560             return null;
21561         },
21562
21563         /**
21564          * Fired after a registered DragDrop object gets the mousedown event.
21565          * Sets up the events required to track the object being dragged
21566          * @method handleMouseDown
21567          * @param {Event} e the event
21568          * @param oDD the DragDrop object being dragged
21569          * @private
21570          * @static
21571          */
21572         handleMouseDown: function(e, oDD) {
21573             if(Roo.QuickTips){
21574                 Roo.QuickTips.disable();
21575             }
21576             this.currentTarget = e.getTarget();
21577
21578             this.dragCurrent = oDD;
21579
21580             var el = oDD.getEl();
21581
21582             // track start position
21583             this.startX = e.getPageX();
21584             this.startY = e.getPageY();
21585
21586             this.deltaX = this.startX - el.offsetLeft;
21587             this.deltaY = this.startY - el.offsetTop;
21588
21589             this.dragThreshMet = false;
21590
21591             this.clickTimeout = setTimeout(
21592                     function() {
21593                         var DDM = Roo.dd.DDM;
21594                         DDM.startDrag(DDM.startX, DDM.startY);
21595                     },
21596                     this.clickTimeThresh );
21597         },
21598
21599         /**
21600          * Fired when either the drag pixel threshol or the mousedown hold
21601          * time threshold has been met.
21602          * @method startDrag
21603          * @param x {int} the X position of the original mousedown
21604          * @param y {int} the Y position of the original mousedown
21605          * @static
21606          */
21607         startDrag: function(x, y) {
21608             clearTimeout(this.clickTimeout);
21609             if (this.dragCurrent) {
21610                 this.dragCurrent.b4StartDrag(x, y);
21611                 this.dragCurrent.startDrag(x, y);
21612             }
21613             this.dragThreshMet = true;
21614         },
21615
21616         /**
21617          * Internal function to handle the mouseup event.  Will be invoked
21618          * from the context of the document.
21619          * @method handleMouseUp
21620          * @param {Event} e the event
21621          * @private
21622          * @static
21623          */
21624         handleMouseUp: function(e) {
21625
21626             if(Roo.QuickTips){
21627                 Roo.QuickTips.enable();
21628             }
21629             if (! this.dragCurrent) {
21630                 return;
21631             }
21632
21633             clearTimeout(this.clickTimeout);
21634
21635             if (this.dragThreshMet) {
21636                 this.fireEvents(e, true);
21637             } else {
21638             }
21639
21640             this.stopDrag(e);
21641
21642             this.stopEvent(e);
21643         },
21644
21645         /**
21646          * Utility to stop event propagation and event default, if these
21647          * features are turned on.
21648          * @method stopEvent
21649          * @param {Event} e the event as returned by this.getEvent()
21650          * @static
21651          */
21652         stopEvent: function(e){
21653             if(this.stopPropagation) {
21654                 e.stopPropagation();
21655             }
21656
21657             if (this.preventDefault) {
21658                 e.preventDefault();
21659             }
21660         },
21661
21662         /**
21663          * Internal function to clean up event handlers after the drag
21664          * operation is complete
21665          * @method stopDrag
21666          * @param {Event} e the event
21667          * @private
21668          * @static
21669          */
21670         stopDrag: function(e) {
21671             // Fire the drag end event for the item that was dragged
21672             if (this.dragCurrent) {
21673                 if (this.dragThreshMet) {
21674                     this.dragCurrent.b4EndDrag(e);
21675                     this.dragCurrent.endDrag(e);
21676                 }
21677
21678                 this.dragCurrent.onMouseUp(e);
21679             }
21680
21681             this.dragCurrent = null;
21682             this.dragOvers = {};
21683         },
21684
21685         /**
21686          * Internal function to handle the mousemove event.  Will be invoked
21687          * from the context of the html element.
21688          *
21689          * @TODO figure out what we can do about mouse events lost when the
21690          * user drags objects beyond the window boundary.  Currently we can
21691          * detect this in internet explorer by verifying that the mouse is
21692          * down during the mousemove event.  Firefox doesn't give us the
21693          * button state on the mousemove event.
21694          * @method handleMouseMove
21695          * @param {Event} e the event
21696          * @private
21697          * @static
21698          */
21699         handleMouseMove: function(e) {
21700             if (! this.dragCurrent) {
21701                 return true;
21702             }
21703
21704             // var button = e.which || e.button;
21705
21706             // check for IE mouseup outside of page boundary
21707             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21708                 this.stopEvent(e);
21709                 return this.handleMouseUp(e);
21710             }
21711
21712             if (!this.dragThreshMet) {
21713                 var diffX = Math.abs(this.startX - e.getPageX());
21714                 var diffY = Math.abs(this.startY - e.getPageY());
21715                 if (diffX > this.clickPixelThresh ||
21716                             diffY > this.clickPixelThresh) {
21717                     this.startDrag(this.startX, this.startY);
21718                 }
21719             }
21720
21721             if (this.dragThreshMet) {
21722                 this.dragCurrent.b4Drag(e);
21723                 this.dragCurrent.onDrag(e);
21724                 if(!this.dragCurrent.moveOnly){
21725                     this.fireEvents(e, false);
21726                 }
21727             }
21728
21729             this.stopEvent(e);
21730
21731             return true;
21732         },
21733
21734         /**
21735          * Iterates over all of the DragDrop elements to find ones we are
21736          * hovering over or dropping on
21737          * @method fireEvents
21738          * @param {Event} e the event
21739          * @param {boolean} isDrop is this a drop op or a mouseover op?
21740          * @private
21741          * @static
21742          */
21743         fireEvents: function(e, isDrop) {
21744             var dc = this.dragCurrent;
21745
21746             // If the user did the mouse up outside of the window, we could
21747             // get here even though we have ended the drag.
21748             if (!dc || dc.isLocked()) {
21749                 return;
21750             }
21751
21752             var pt = e.getPoint();
21753
21754             // cache the previous dragOver array
21755             var oldOvers = [];
21756
21757             var outEvts   = [];
21758             var overEvts  = [];
21759             var dropEvts  = [];
21760             var enterEvts = [];
21761
21762             // Check to see if the object(s) we were hovering over is no longer
21763             // being hovered over so we can fire the onDragOut event
21764             for (var i in this.dragOvers) {
21765
21766                 var ddo = this.dragOvers[i];
21767
21768                 if (! this.isTypeOfDD(ddo)) {
21769                     continue;
21770                 }
21771
21772                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21773                     outEvts.push( ddo );
21774                 }
21775
21776                 oldOvers[i] = true;
21777                 delete this.dragOvers[i];
21778             }
21779
21780             for (var sGroup in dc.groups) {
21781
21782                 if ("string" != typeof sGroup) {
21783                     continue;
21784                 }
21785
21786                 for (i in this.ids[sGroup]) {
21787                     var oDD = this.ids[sGroup][i];
21788                     if (! this.isTypeOfDD(oDD)) {
21789                         continue;
21790                     }
21791
21792                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21793                         if (this.isOverTarget(pt, oDD, this.mode)) {
21794                             // look for drop interactions
21795                             if (isDrop) {
21796                                 dropEvts.push( oDD );
21797                             // look for drag enter and drag over interactions
21798                             } else {
21799
21800                                 // initial drag over: dragEnter fires
21801                                 if (!oldOvers[oDD.id]) {
21802                                     enterEvts.push( oDD );
21803                                 // subsequent drag overs: dragOver fires
21804                                 } else {
21805                                     overEvts.push( oDD );
21806                                 }
21807
21808                                 this.dragOvers[oDD.id] = oDD;
21809                             }
21810                         }
21811                     }
21812                 }
21813             }
21814
21815             if (this.mode) {
21816                 if (outEvts.length) {
21817                     dc.b4DragOut(e, outEvts);
21818                     dc.onDragOut(e, outEvts);
21819                 }
21820
21821                 if (enterEvts.length) {
21822                     dc.onDragEnter(e, enterEvts);
21823                 }
21824
21825                 if (overEvts.length) {
21826                     dc.b4DragOver(e, overEvts);
21827                     dc.onDragOver(e, overEvts);
21828                 }
21829
21830                 if (dropEvts.length) {
21831                     dc.b4DragDrop(e, dropEvts);
21832                     dc.onDragDrop(e, dropEvts);
21833                 }
21834
21835             } else {
21836                 // fire dragout events
21837                 var len = 0;
21838                 for (i=0, len=outEvts.length; i<len; ++i) {
21839                     dc.b4DragOut(e, outEvts[i].id);
21840                     dc.onDragOut(e, outEvts[i].id);
21841                 }
21842
21843                 // fire enter events
21844                 for (i=0,len=enterEvts.length; i<len; ++i) {
21845                     // dc.b4DragEnter(e, oDD.id);
21846                     dc.onDragEnter(e, enterEvts[i].id);
21847                 }
21848
21849                 // fire over events
21850                 for (i=0,len=overEvts.length; i<len; ++i) {
21851                     dc.b4DragOver(e, overEvts[i].id);
21852                     dc.onDragOver(e, overEvts[i].id);
21853                 }
21854
21855                 // fire drop events
21856                 for (i=0, len=dropEvts.length; i<len; ++i) {
21857                     dc.b4DragDrop(e, dropEvts[i].id);
21858                     dc.onDragDrop(e, dropEvts[i].id);
21859                 }
21860
21861             }
21862
21863             // notify about a drop that did not find a target
21864             if (isDrop && !dropEvts.length) {
21865                 dc.onInvalidDrop(e);
21866             }
21867
21868         },
21869
21870         /**
21871          * Helper function for getting the best match from the list of drag
21872          * and drop objects returned by the drag and drop events when we are
21873          * in INTERSECT mode.  It returns either the first object that the
21874          * cursor is over, or the object that has the greatest overlap with
21875          * the dragged element.
21876          * @method getBestMatch
21877          * @param  {DragDrop[]} dds The array of drag and drop objects
21878          * targeted
21879          * @return {DragDrop}       The best single match
21880          * @static
21881          */
21882         getBestMatch: function(dds) {
21883             var winner = null;
21884             // Return null if the input is not what we expect
21885             //if (!dds || !dds.length || dds.length == 0) {
21886                // winner = null;
21887             // If there is only one item, it wins
21888             //} else if (dds.length == 1) {
21889
21890             var len = dds.length;
21891
21892             if (len == 1) {
21893                 winner = dds[0];
21894             } else {
21895                 // Loop through the targeted items
21896                 for (var i=0; i<len; ++i) {
21897                     var dd = dds[i];
21898                     // If the cursor is over the object, it wins.  If the
21899                     // cursor is over multiple matches, the first one we come
21900                     // to wins.
21901                     if (dd.cursorIsOver) {
21902                         winner = dd;
21903                         break;
21904                     // Otherwise the object with the most overlap wins
21905                     } else {
21906                         if (!winner ||
21907                             winner.overlap.getArea() < dd.overlap.getArea()) {
21908                             winner = dd;
21909                         }
21910                     }
21911                 }
21912             }
21913
21914             return winner;
21915         },
21916
21917         /**
21918          * Refreshes the cache of the top-left and bottom-right points of the
21919          * drag and drop objects in the specified group(s).  This is in the
21920          * format that is stored in the drag and drop instance, so typical
21921          * usage is:
21922          * <code>
21923          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21924          * </code>
21925          * Alternatively:
21926          * <code>
21927          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21928          * </code>
21929          * @TODO this really should be an indexed array.  Alternatively this
21930          * method could accept both.
21931          * @method refreshCache
21932          * @param {Object} groups an associative array of groups to refresh
21933          * @static
21934          */
21935         refreshCache: function(groups) {
21936             for (var sGroup in groups) {
21937                 if ("string" != typeof sGroup) {
21938                     continue;
21939                 }
21940                 for (var i in this.ids[sGroup]) {
21941                     var oDD = this.ids[sGroup][i];
21942
21943                     if (this.isTypeOfDD(oDD)) {
21944                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21945                         var loc = this.getLocation(oDD);
21946                         if (loc) {
21947                             this.locationCache[oDD.id] = loc;
21948                         } else {
21949                             delete this.locationCache[oDD.id];
21950                             // this will unregister the drag and drop object if
21951                             // the element is not in a usable state
21952                             // oDD.unreg();
21953                         }
21954                     }
21955                 }
21956             }
21957         },
21958
21959         /**
21960          * This checks to make sure an element exists and is in the DOM.  The
21961          * main purpose is to handle cases where innerHTML is used to remove
21962          * drag and drop objects from the DOM.  IE provides an 'unspecified
21963          * error' when trying to access the offsetParent of such an element
21964          * @method verifyEl
21965          * @param {HTMLElement} el the element to check
21966          * @return {boolean} true if the element looks usable
21967          * @static
21968          */
21969         verifyEl: function(el) {
21970             if (el) {
21971                 var parent;
21972                 if(Roo.isIE){
21973                     try{
21974                         parent = el.offsetParent;
21975                     }catch(e){}
21976                 }else{
21977                     parent = el.offsetParent;
21978                 }
21979                 if (parent) {
21980                     return true;
21981                 }
21982             }
21983
21984             return false;
21985         },
21986
21987         /**
21988          * Returns a Region object containing the drag and drop element's position
21989          * and size, including the padding configured for it
21990          * @method getLocation
21991          * @param {DragDrop} oDD the drag and drop object to get the
21992          *                       location for
21993          * @return {Roo.lib.Region} a Region object representing the total area
21994          *                             the element occupies, including any padding
21995          *                             the instance is configured for.
21996          * @static
21997          */
21998         getLocation: function(oDD) {
21999             if (! this.isTypeOfDD(oDD)) {
22000                 return null;
22001             }
22002
22003             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22004
22005             try {
22006                 pos= Roo.lib.Dom.getXY(el);
22007             } catch (e) { }
22008
22009             if (!pos) {
22010                 return null;
22011             }
22012
22013             x1 = pos[0];
22014             x2 = x1 + el.offsetWidth;
22015             y1 = pos[1];
22016             y2 = y1 + el.offsetHeight;
22017
22018             t = y1 - oDD.padding[0];
22019             r = x2 + oDD.padding[1];
22020             b = y2 + oDD.padding[2];
22021             l = x1 - oDD.padding[3];
22022
22023             return new Roo.lib.Region( t, r, b, l );
22024         },
22025
22026         /**
22027          * Checks the cursor location to see if it over the target
22028          * @method isOverTarget
22029          * @param {Roo.lib.Point} pt The point to evaluate
22030          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22031          * @return {boolean} true if the mouse is over the target
22032          * @private
22033          * @static
22034          */
22035         isOverTarget: function(pt, oTarget, intersect) {
22036             // use cache if available
22037             var loc = this.locationCache[oTarget.id];
22038             if (!loc || !this.useCache) {
22039                 loc = this.getLocation(oTarget);
22040                 this.locationCache[oTarget.id] = loc;
22041
22042             }
22043
22044             if (!loc) {
22045                 return false;
22046             }
22047
22048             oTarget.cursorIsOver = loc.contains( pt );
22049
22050             // DragDrop is using this as a sanity check for the initial mousedown
22051             // in this case we are done.  In POINT mode, if the drag obj has no
22052             // contraints, we are also done. Otherwise we need to evaluate the
22053             // location of the target as related to the actual location of the
22054             // dragged element.
22055             var dc = this.dragCurrent;
22056             if (!dc || !dc.getTargetCoord ||
22057                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22058                 return oTarget.cursorIsOver;
22059             }
22060
22061             oTarget.overlap = null;
22062
22063             // Get the current location of the drag element, this is the
22064             // location of the mouse event less the delta that represents
22065             // where the original mousedown happened on the element.  We
22066             // need to consider constraints and ticks as well.
22067             var pos = dc.getTargetCoord(pt.x, pt.y);
22068
22069             var el = dc.getDragEl();
22070             var curRegion = new Roo.lib.Region( pos.y,
22071                                                    pos.x + el.offsetWidth,
22072                                                    pos.y + el.offsetHeight,
22073                                                    pos.x );
22074
22075             var overlap = curRegion.intersect(loc);
22076
22077             if (overlap) {
22078                 oTarget.overlap = overlap;
22079                 return (intersect) ? true : oTarget.cursorIsOver;
22080             } else {
22081                 return false;
22082             }
22083         },
22084
22085         /**
22086          * unload event handler
22087          * @method _onUnload
22088          * @private
22089          * @static
22090          */
22091         _onUnload: function(e, me) {
22092             Roo.dd.DragDropMgr.unregAll();
22093         },
22094
22095         /**
22096          * Cleans up the drag and drop events and objects.
22097          * @method unregAll
22098          * @private
22099          * @static
22100          */
22101         unregAll: function() {
22102
22103             if (this.dragCurrent) {
22104                 this.stopDrag();
22105                 this.dragCurrent = null;
22106             }
22107
22108             this._execOnAll("unreg", []);
22109
22110             for (i in this.elementCache) {
22111                 delete this.elementCache[i];
22112             }
22113
22114             this.elementCache = {};
22115             this.ids = {};
22116         },
22117
22118         /**
22119          * A cache of DOM elements
22120          * @property elementCache
22121          * @private
22122          * @static
22123          */
22124         elementCache: {},
22125
22126         /**
22127          * Get the wrapper for the DOM element specified
22128          * @method getElWrapper
22129          * @param {String} id the id of the element to get
22130          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22131          * @private
22132          * @deprecated This wrapper isn't that useful
22133          * @static
22134          */
22135         getElWrapper: function(id) {
22136             var oWrapper = this.elementCache[id];
22137             if (!oWrapper || !oWrapper.el) {
22138                 oWrapper = this.elementCache[id] =
22139                     new this.ElementWrapper(Roo.getDom(id));
22140             }
22141             return oWrapper;
22142         },
22143
22144         /**
22145          * Returns the actual DOM element
22146          * @method getElement
22147          * @param {String} id the id of the elment to get
22148          * @return {Object} The element
22149          * @deprecated use Roo.getDom instead
22150          * @static
22151          */
22152         getElement: function(id) {
22153             return Roo.getDom(id);
22154         },
22155
22156         /**
22157          * Returns the style property for the DOM element (i.e.,
22158          * document.getElById(id).style)
22159          * @method getCss
22160          * @param {String} id the id of the elment to get
22161          * @return {Object} The style property of the element
22162          * @deprecated use Roo.getDom instead
22163          * @static
22164          */
22165         getCss: function(id) {
22166             var el = Roo.getDom(id);
22167             return (el) ? el.style : null;
22168         },
22169
22170         /**
22171          * Inner class for cached elements
22172          * @class DragDropMgr.ElementWrapper
22173          * @for DragDropMgr
22174          * @private
22175          * @deprecated
22176          */
22177         ElementWrapper: function(el) {
22178                 /**
22179                  * The element
22180                  * @property el
22181                  */
22182                 this.el = el || null;
22183                 /**
22184                  * The element id
22185                  * @property id
22186                  */
22187                 this.id = this.el && el.id;
22188                 /**
22189                  * A reference to the style property
22190                  * @property css
22191                  */
22192                 this.css = this.el && el.style;
22193             },
22194
22195         /**
22196          * Returns the X position of an html element
22197          * @method getPosX
22198          * @param el the element for which to get the position
22199          * @return {int} the X coordinate
22200          * @for DragDropMgr
22201          * @deprecated use Roo.lib.Dom.getX instead
22202          * @static
22203          */
22204         getPosX: function(el) {
22205             return Roo.lib.Dom.getX(el);
22206         },
22207
22208         /**
22209          * Returns the Y position of an html element
22210          * @method getPosY
22211          * @param el the element for which to get the position
22212          * @return {int} the Y coordinate
22213          * @deprecated use Roo.lib.Dom.getY instead
22214          * @static
22215          */
22216         getPosY: function(el) {
22217             return Roo.lib.Dom.getY(el);
22218         },
22219
22220         /**
22221          * Swap two nodes.  In IE, we use the native method, for others we
22222          * emulate the IE behavior
22223          * @method swapNode
22224          * @param n1 the first node to swap
22225          * @param n2 the other node to swap
22226          * @static
22227          */
22228         swapNode: function(n1, n2) {
22229             if (n1.swapNode) {
22230                 n1.swapNode(n2);
22231             } else {
22232                 var p = n2.parentNode;
22233                 var s = n2.nextSibling;
22234
22235                 if (s == n1) {
22236                     p.insertBefore(n1, n2);
22237                 } else if (n2 == n1.nextSibling) {
22238                     p.insertBefore(n2, n1);
22239                 } else {
22240                     n1.parentNode.replaceChild(n2, n1);
22241                     p.insertBefore(n1, s);
22242                 }
22243             }
22244         },
22245
22246         /**
22247          * Returns the current scroll position
22248          * @method getScroll
22249          * @private
22250          * @static
22251          */
22252         getScroll: function () {
22253             var t, l, dde=document.documentElement, db=document.body;
22254             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22255                 t = dde.scrollTop;
22256                 l = dde.scrollLeft;
22257             } else if (db) {
22258                 t = db.scrollTop;
22259                 l = db.scrollLeft;
22260             } else {
22261
22262             }
22263             return { top: t, left: l };
22264         },
22265
22266         /**
22267          * Returns the specified element style property
22268          * @method getStyle
22269          * @param {HTMLElement} el          the element
22270          * @param {string}      styleProp   the style property
22271          * @return {string} The value of the style property
22272          * @deprecated use Roo.lib.Dom.getStyle
22273          * @static
22274          */
22275         getStyle: function(el, styleProp) {
22276             return Roo.fly(el).getStyle(styleProp);
22277         },
22278
22279         /**
22280          * Gets the scrollTop
22281          * @method getScrollTop
22282          * @return {int} the document's scrollTop
22283          * @static
22284          */
22285         getScrollTop: function () { return this.getScroll().top; },
22286
22287         /**
22288          * Gets the scrollLeft
22289          * @method getScrollLeft
22290          * @return {int} the document's scrollTop
22291          * @static
22292          */
22293         getScrollLeft: function () { return this.getScroll().left; },
22294
22295         /**
22296          * Sets the x/y position of an element to the location of the
22297          * target element.
22298          * @method moveToEl
22299          * @param {HTMLElement} moveEl      The element to move
22300          * @param {HTMLElement} targetEl    The position reference element
22301          * @static
22302          */
22303         moveToEl: function (moveEl, targetEl) {
22304             var aCoord = Roo.lib.Dom.getXY(targetEl);
22305             Roo.lib.Dom.setXY(moveEl, aCoord);
22306         },
22307
22308         /**
22309          * Numeric array sort function
22310          * @method numericSort
22311          * @static
22312          */
22313         numericSort: function(a, b) { return (a - b); },
22314
22315         /**
22316          * Internal counter
22317          * @property _timeoutCount
22318          * @private
22319          * @static
22320          */
22321         _timeoutCount: 0,
22322
22323         /**
22324          * Trying to make the load order less important.  Without this we get
22325          * an error if this file is loaded before the Event Utility.
22326          * @method _addListeners
22327          * @private
22328          * @static
22329          */
22330         _addListeners: function() {
22331             var DDM = Roo.dd.DDM;
22332             if ( Roo.lib.Event && document ) {
22333                 DDM._onLoad();
22334             } else {
22335                 if (DDM._timeoutCount > 2000) {
22336                 } else {
22337                     setTimeout(DDM._addListeners, 10);
22338                     if (document && document.body) {
22339                         DDM._timeoutCount += 1;
22340                     }
22341                 }
22342             }
22343         },
22344
22345         /**
22346          * Recursively searches the immediate parent and all child nodes for
22347          * the handle element in order to determine wheter or not it was
22348          * clicked.
22349          * @method handleWasClicked
22350          * @param node the html element to inspect
22351          * @static
22352          */
22353         handleWasClicked: function(node, id) {
22354             if (this.isHandle(id, node.id)) {
22355                 return true;
22356             } else {
22357                 // check to see if this is a text node child of the one we want
22358                 var p = node.parentNode;
22359
22360                 while (p) {
22361                     if (this.isHandle(id, p.id)) {
22362                         return true;
22363                     } else {
22364                         p = p.parentNode;
22365                     }
22366                 }
22367             }
22368
22369             return false;
22370         }
22371
22372     };
22373
22374 }();
22375
22376 // shorter alias, save a few bytes
22377 Roo.dd.DDM = Roo.dd.DragDropMgr;
22378 Roo.dd.DDM._addListeners();
22379
22380 }/*
22381  * Based on:
22382  * Ext JS Library 1.1.1
22383  * Copyright(c) 2006-2007, Ext JS, LLC.
22384  *
22385  * Originally Released Under LGPL - original licence link has changed is not relivant.
22386  *
22387  * Fork - LGPL
22388  * <script type="text/javascript">
22389  */
22390
22391 /**
22392  * @class Roo.dd.DD
22393  * A DragDrop implementation where the linked element follows the
22394  * mouse cursor during a drag.
22395  * @extends Roo.dd.DragDrop
22396  * @constructor
22397  * @param {String} id the id of the linked element
22398  * @param {String} sGroup the group of related DragDrop items
22399  * @param {object} config an object containing configurable attributes
22400  *                Valid properties for DD:
22401  *                    scroll
22402  */
22403 Roo.dd.DD = function(id, sGroup, config) {
22404     if (id) {
22405         this.init(id, sGroup, config);
22406     }
22407 };
22408
22409 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22410
22411     /**
22412      * When set to true, the utility automatically tries to scroll the browser
22413      * window wehn a drag and drop element is dragged near the viewport boundary.
22414      * Defaults to true.
22415      * @property scroll
22416      * @type boolean
22417      */
22418     scroll: true,
22419
22420     /**
22421      * Sets the pointer offset to the distance between the linked element's top
22422      * left corner and the location the element was clicked
22423      * @method autoOffset
22424      * @param {int} iPageX the X coordinate of the click
22425      * @param {int} iPageY the Y coordinate of the click
22426      */
22427     autoOffset: function(iPageX, iPageY) {
22428         var x = iPageX - this.startPageX;
22429         var y = iPageY - this.startPageY;
22430         this.setDelta(x, y);
22431     },
22432
22433     /**
22434      * Sets the pointer offset.  You can call this directly to force the
22435      * offset to be in a particular location (e.g., pass in 0,0 to set it
22436      * to the center of the object)
22437      * @method setDelta
22438      * @param {int} iDeltaX the distance from the left
22439      * @param {int} iDeltaY the distance from the top
22440      */
22441     setDelta: function(iDeltaX, iDeltaY) {
22442         this.deltaX = iDeltaX;
22443         this.deltaY = iDeltaY;
22444     },
22445
22446     /**
22447      * Sets the drag element to the location of the mousedown or click event,
22448      * maintaining the cursor location relative to the location on the element
22449      * that was clicked.  Override this if you want to place the element in a
22450      * location other than where the cursor is.
22451      * @method setDragElPos
22452      * @param {int} iPageX the X coordinate of the mousedown or drag event
22453      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22454      */
22455     setDragElPos: function(iPageX, iPageY) {
22456         // the first time we do this, we are going to check to make sure
22457         // the element has css positioning
22458
22459         var el = this.getDragEl();
22460         this.alignElWithMouse(el, iPageX, iPageY);
22461     },
22462
22463     /**
22464      * Sets the element to the location of the mousedown or click event,
22465      * maintaining the cursor location relative to the location on the element
22466      * that was clicked.  Override this if you want to place the element in a
22467      * location other than where the cursor is.
22468      * @method alignElWithMouse
22469      * @param {HTMLElement} el the element to move
22470      * @param {int} iPageX the X coordinate of the mousedown or drag event
22471      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22472      */
22473     alignElWithMouse: function(el, iPageX, iPageY) {
22474         var oCoord = this.getTargetCoord(iPageX, iPageY);
22475         var fly = el.dom ? el : Roo.fly(el);
22476         if (!this.deltaSetXY) {
22477             var aCoord = [oCoord.x, oCoord.y];
22478             fly.setXY(aCoord);
22479             var newLeft = fly.getLeft(true);
22480             var newTop  = fly.getTop(true);
22481             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22482         } else {
22483             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22484         }
22485
22486         this.cachePosition(oCoord.x, oCoord.y);
22487         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22488         return oCoord;
22489     },
22490
22491     /**
22492      * Saves the most recent position so that we can reset the constraints and
22493      * tick marks on-demand.  We need to know this so that we can calculate the
22494      * number of pixels the element is offset from its original position.
22495      * @method cachePosition
22496      * @param iPageX the current x position (optional, this just makes it so we
22497      * don't have to look it up again)
22498      * @param iPageY the current y position (optional, this just makes it so we
22499      * don't have to look it up again)
22500      */
22501     cachePosition: function(iPageX, iPageY) {
22502         if (iPageX) {
22503             this.lastPageX = iPageX;
22504             this.lastPageY = iPageY;
22505         } else {
22506             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22507             this.lastPageX = aCoord[0];
22508             this.lastPageY = aCoord[1];
22509         }
22510     },
22511
22512     /**
22513      * Auto-scroll the window if the dragged object has been moved beyond the
22514      * visible window boundary.
22515      * @method autoScroll
22516      * @param {int} x the drag element's x position
22517      * @param {int} y the drag element's y position
22518      * @param {int} h the height of the drag element
22519      * @param {int} w the width of the drag element
22520      * @private
22521      */
22522     autoScroll: function(x, y, h, w) {
22523
22524         if (this.scroll) {
22525             // The client height
22526             var clientH = Roo.lib.Dom.getViewWidth();
22527
22528             // The client width
22529             var clientW = Roo.lib.Dom.getViewHeight();
22530
22531             // The amt scrolled down
22532             var st = this.DDM.getScrollTop();
22533
22534             // The amt scrolled right
22535             var sl = this.DDM.getScrollLeft();
22536
22537             // Location of the bottom of the element
22538             var bot = h + y;
22539
22540             // Location of the right of the element
22541             var right = w + x;
22542
22543             // The distance from the cursor to the bottom of the visible area,
22544             // adjusted so that we don't scroll if the cursor is beyond the
22545             // element drag constraints
22546             var toBot = (clientH + st - y - this.deltaY);
22547
22548             // The distance from the cursor to the right of the visible area
22549             var toRight = (clientW + sl - x - this.deltaX);
22550
22551
22552             // How close to the edge the cursor must be before we scroll
22553             // var thresh = (document.all) ? 100 : 40;
22554             var thresh = 40;
22555
22556             // How many pixels to scroll per autoscroll op.  This helps to reduce
22557             // clunky scrolling. IE is more sensitive about this ... it needs this
22558             // value to be higher.
22559             var scrAmt = (document.all) ? 80 : 30;
22560
22561             // Scroll down if we are near the bottom of the visible page and the
22562             // obj extends below the crease
22563             if ( bot > clientH && toBot < thresh ) {
22564                 window.scrollTo(sl, st + scrAmt);
22565             }
22566
22567             // Scroll up if the window is scrolled down and the top of the object
22568             // goes above the top border
22569             if ( y < st && st > 0 && y - st < thresh ) {
22570                 window.scrollTo(sl, st - scrAmt);
22571             }
22572
22573             // Scroll right if the obj is beyond the right border and the cursor is
22574             // near the border.
22575             if ( right > clientW && toRight < thresh ) {
22576                 window.scrollTo(sl + scrAmt, st);
22577             }
22578
22579             // Scroll left if the window has been scrolled to the right and the obj
22580             // extends past the left border
22581             if ( x < sl && sl > 0 && x - sl < thresh ) {
22582                 window.scrollTo(sl - scrAmt, st);
22583             }
22584         }
22585     },
22586
22587     /**
22588      * Finds the location the element should be placed if we want to move
22589      * it to where the mouse location less the click offset would place us.
22590      * @method getTargetCoord
22591      * @param {int} iPageX the X coordinate of the click
22592      * @param {int} iPageY the Y coordinate of the click
22593      * @return an object that contains the coordinates (Object.x and Object.y)
22594      * @private
22595      */
22596     getTargetCoord: function(iPageX, iPageY) {
22597
22598
22599         var x = iPageX - this.deltaX;
22600         var y = iPageY - this.deltaY;
22601
22602         if (this.constrainX) {
22603             if (x < this.minX) { x = this.minX; }
22604             if (x > this.maxX) { x = this.maxX; }
22605         }
22606
22607         if (this.constrainY) {
22608             if (y < this.minY) { y = this.minY; }
22609             if (y > this.maxY) { y = this.maxY; }
22610         }
22611
22612         x = this.getTick(x, this.xTicks);
22613         y = this.getTick(y, this.yTicks);
22614
22615
22616         return {x:x, y:y};
22617     },
22618
22619     /*
22620      * Sets up config options specific to this class. Overrides
22621      * Roo.dd.DragDrop, but all versions of this method through the
22622      * inheritance chain are called
22623      */
22624     applyConfig: function() {
22625         Roo.dd.DD.superclass.applyConfig.call(this);
22626         this.scroll = (this.config.scroll !== false);
22627     },
22628
22629     /*
22630      * Event that fires prior to the onMouseDown event.  Overrides
22631      * Roo.dd.DragDrop.
22632      */
22633     b4MouseDown: function(e) {
22634         // this.resetConstraints();
22635         this.autoOffset(e.getPageX(),
22636                             e.getPageY());
22637     },
22638
22639     /*
22640      * Event that fires prior to the onDrag event.  Overrides
22641      * Roo.dd.DragDrop.
22642      */
22643     b4Drag: function(e) {
22644         this.setDragElPos(e.getPageX(),
22645                             e.getPageY());
22646     },
22647
22648     toString: function() {
22649         return ("DD " + this.id);
22650     }
22651
22652     //////////////////////////////////////////////////////////////////////////
22653     // Debugging ygDragDrop events that can be overridden
22654     //////////////////////////////////////////////////////////////////////////
22655     /*
22656     startDrag: function(x, y) {
22657     },
22658
22659     onDrag: function(e) {
22660     },
22661
22662     onDragEnter: function(e, id) {
22663     },
22664
22665     onDragOver: function(e, id) {
22666     },
22667
22668     onDragOut: function(e, id) {
22669     },
22670
22671     onDragDrop: function(e, id) {
22672     },
22673
22674     endDrag: function(e) {
22675     }
22676
22677     */
22678
22679 });/*
22680  * Based on:
22681  * Ext JS Library 1.1.1
22682  * Copyright(c) 2006-2007, Ext JS, LLC.
22683  *
22684  * Originally Released Under LGPL - original licence link has changed is not relivant.
22685  *
22686  * Fork - LGPL
22687  * <script type="text/javascript">
22688  */
22689
22690 /**
22691  * @class Roo.dd.DDProxy
22692  * A DragDrop implementation that inserts an empty, bordered div into
22693  * the document that follows the cursor during drag operations.  At the time of
22694  * the click, the frame div is resized to the dimensions of the linked html
22695  * element, and moved to the exact location of the linked element.
22696  *
22697  * References to the "frame" element refer to the single proxy element that
22698  * was created to be dragged in place of all DDProxy elements on the
22699  * page.
22700  *
22701  * @extends Roo.dd.DD
22702  * @constructor
22703  * @param {String} id the id of the linked html element
22704  * @param {String} sGroup the group of related DragDrop objects
22705  * @param {object} config an object containing configurable attributes
22706  *                Valid properties for DDProxy in addition to those in DragDrop:
22707  *                   resizeFrame, centerFrame, dragElId
22708  */
22709 Roo.dd.DDProxy = function(id, sGroup, config) {
22710     if (id) {
22711         this.init(id, sGroup, config);
22712         this.initFrame();
22713     }
22714 };
22715
22716 /**
22717  * The default drag frame div id
22718  * @property Roo.dd.DDProxy.dragElId
22719  * @type String
22720  * @static
22721  */
22722 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22723
22724 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22725
22726     /**
22727      * By default we resize the drag frame to be the same size as the element
22728      * we want to drag (this is to get the frame effect).  We can turn it off
22729      * if we want a different behavior.
22730      * @property resizeFrame
22731      * @type boolean
22732      */
22733     resizeFrame: true,
22734
22735     /**
22736      * By default the frame is positioned exactly where the drag element is, so
22737      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22738      * you do not have constraints on the obj is to have the drag frame centered
22739      * around the cursor.  Set centerFrame to true for this effect.
22740      * @property centerFrame
22741      * @type boolean
22742      */
22743     centerFrame: false,
22744
22745     /**
22746      * Creates the proxy element if it does not yet exist
22747      * @method createFrame
22748      */
22749     createFrame: function() {
22750         var self = this;
22751         var body = document.body;
22752
22753         if (!body || !body.firstChild) {
22754             setTimeout( function() { self.createFrame(); }, 50 );
22755             return;
22756         }
22757
22758         var div = this.getDragEl();
22759
22760         if (!div) {
22761             div    = document.createElement("div");
22762             div.id = this.dragElId;
22763             var s  = div.style;
22764
22765             s.position   = "absolute";
22766             s.visibility = "hidden";
22767             s.cursor     = "move";
22768             s.border     = "2px solid #aaa";
22769             s.zIndex     = 999;
22770
22771             // appendChild can blow up IE if invoked prior to the window load event
22772             // while rendering a table.  It is possible there are other scenarios
22773             // that would cause this to happen as well.
22774             body.insertBefore(div, body.firstChild);
22775         }
22776     },
22777
22778     /**
22779      * Initialization for the drag frame element.  Must be called in the
22780      * constructor of all subclasses
22781      * @method initFrame
22782      */
22783     initFrame: function() {
22784         this.createFrame();
22785     },
22786
22787     applyConfig: function() {
22788         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22789
22790         this.resizeFrame = (this.config.resizeFrame !== false);
22791         this.centerFrame = (this.config.centerFrame);
22792         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22793     },
22794
22795     /**
22796      * Resizes the drag frame to the dimensions of the clicked object, positions
22797      * it over the object, and finally displays it
22798      * @method showFrame
22799      * @param {int} iPageX X click position
22800      * @param {int} iPageY Y click position
22801      * @private
22802      */
22803     showFrame: function(iPageX, iPageY) {
22804         var el = this.getEl();
22805         var dragEl = this.getDragEl();
22806         var s = dragEl.style;
22807
22808         this._resizeProxy();
22809
22810         if (this.centerFrame) {
22811             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22812                            Math.round(parseInt(s.height, 10)/2) );
22813         }
22814
22815         this.setDragElPos(iPageX, iPageY);
22816
22817         Roo.fly(dragEl).show();
22818     },
22819
22820     /**
22821      * The proxy is automatically resized to the dimensions of the linked
22822      * element when a drag is initiated, unless resizeFrame is set to false
22823      * @method _resizeProxy
22824      * @private
22825      */
22826     _resizeProxy: function() {
22827         if (this.resizeFrame) {
22828             var el = this.getEl();
22829             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22830         }
22831     },
22832
22833     // overrides Roo.dd.DragDrop
22834     b4MouseDown: function(e) {
22835         var x = e.getPageX();
22836         var y = e.getPageY();
22837         this.autoOffset(x, y);
22838         this.setDragElPos(x, y);
22839     },
22840
22841     // overrides Roo.dd.DragDrop
22842     b4StartDrag: function(x, y) {
22843         // show the drag frame
22844         this.showFrame(x, y);
22845     },
22846
22847     // overrides Roo.dd.DragDrop
22848     b4EndDrag: function(e) {
22849         Roo.fly(this.getDragEl()).hide();
22850     },
22851
22852     // overrides Roo.dd.DragDrop
22853     // By default we try to move the element to the last location of the frame.
22854     // This is so that the default behavior mirrors that of Roo.dd.DD.
22855     endDrag: function(e) {
22856
22857         var lel = this.getEl();
22858         var del = this.getDragEl();
22859
22860         // Show the drag frame briefly so we can get its position
22861         del.style.visibility = "";
22862
22863         this.beforeMove();
22864         // Hide the linked element before the move to get around a Safari
22865         // rendering bug.
22866         lel.style.visibility = "hidden";
22867         Roo.dd.DDM.moveToEl(lel, del);
22868         del.style.visibility = "hidden";
22869         lel.style.visibility = "";
22870
22871         this.afterDrag();
22872     },
22873
22874     beforeMove : function(){
22875
22876     },
22877
22878     afterDrag : function(){
22879
22880     },
22881
22882     toString: function() {
22883         return ("DDProxy " + this.id);
22884     }
22885
22886 });
22887 /*
22888  * Based on:
22889  * Ext JS Library 1.1.1
22890  * Copyright(c) 2006-2007, Ext JS, LLC.
22891  *
22892  * Originally Released Under LGPL - original licence link has changed is not relivant.
22893  *
22894  * Fork - LGPL
22895  * <script type="text/javascript">
22896  */
22897
22898  /**
22899  * @class Roo.dd.DDTarget
22900  * A DragDrop implementation that does not move, but can be a drop
22901  * target.  You would get the same result by simply omitting implementation
22902  * for the event callbacks, but this way we reduce the processing cost of the
22903  * event listener and the callbacks.
22904  * @extends Roo.dd.DragDrop
22905  * @constructor
22906  * @param {String} id the id of the element that is a drop target
22907  * @param {String} sGroup the group of related DragDrop objects
22908  * @param {object} config an object containing configurable attributes
22909  *                 Valid properties for DDTarget in addition to those in
22910  *                 DragDrop:
22911  *                    none
22912  */
22913 Roo.dd.DDTarget = function(id, sGroup, config) {
22914     if (id) {
22915         this.initTarget(id, sGroup, config);
22916     }
22917     if (config && (config.listeners || config.events)) { 
22918         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22919             listeners : config.listeners || {}, 
22920             events : config.events || {} 
22921         });    
22922     }
22923 };
22924
22925 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22926 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22927     toString: function() {
22928         return ("DDTarget " + this.id);
22929     }
22930 });
22931 /*
22932  * Based on:
22933  * Ext JS Library 1.1.1
22934  * Copyright(c) 2006-2007, Ext JS, LLC.
22935  *
22936  * Originally Released Under LGPL - original licence link has changed is not relivant.
22937  *
22938  * Fork - LGPL
22939  * <script type="text/javascript">
22940  */
22941  
22942
22943 /**
22944  * @class Roo.dd.ScrollManager
22945  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22946  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22947  * @static
22948  */
22949 Roo.dd.ScrollManager = function(){
22950     var ddm = Roo.dd.DragDropMgr;
22951     var els = {};
22952     var dragEl = null;
22953     var proc = {};
22954     
22955     
22956     
22957     var onStop = function(e){
22958         dragEl = null;
22959         clearProc();
22960     };
22961     
22962     var triggerRefresh = function(){
22963         if(ddm.dragCurrent){
22964              ddm.refreshCache(ddm.dragCurrent.groups);
22965         }
22966     };
22967     
22968     var doScroll = function(){
22969         if(ddm.dragCurrent){
22970             var dds = Roo.dd.ScrollManager;
22971             if(!dds.animate){
22972                 if(proc.el.scroll(proc.dir, dds.increment)){
22973                     triggerRefresh();
22974                 }
22975             }else{
22976                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22977             }
22978         }
22979     };
22980     
22981     var clearProc = function(){
22982         if(proc.id){
22983             clearInterval(proc.id);
22984         }
22985         proc.id = 0;
22986         proc.el = null;
22987         proc.dir = "";
22988     };
22989     
22990     var startProc = function(el, dir){
22991          Roo.log('scroll startproc');
22992         clearProc();
22993         proc.el = el;
22994         proc.dir = dir;
22995         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22996     };
22997     
22998     var onFire = function(e, isDrop){
22999        
23000         if(isDrop || !ddm.dragCurrent){ return; }
23001         var dds = Roo.dd.ScrollManager;
23002         if(!dragEl || dragEl != ddm.dragCurrent){
23003             dragEl = ddm.dragCurrent;
23004             // refresh regions on drag start
23005             dds.refreshCache();
23006         }
23007         
23008         var xy = Roo.lib.Event.getXY(e);
23009         var pt = new Roo.lib.Point(xy[0], xy[1]);
23010         for(var id in els){
23011             var el = els[id], r = el._region;
23012             if(r && r.contains(pt) && el.isScrollable()){
23013                 if(r.bottom - pt.y <= dds.thresh){
23014                     if(proc.el != el){
23015                         startProc(el, "down");
23016                     }
23017                     return;
23018                 }else if(r.right - pt.x <= dds.thresh){
23019                     if(proc.el != el){
23020                         startProc(el, "left");
23021                     }
23022                     return;
23023                 }else if(pt.y - r.top <= dds.thresh){
23024                     if(proc.el != el){
23025                         startProc(el, "up");
23026                     }
23027                     return;
23028                 }else if(pt.x - r.left <= dds.thresh){
23029                     if(proc.el != el){
23030                         startProc(el, "right");
23031                     }
23032                     return;
23033                 }
23034             }
23035         }
23036         clearProc();
23037     };
23038     
23039     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23040     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23041     
23042     return {
23043         /**
23044          * Registers new overflow element(s) to auto scroll
23045          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23046          */
23047         register : function(el){
23048             if(el instanceof Array){
23049                 for(var i = 0, len = el.length; i < len; i++) {
23050                         this.register(el[i]);
23051                 }
23052             }else{
23053                 el = Roo.get(el);
23054                 els[el.id] = el;
23055             }
23056             Roo.dd.ScrollManager.els = els;
23057         },
23058         
23059         /**
23060          * Unregisters overflow element(s) so they are no longer scrolled
23061          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23062          */
23063         unregister : function(el){
23064             if(el instanceof Array){
23065                 for(var i = 0, len = el.length; i < len; i++) {
23066                         this.unregister(el[i]);
23067                 }
23068             }else{
23069                 el = Roo.get(el);
23070                 delete els[el.id];
23071             }
23072         },
23073         
23074         /**
23075          * The number of pixels from the edge of a container the pointer needs to be to 
23076          * trigger scrolling (defaults to 25)
23077          * @type Number
23078          */
23079         thresh : 25,
23080         
23081         /**
23082          * The number of pixels to scroll in each scroll increment (defaults to 50)
23083          * @type Number
23084          */
23085         increment : 100,
23086         
23087         /**
23088          * The frequency of scrolls in milliseconds (defaults to 500)
23089          * @type Number
23090          */
23091         frequency : 500,
23092         
23093         /**
23094          * True to animate the scroll (defaults to true)
23095          * @type Boolean
23096          */
23097         animate: true,
23098         
23099         /**
23100          * The animation duration in seconds - 
23101          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23102          * @type Number
23103          */
23104         animDuration: .4,
23105         
23106         /**
23107          * Manually trigger a cache refresh.
23108          */
23109         refreshCache : function(){
23110             for(var id in els){
23111                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23112                     els[id]._region = els[id].getRegion();
23113                 }
23114             }
23115         }
23116     };
23117 }();/*
23118  * Based on:
23119  * Ext JS Library 1.1.1
23120  * Copyright(c) 2006-2007, Ext JS, LLC.
23121  *
23122  * Originally Released Under LGPL - original licence link has changed is not relivant.
23123  *
23124  * Fork - LGPL
23125  * <script type="text/javascript">
23126  */
23127  
23128
23129 /**
23130  * @class Roo.dd.Registry
23131  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23132  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23133  * @static
23134  */
23135 Roo.dd.Registry = function(){
23136     var elements = {}; 
23137     var handles = {}; 
23138     var autoIdSeed = 0;
23139
23140     var getId = function(el, autogen){
23141         if(typeof el == "string"){
23142             return el;
23143         }
23144         var id = el.id;
23145         if(!id && autogen !== false){
23146             id = "roodd-" + (++autoIdSeed);
23147             el.id = id;
23148         }
23149         return id;
23150     };
23151     
23152     return {
23153     /**
23154      * Register a drag drop element
23155      * @param {String|HTMLElement} element The id or DOM node to register
23156      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23157      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23158      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23159      * populated in the data object (if applicable):
23160      * <pre>
23161 Value      Description<br />
23162 ---------  ------------------------------------------<br />
23163 handles    Array of DOM nodes that trigger dragging<br />
23164            for the element being registered<br />
23165 isHandle   True if the element passed in triggers<br />
23166            dragging itself, else false
23167 </pre>
23168      */
23169         register : function(el, data){
23170             data = data || {};
23171             if(typeof el == "string"){
23172                 el = document.getElementById(el);
23173             }
23174             data.ddel = el;
23175             elements[getId(el)] = data;
23176             if(data.isHandle !== false){
23177                 handles[data.ddel.id] = data;
23178             }
23179             if(data.handles){
23180                 var hs = data.handles;
23181                 for(var i = 0, len = hs.length; i < len; i++){
23182                         handles[getId(hs[i])] = data;
23183                 }
23184             }
23185         },
23186
23187     /**
23188      * Unregister a drag drop element
23189      * @param {String|HTMLElement}  element The id or DOM node to unregister
23190      */
23191         unregister : function(el){
23192             var id = getId(el, false);
23193             var data = elements[id];
23194             if(data){
23195                 delete elements[id];
23196                 if(data.handles){
23197                     var hs = data.handles;
23198                     for(var i = 0, len = hs.length; i < len; i++){
23199                         delete handles[getId(hs[i], false)];
23200                     }
23201                 }
23202             }
23203         },
23204
23205     /**
23206      * Returns the handle registered for a DOM Node by id
23207      * @param {String|HTMLElement} id The DOM node or id to look up
23208      * @return {Object} handle The custom handle data
23209      */
23210         getHandle : function(id){
23211             if(typeof id != "string"){ // must be element?
23212                 id = id.id;
23213             }
23214             return handles[id];
23215         },
23216
23217     /**
23218      * Returns the handle that is registered for the DOM node that is the target of the event
23219      * @param {Event} e The event
23220      * @return {Object} handle The custom handle data
23221      */
23222         getHandleFromEvent : function(e){
23223             var t = Roo.lib.Event.getTarget(e);
23224             return t ? handles[t.id] : null;
23225         },
23226
23227     /**
23228      * Returns a custom data object that is registered for a DOM node by id
23229      * @param {String|HTMLElement} id The DOM node or id to look up
23230      * @return {Object} data The custom data
23231      */
23232         getTarget : function(id){
23233             if(typeof id != "string"){ // must be element?
23234                 id = id.id;
23235             }
23236             return elements[id];
23237         },
23238
23239     /**
23240      * Returns a custom data object that is registered for the DOM node that is the target of the event
23241      * @param {Event} e The event
23242      * @return {Object} data The custom data
23243      */
23244         getTargetFromEvent : function(e){
23245             var t = Roo.lib.Event.getTarget(e);
23246             return t ? elements[t.id] || handles[t.id] : null;
23247         }
23248     };
23249 }();/*
23250  * Based on:
23251  * Ext JS Library 1.1.1
23252  * Copyright(c) 2006-2007, Ext JS, LLC.
23253  *
23254  * Originally Released Under LGPL - original licence link has changed is not relivant.
23255  *
23256  * Fork - LGPL
23257  * <script type="text/javascript">
23258  */
23259  
23260
23261 /**
23262  * @class Roo.dd.StatusProxy
23263  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23264  * default drag proxy used by all Roo.dd components.
23265  * @constructor
23266  * @param {Object} config
23267  */
23268 Roo.dd.StatusProxy = function(config){
23269     Roo.apply(this, config);
23270     this.id = this.id || Roo.id();
23271     this.el = new Roo.Layer({
23272         dh: {
23273             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23274                 {tag: "div", cls: "x-dd-drop-icon"},
23275                 {tag: "div", cls: "x-dd-drag-ghost"}
23276             ]
23277         }, 
23278         shadow: !config || config.shadow !== false
23279     });
23280     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23281     this.dropStatus = this.dropNotAllowed;
23282 };
23283
23284 Roo.dd.StatusProxy.prototype = {
23285     /**
23286      * @cfg {String} dropAllowed
23287      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23288      */
23289     dropAllowed : "x-dd-drop-ok",
23290     /**
23291      * @cfg {String} dropNotAllowed
23292      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23293      */
23294     dropNotAllowed : "x-dd-drop-nodrop",
23295
23296     /**
23297      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23298      * over the current target element.
23299      * @param {String} cssClass The css class for the new drop status indicator image
23300      */
23301     setStatus : function(cssClass){
23302         cssClass = cssClass || this.dropNotAllowed;
23303         if(this.dropStatus != cssClass){
23304             this.el.replaceClass(this.dropStatus, cssClass);
23305             this.dropStatus = cssClass;
23306         }
23307     },
23308
23309     /**
23310      * Resets the status indicator to the default dropNotAllowed value
23311      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23312      */
23313     reset : function(clearGhost){
23314         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23315         this.dropStatus = this.dropNotAllowed;
23316         if(clearGhost){
23317             this.ghost.update("");
23318         }
23319     },
23320
23321     /**
23322      * Updates the contents of the ghost element
23323      * @param {String} html The html that will replace the current innerHTML of the ghost element
23324      */
23325     update : function(html){
23326         if(typeof html == "string"){
23327             this.ghost.update(html);
23328         }else{
23329             this.ghost.update("");
23330             html.style.margin = "0";
23331             this.ghost.dom.appendChild(html);
23332         }
23333         // ensure float = none set?? cant remember why though.
23334         var el = this.ghost.dom.firstChild;
23335                 if(el){
23336                         Roo.fly(el).setStyle('float', 'none');
23337                 }
23338     },
23339     
23340     /**
23341      * Returns the underlying proxy {@link Roo.Layer}
23342      * @return {Roo.Layer} el
23343     */
23344     getEl : function(){
23345         return this.el;
23346     },
23347
23348     /**
23349      * Returns the ghost element
23350      * @return {Roo.Element} el
23351      */
23352     getGhost : function(){
23353         return this.ghost;
23354     },
23355
23356     /**
23357      * Hides the proxy
23358      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23359      */
23360     hide : function(clear){
23361         this.el.hide();
23362         if(clear){
23363             this.reset(true);
23364         }
23365     },
23366
23367     /**
23368      * Stops the repair animation if it's currently running
23369      */
23370     stop : function(){
23371         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23372             this.anim.stop();
23373         }
23374     },
23375
23376     /**
23377      * Displays this proxy
23378      */
23379     show : function(){
23380         this.el.show();
23381     },
23382
23383     /**
23384      * Force the Layer to sync its shadow and shim positions to the element
23385      */
23386     sync : function(){
23387         this.el.sync();
23388     },
23389
23390     /**
23391      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23392      * invalid drop operation by the item being dragged.
23393      * @param {Array} xy The XY position of the element ([x, y])
23394      * @param {Function} callback The function to call after the repair is complete
23395      * @param {Object} scope The scope in which to execute the callback
23396      */
23397     repair : function(xy, callback, scope){
23398         this.callback = callback;
23399         this.scope = scope;
23400         if(xy && this.animRepair !== false){
23401             this.el.addClass("x-dd-drag-repair");
23402             this.el.hideUnders(true);
23403             this.anim = this.el.shift({
23404                 duration: this.repairDuration || .5,
23405                 easing: 'easeOut',
23406                 xy: xy,
23407                 stopFx: true,
23408                 callback: this.afterRepair,
23409                 scope: this
23410             });
23411         }else{
23412             this.afterRepair();
23413         }
23414     },
23415
23416     // private
23417     afterRepair : function(){
23418         this.hide(true);
23419         if(typeof this.callback == "function"){
23420             this.callback.call(this.scope || this);
23421         }
23422         this.callback = null;
23423         this.scope = null;
23424     }
23425 };/*
23426  * Based on:
23427  * Ext JS Library 1.1.1
23428  * Copyright(c) 2006-2007, Ext JS, LLC.
23429  *
23430  * Originally Released Under LGPL - original licence link has changed is not relivant.
23431  *
23432  * Fork - LGPL
23433  * <script type="text/javascript">
23434  */
23435
23436 /**
23437  * @class Roo.dd.DragSource
23438  * @extends Roo.dd.DDProxy
23439  * A simple class that provides the basic implementation needed to make any element draggable.
23440  * @constructor
23441  * @param {String/HTMLElement/Element} el The container element
23442  * @param {Object} config
23443  */
23444 Roo.dd.DragSource = function(el, config){
23445     this.el = Roo.get(el);
23446     this.dragData = {};
23447     
23448     Roo.apply(this, config);
23449     
23450     if(!this.proxy){
23451         this.proxy = new Roo.dd.StatusProxy();
23452     }
23453
23454     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23455           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23456     
23457     this.dragging = false;
23458 };
23459
23460 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23461     /**
23462      * @cfg {String} dropAllowed
23463      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23464      */
23465     dropAllowed : "x-dd-drop-ok",
23466     /**
23467      * @cfg {String} dropNotAllowed
23468      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23469      */
23470     dropNotAllowed : "x-dd-drop-nodrop",
23471
23472     /**
23473      * Returns the data object associated with this drag source
23474      * @return {Object} data An object containing arbitrary data
23475      */
23476     getDragData : function(e){
23477         return this.dragData;
23478     },
23479
23480     // private
23481     onDragEnter : function(e, id){
23482         var target = Roo.dd.DragDropMgr.getDDById(id);
23483         this.cachedTarget = target;
23484         if(this.beforeDragEnter(target, e, id) !== false){
23485             if(target.isNotifyTarget){
23486                 var status = target.notifyEnter(this, e, this.dragData);
23487                 this.proxy.setStatus(status);
23488             }else{
23489                 this.proxy.setStatus(this.dropAllowed);
23490             }
23491             
23492             if(this.afterDragEnter){
23493                 /**
23494                  * An empty function by default, but provided so that you can perform a custom action
23495                  * when the dragged item enters the drop target by providing an implementation.
23496                  * @param {Roo.dd.DragDrop} target The drop target
23497                  * @param {Event} e The event object
23498                  * @param {String} id The id of the dragged element
23499                  * @method afterDragEnter
23500                  */
23501                 this.afterDragEnter(target, e, id);
23502             }
23503         }
23504     },
23505
23506     /**
23507      * An empty function by default, but provided so that you can perform a custom action
23508      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23509      * @param {Roo.dd.DragDrop} target The drop target
23510      * @param {Event} e The event object
23511      * @param {String} id The id of the dragged element
23512      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23513      */
23514     beforeDragEnter : function(target, e, id){
23515         return true;
23516     },
23517
23518     // private
23519     alignElWithMouse: function() {
23520         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23521         this.proxy.sync();
23522     },
23523
23524     // private
23525     onDragOver : function(e, id){
23526         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23527         if(this.beforeDragOver(target, e, id) !== false){
23528             if(target.isNotifyTarget){
23529                 var status = target.notifyOver(this, e, this.dragData);
23530                 this.proxy.setStatus(status);
23531             }
23532
23533             if(this.afterDragOver){
23534                 /**
23535                  * An empty function by default, but provided so that you can perform a custom action
23536                  * while the dragged item is over the drop target by providing an implementation.
23537                  * @param {Roo.dd.DragDrop} target The drop target
23538                  * @param {Event} e The event object
23539                  * @param {String} id The id of the dragged element
23540                  * @method afterDragOver
23541                  */
23542                 this.afterDragOver(target, e, id);
23543             }
23544         }
23545     },
23546
23547     /**
23548      * An empty function by default, but provided so that you can perform a custom action
23549      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23550      * @param {Roo.dd.DragDrop} target The drop target
23551      * @param {Event} e The event object
23552      * @param {String} id The id of the dragged element
23553      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23554      */
23555     beforeDragOver : function(target, e, id){
23556         return true;
23557     },
23558
23559     // private
23560     onDragOut : function(e, id){
23561         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23562         if(this.beforeDragOut(target, e, id) !== false){
23563             if(target.isNotifyTarget){
23564                 target.notifyOut(this, e, this.dragData);
23565             }
23566             this.proxy.reset();
23567             if(this.afterDragOut){
23568                 /**
23569                  * An empty function by default, but provided so that you can perform a custom action
23570                  * after the dragged item is dragged out of the target without dropping.
23571                  * @param {Roo.dd.DragDrop} target The drop target
23572                  * @param {Event} e The event object
23573                  * @param {String} id The id of the dragged element
23574                  * @method afterDragOut
23575                  */
23576                 this.afterDragOut(target, e, id);
23577             }
23578         }
23579         this.cachedTarget = null;
23580     },
23581
23582     /**
23583      * An empty function by default, but provided so that you can perform a custom action before the dragged
23584      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23585      * @param {Roo.dd.DragDrop} target The drop target
23586      * @param {Event} e The event object
23587      * @param {String} id The id of the dragged element
23588      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23589      */
23590     beforeDragOut : function(target, e, id){
23591         return true;
23592     },
23593     
23594     // private
23595     onDragDrop : function(e, id){
23596         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23597         if(this.beforeDragDrop(target, e, id) !== false){
23598             if(target.isNotifyTarget){
23599                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23600                     this.onValidDrop(target, e, id);
23601                 }else{
23602                     this.onInvalidDrop(target, e, id);
23603                 }
23604             }else{
23605                 this.onValidDrop(target, e, id);
23606             }
23607             
23608             if(this.afterDragDrop){
23609                 /**
23610                  * An empty function by default, but provided so that you can perform a custom action
23611                  * after a valid drag drop has occurred by providing an implementation.
23612                  * @param {Roo.dd.DragDrop} target The drop target
23613                  * @param {Event} e The event object
23614                  * @param {String} id The id of the dropped element
23615                  * @method afterDragDrop
23616                  */
23617                 this.afterDragDrop(target, e, id);
23618             }
23619         }
23620         delete this.cachedTarget;
23621     },
23622
23623     /**
23624      * An empty function by default, but provided so that you can perform a custom action before the dragged
23625      * item is dropped onto the target and optionally cancel the onDragDrop.
23626      * @param {Roo.dd.DragDrop} target The drop target
23627      * @param {Event} e The event object
23628      * @param {String} id The id of the dragged element
23629      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23630      */
23631     beforeDragDrop : function(target, e, id){
23632         return true;
23633     },
23634
23635     // private
23636     onValidDrop : function(target, e, id){
23637         this.hideProxy();
23638         if(this.afterValidDrop){
23639             /**
23640              * An empty function by default, but provided so that you can perform a custom action
23641              * after a valid drop has occurred by providing an implementation.
23642              * @param {Object} target The target DD 
23643              * @param {Event} e The event object
23644              * @param {String} id The id of the dropped element
23645              * @method afterInvalidDrop
23646              */
23647             this.afterValidDrop(target, e, id);
23648         }
23649     },
23650
23651     // private
23652     getRepairXY : function(e, data){
23653         return this.el.getXY();  
23654     },
23655
23656     // private
23657     onInvalidDrop : function(target, e, id){
23658         this.beforeInvalidDrop(target, e, id);
23659         if(this.cachedTarget){
23660             if(this.cachedTarget.isNotifyTarget){
23661                 this.cachedTarget.notifyOut(this, e, this.dragData);
23662             }
23663             this.cacheTarget = null;
23664         }
23665         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23666
23667         if(this.afterInvalidDrop){
23668             /**
23669              * An empty function by default, but provided so that you can perform a custom action
23670              * after an invalid drop has occurred by providing an implementation.
23671              * @param {Event} e The event object
23672              * @param {String} id The id of the dropped element
23673              * @method afterInvalidDrop
23674              */
23675             this.afterInvalidDrop(e, id);
23676         }
23677     },
23678
23679     // private
23680     afterRepair : function(){
23681         if(Roo.enableFx){
23682             this.el.highlight(this.hlColor || "c3daf9");
23683         }
23684         this.dragging = false;
23685     },
23686
23687     /**
23688      * An empty function by default, but provided so that you can perform a custom action after an invalid
23689      * drop has occurred.
23690      * @param {Roo.dd.DragDrop} target The drop target
23691      * @param {Event} e The event object
23692      * @param {String} id The id of the dragged element
23693      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23694      */
23695     beforeInvalidDrop : function(target, e, id){
23696         return true;
23697     },
23698
23699     // private
23700     handleMouseDown : function(e){
23701         if(this.dragging) {
23702             return;
23703         }
23704         var data = this.getDragData(e);
23705         if(data && this.onBeforeDrag(data, e) !== false){
23706             this.dragData = data;
23707             this.proxy.stop();
23708             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23709         } 
23710     },
23711
23712     /**
23713      * An empty function by default, but provided so that you can perform a custom action before the initial
23714      * drag event begins and optionally cancel it.
23715      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23716      * @param {Event} e The event object
23717      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23718      */
23719     onBeforeDrag : function(data, e){
23720         return true;
23721     },
23722
23723     /**
23724      * An empty function by default, but provided so that you can perform a custom action once the initial
23725      * drag event has begun.  The drag cannot be canceled from this function.
23726      * @param {Number} x The x position of the click on the dragged object
23727      * @param {Number} y The y position of the click on the dragged object
23728      */
23729     onStartDrag : Roo.emptyFn,
23730
23731     // private - YUI override
23732     startDrag : function(x, y){
23733         this.proxy.reset();
23734         this.dragging = true;
23735         this.proxy.update("");
23736         this.onInitDrag(x, y);
23737         this.proxy.show();
23738     },
23739
23740     // private
23741     onInitDrag : function(x, y){
23742         var clone = this.el.dom.cloneNode(true);
23743         clone.id = Roo.id(); // prevent duplicate ids
23744         this.proxy.update(clone);
23745         this.onStartDrag(x, y);
23746         return true;
23747     },
23748
23749     /**
23750      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23751      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23752      */
23753     getProxy : function(){
23754         return this.proxy;  
23755     },
23756
23757     /**
23758      * Hides the drag source's {@link Roo.dd.StatusProxy}
23759      */
23760     hideProxy : function(){
23761         this.proxy.hide();  
23762         this.proxy.reset(true);
23763         this.dragging = false;
23764     },
23765
23766     // private
23767     triggerCacheRefresh : function(){
23768         Roo.dd.DDM.refreshCache(this.groups);
23769     },
23770
23771     // private - override to prevent hiding
23772     b4EndDrag: function(e) {
23773     },
23774
23775     // private - override to prevent moving
23776     endDrag : function(e){
23777         this.onEndDrag(this.dragData, e);
23778     },
23779
23780     // private
23781     onEndDrag : function(data, e){
23782     },
23783     
23784     // private - pin to cursor
23785     autoOffset : function(x, y) {
23786         this.setDelta(-12, -20);
23787     }    
23788 });/*
23789  * Based on:
23790  * Ext JS Library 1.1.1
23791  * Copyright(c) 2006-2007, Ext JS, LLC.
23792  *
23793  * Originally Released Under LGPL - original licence link has changed is not relivant.
23794  *
23795  * Fork - LGPL
23796  * <script type="text/javascript">
23797  */
23798
23799
23800 /**
23801  * @class Roo.dd.DropTarget
23802  * @extends Roo.dd.DDTarget
23803  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23804  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23805  * @constructor
23806  * @param {String/HTMLElement/Element} el The container element
23807  * @param {Object} config
23808  */
23809 Roo.dd.DropTarget = function(el, config){
23810     this.el = Roo.get(el);
23811     
23812     var listeners = false; ;
23813     if (config && config.listeners) {
23814         listeners= config.listeners;
23815         delete config.listeners;
23816     }
23817     Roo.apply(this, config);
23818     
23819     if(this.containerScroll){
23820         Roo.dd.ScrollManager.register(this.el);
23821     }
23822     this.addEvents( {
23823          /**
23824          * @scope Roo.dd.DropTarget
23825          */
23826          
23827          /**
23828          * @event enter
23829          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23830          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23831          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23832          * 
23833          * IMPORTANT : it should set  this.valid to true|false
23834          * 
23835          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23836          * @param {Event} e The event
23837          * @param {Object} data An object containing arbitrary data supplied by the drag source
23838          */
23839         "enter" : true,
23840         
23841          /**
23842          * @event over
23843          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23844          * This method will be called on every mouse movement while the drag source is over the drop target.
23845          * This default implementation simply returns the dropAllowed config value.
23846          * 
23847          * IMPORTANT : it should set  this.valid to true|false
23848          * 
23849          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23850          * @param {Event} e The event
23851          * @param {Object} data An object containing arbitrary data supplied by the drag source
23852          
23853          */
23854         "over" : true,
23855         /**
23856          * @event out
23857          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23858          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23859          * overClass (if any) from the drop element.
23860          * 
23861          * 
23862          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23863          * @param {Event} e The event
23864          * @param {Object} data An object containing arbitrary data supplied by the drag source
23865          */
23866          "out" : true,
23867          
23868         /**
23869          * @event drop
23870          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23871          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23872          * implementation that does something to process the drop event and returns true so that the drag source's
23873          * repair action does not run.
23874          * 
23875          * IMPORTANT : it should set this.success
23876          * 
23877          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23878          * @param {Event} e The event
23879          * @param {Object} data An object containing arbitrary data supplied by the drag source
23880         */
23881          "drop" : true
23882     });
23883             
23884      
23885     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23886         this.el.dom, 
23887         this.ddGroup || this.group,
23888         {
23889             isTarget: true,
23890             listeners : listeners || {} 
23891            
23892         
23893         }
23894     );
23895
23896 };
23897
23898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23899     /**
23900      * @cfg {String} overClass
23901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23902      */
23903      /**
23904      * @cfg {String} ddGroup
23905      * The drag drop group to handle drop events for
23906      */
23907      
23908     /**
23909      * @cfg {String} dropAllowed
23910      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23911      */
23912     dropAllowed : "x-dd-drop-ok",
23913     /**
23914      * @cfg {String} dropNotAllowed
23915      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23916      */
23917     dropNotAllowed : "x-dd-drop-nodrop",
23918     /**
23919      * @cfg {boolean} success
23920      * set this after drop listener.. 
23921      */
23922     success : false,
23923     /**
23924      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23925      * if the drop point is valid for over/enter..
23926      */
23927     valid : false,
23928     // private
23929     isTarget : true,
23930
23931     // private
23932     isNotifyTarget : true,
23933     
23934     /**
23935      * @hide
23936      */
23937     notifyEnter : function(dd, e, data)
23938     {
23939         this.valid = true;
23940         this.fireEvent('enter', dd, e, data);
23941         if(this.overClass){
23942             this.el.addClass(this.overClass);
23943         }
23944         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23945             this.valid ? this.dropAllowed : this.dropNotAllowed
23946         );
23947     },
23948
23949     /**
23950      * @hide
23951      */
23952     notifyOver : function(dd, e, data)
23953     {
23954         this.valid = true;
23955         this.fireEvent('over', dd, e, data);
23956         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23957             this.valid ? this.dropAllowed : this.dropNotAllowed
23958         );
23959     },
23960
23961     /**
23962      * @hide
23963      */
23964     notifyOut : function(dd, e, data)
23965     {
23966         this.fireEvent('out', dd, e, data);
23967         if(this.overClass){
23968             this.el.removeClass(this.overClass);
23969         }
23970     },
23971
23972     /**
23973      * @hide
23974      */
23975     notifyDrop : function(dd, e, data)
23976     {
23977         this.success = false;
23978         this.fireEvent('drop', dd, e, data);
23979         return this.success;
23980     }
23981 });/*
23982  * Based on:
23983  * Ext JS Library 1.1.1
23984  * Copyright(c) 2006-2007, Ext JS, LLC.
23985  *
23986  * Originally Released Under LGPL - original licence link has changed is not relivant.
23987  *
23988  * Fork - LGPL
23989  * <script type="text/javascript">
23990  */
23991
23992
23993 /**
23994  * @class Roo.dd.DragZone
23995  * @extends Roo.dd.DragSource
23996  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23997  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23998  * @constructor
23999  * @param {String/HTMLElement/Element} el The container element
24000  * @param {Object} config
24001  */
24002 Roo.dd.DragZone = function(el, config){
24003     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24004     if(this.containerScroll){
24005         Roo.dd.ScrollManager.register(this.el);
24006     }
24007 };
24008
24009 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24010     /**
24011      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24012      * for auto scrolling during drag operations.
24013      */
24014     /**
24015      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24016      * method after a failed drop (defaults to "c3daf9" - light blue)
24017      */
24018
24019     /**
24020      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24021      * for a valid target to drag based on the mouse down. Override this method
24022      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24023      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24024      * @param {EventObject} e The mouse down event
24025      * @return {Object} The dragData
24026      */
24027     getDragData : function(e){
24028         return Roo.dd.Registry.getHandleFromEvent(e);
24029     },
24030     
24031     /**
24032      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24033      * this.dragData.ddel
24034      * @param {Number} x The x position of the click on the dragged object
24035      * @param {Number} y The y position of the click on the dragged object
24036      * @return {Boolean} true to continue the drag, false to cancel
24037      */
24038     onInitDrag : function(x, y){
24039         this.proxy.update(this.dragData.ddel.cloneNode(true));
24040         this.onStartDrag(x, y);
24041         return true;
24042     },
24043     
24044     /**
24045      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24046      */
24047     afterRepair : function(){
24048         if(Roo.enableFx){
24049             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24050         }
24051         this.dragging = false;
24052     },
24053
24054     /**
24055      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24056      * the XY of this.dragData.ddel
24057      * @param {EventObject} e The mouse up event
24058      * @return {Array} The xy location (e.g. [100, 200])
24059      */
24060     getRepairXY : function(e){
24061         return Roo.Element.fly(this.dragData.ddel).getXY();  
24062     }
24063 });/*
24064  * Based on:
24065  * Ext JS Library 1.1.1
24066  * Copyright(c) 2006-2007, Ext JS, LLC.
24067  *
24068  * Originally Released Under LGPL - original licence link has changed is not relivant.
24069  *
24070  * Fork - LGPL
24071  * <script type="text/javascript">
24072  */
24073 /**
24074  * @class Roo.dd.DropZone
24075  * @extends Roo.dd.DropTarget
24076  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24077  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24078  * @constructor
24079  * @param {String/HTMLElement/Element} el The container element
24080  * @param {Object} config
24081  */
24082 Roo.dd.DropZone = function(el, config){
24083     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24084 };
24085
24086 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24087     /**
24088      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24089      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24090      * provide your own custom lookup.
24091      * @param {Event} e The event
24092      * @return {Object} data The custom data
24093      */
24094     getTargetFromEvent : function(e){
24095         return Roo.dd.Registry.getTargetFromEvent(e);
24096     },
24097
24098     /**
24099      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24100      * that it has registered.  This method has no default implementation and should be overridden to provide
24101      * node-specific processing if necessary.
24102      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24103      * {@link #getTargetFromEvent} for this node)
24104      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24105      * @param {Event} e The event
24106      * @param {Object} data An object containing arbitrary data supplied by the drag source
24107      */
24108     onNodeEnter : function(n, dd, e, data){
24109         
24110     },
24111
24112     /**
24113      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24114      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24115      * overridden to provide the proper feedback.
24116      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24117      * {@link #getTargetFromEvent} for this node)
24118      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24119      * @param {Event} e The event
24120      * @param {Object} data An object containing arbitrary data supplied by the drag source
24121      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24122      * underlying {@link Roo.dd.StatusProxy} can be updated
24123      */
24124     onNodeOver : function(n, dd, e, data){
24125         return this.dropAllowed;
24126     },
24127
24128     /**
24129      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24130      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24131      * node-specific processing if necessary.
24132      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24133      * {@link #getTargetFromEvent} for this node)
24134      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24135      * @param {Event} e The event
24136      * @param {Object} data An object containing arbitrary data supplied by the drag source
24137      */
24138     onNodeOut : function(n, dd, e, data){
24139         
24140     },
24141
24142     /**
24143      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24144      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24145      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24146      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24147      * {@link #getTargetFromEvent} for this node)
24148      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24149      * @param {Event} e The event
24150      * @param {Object} data An object containing arbitrary data supplied by the drag source
24151      * @return {Boolean} True if the drop was valid, else false
24152      */
24153     onNodeDrop : function(n, dd, e, data){
24154         return false;
24155     },
24156
24157     /**
24158      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24159      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24160      * it should be overridden to provide the proper feedback if necessary.
24161      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24162      * @param {Event} e The event
24163      * @param {Object} data An object containing arbitrary data supplied by the drag source
24164      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24165      * underlying {@link Roo.dd.StatusProxy} can be updated
24166      */
24167     onContainerOver : function(dd, e, data){
24168         return this.dropNotAllowed;
24169     },
24170
24171     /**
24172      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24173      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24174      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24175      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24176      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24177      * @param {Event} e The event
24178      * @param {Object} data An object containing arbitrary data supplied by the drag source
24179      * @return {Boolean} True if the drop was valid, else false
24180      */
24181     onContainerDrop : function(dd, e, data){
24182         return false;
24183     },
24184
24185     /**
24186      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24187      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24188      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24189      * you should override this method and provide a custom implementation.
24190      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24191      * @param {Event} e The event
24192      * @param {Object} data An object containing arbitrary data supplied by the drag source
24193      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24194      * underlying {@link Roo.dd.StatusProxy} can be updated
24195      */
24196     notifyEnter : function(dd, e, data){
24197         return this.dropNotAllowed;
24198     },
24199
24200     /**
24201      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24202      * This method will be called on every mouse movement while the drag source is over the drop zone.
24203      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24204      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24205      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24206      * registered node, it will call {@link #onContainerOver}.
24207      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24208      * @param {Event} e The event
24209      * @param {Object} data An object containing arbitrary data supplied by the drag source
24210      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24211      * underlying {@link Roo.dd.StatusProxy} can be updated
24212      */
24213     notifyOver : function(dd, e, data){
24214         var n = this.getTargetFromEvent(e);
24215         if(!n){ // not over valid drop target
24216             if(this.lastOverNode){
24217                 this.onNodeOut(this.lastOverNode, dd, e, data);
24218                 this.lastOverNode = null;
24219             }
24220             return this.onContainerOver(dd, e, data);
24221         }
24222         if(this.lastOverNode != n){
24223             if(this.lastOverNode){
24224                 this.onNodeOut(this.lastOverNode, dd, e, data);
24225             }
24226             this.onNodeEnter(n, dd, e, data);
24227             this.lastOverNode = n;
24228         }
24229         return this.onNodeOver(n, dd, e, data);
24230     },
24231
24232     /**
24233      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24234      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24235      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24236      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24237      * @param {Event} e The event
24238      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24239      */
24240     notifyOut : function(dd, e, data){
24241         if(this.lastOverNode){
24242             this.onNodeOut(this.lastOverNode, dd, e, data);
24243             this.lastOverNode = null;
24244         }
24245     },
24246
24247     /**
24248      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24249      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24250      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24251      * otherwise it will call {@link #onContainerDrop}.
24252      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24253      * @param {Event} e The event
24254      * @param {Object} data An object containing arbitrary data supplied by the drag source
24255      * @return {Boolean} True if the drop was valid, else false
24256      */
24257     notifyDrop : function(dd, e, data){
24258         if(this.lastOverNode){
24259             this.onNodeOut(this.lastOverNode, dd, e, data);
24260             this.lastOverNode = null;
24261         }
24262         var n = this.getTargetFromEvent(e);
24263         return n ?
24264             this.onNodeDrop(n, dd, e, data) :
24265             this.onContainerDrop(dd, e, data);
24266     },
24267
24268     // private
24269     triggerCacheRefresh : function(){
24270         Roo.dd.DDM.refreshCache(this.groups);
24271     }  
24272 });/*
24273  * Based on:
24274  * Ext JS Library 1.1.1
24275  * Copyright(c) 2006-2007, Ext JS, LLC.
24276  *
24277  * Originally Released Under LGPL - original licence link has changed is not relivant.
24278  *
24279  * Fork - LGPL
24280  * <script type="text/javascript">
24281  */
24282
24283
24284 /**
24285  * @class Roo.data.SortTypes
24286  * @static
24287  * Defines the default sorting (casting?) comparison functions used when sorting data.
24288  */
24289 Roo.data.SortTypes = {
24290     /**
24291      * Default sort that does nothing
24292      * @param {Mixed} s The value being converted
24293      * @return {Mixed} The comparison value
24294      */
24295     none : function(s){
24296         return s;
24297     },
24298     
24299     /**
24300      * The regular expression used to strip tags
24301      * @type {RegExp}
24302      * @property
24303      */
24304     stripTagsRE : /<\/?[^>]+>/gi,
24305     
24306     /**
24307      * Strips all HTML tags to sort on text only
24308      * @param {Mixed} s The value being converted
24309      * @return {String} The comparison value
24310      */
24311     asText : function(s){
24312         return String(s).replace(this.stripTagsRE, "");
24313     },
24314     
24315     /**
24316      * Strips all HTML tags to sort on text only - Case insensitive
24317      * @param {Mixed} s The value being converted
24318      * @return {String} The comparison value
24319      */
24320     asUCText : function(s){
24321         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24322     },
24323     
24324     /**
24325      * Case insensitive string
24326      * @param {Mixed} s The value being converted
24327      * @return {String} The comparison value
24328      */
24329     asUCString : function(s) {
24330         return String(s).toUpperCase();
24331     },
24332     
24333     /**
24334      * Date sorting
24335      * @param {Mixed} s The value being converted
24336      * @return {Number} The comparison value
24337      */
24338     asDate : function(s) {
24339         if(!s){
24340             return 0;
24341         }
24342         if(s instanceof Date){
24343             return s.getTime();
24344         }
24345         return Date.parse(String(s));
24346     },
24347     
24348     /**
24349      * Float sorting
24350      * @param {Mixed} s The value being converted
24351      * @return {Float} The comparison value
24352      */
24353     asFloat : function(s) {
24354         var val = parseFloat(String(s).replace(/,/g, ""));
24355         if(isNaN(val)) {
24356             val = 0;
24357         }
24358         return val;
24359     },
24360     
24361     /**
24362      * Integer sorting
24363      * @param {Mixed} s The value being converted
24364      * @return {Number} The comparison value
24365      */
24366     asInt : function(s) {
24367         var val = parseInt(String(s).replace(/,/g, ""));
24368         if(isNaN(val)) {
24369             val = 0;
24370         }
24371         return val;
24372     }
24373 };/*
24374  * Based on:
24375  * Ext JS Library 1.1.1
24376  * Copyright(c) 2006-2007, Ext JS, LLC.
24377  *
24378  * Originally Released Under LGPL - original licence link has changed is not relivant.
24379  *
24380  * Fork - LGPL
24381  * <script type="text/javascript">
24382  */
24383
24384 /**
24385 * @class Roo.data.Record
24386  * Instances of this class encapsulate both record <em>definition</em> information, and record
24387  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24388  * to access Records cached in an {@link Roo.data.Store} object.<br>
24389  * <p>
24390  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24391  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24392  * objects.<br>
24393  * <p>
24394  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24395  * @constructor
24396  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24397  * {@link #create}. The parameters are the same.
24398  * @param {Array} data An associative Array of data values keyed by the field name.
24399  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24400  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24401  * not specified an integer id is generated.
24402  */
24403 Roo.data.Record = function(data, id){
24404     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24405     this.data = data;
24406 };
24407
24408 /**
24409  * Generate a constructor for a specific record layout.
24410  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24411  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24412  * Each field definition object may contain the following properties: <ul>
24413  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
24414  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24415  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24416  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24417  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24418  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24419  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24420  * this may be omitted.</p></li>
24421  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24422  * <ul><li>auto (Default, implies no conversion)</li>
24423  * <li>string</li>
24424  * <li>int</li>
24425  * <li>float</li>
24426  * <li>boolean</li>
24427  * <li>date</li></ul></p></li>
24428  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24429  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24430  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24431  * by the Reader into an object that will be stored in the Record. It is passed the
24432  * following parameters:<ul>
24433  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24434  * </ul></p></li>
24435  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24436  * </ul>
24437  * <br>usage:<br><pre><code>
24438 var TopicRecord = Roo.data.Record.create(
24439     {name: 'title', mapping: 'topic_title'},
24440     {name: 'author', mapping: 'username'},
24441     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24442     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24443     {name: 'lastPoster', mapping: 'user2'},
24444     {name: 'excerpt', mapping: 'post_text'}
24445 );
24446
24447 var myNewRecord = new TopicRecord({
24448     title: 'Do my job please',
24449     author: 'noobie',
24450     totalPosts: 1,
24451     lastPost: new Date(),
24452     lastPoster: 'Animal',
24453     excerpt: 'No way dude!'
24454 });
24455 myStore.add(myNewRecord);
24456 </code></pre>
24457  * @method create
24458  * @static
24459  */
24460 Roo.data.Record.create = function(o){
24461     var f = function(){
24462         f.superclass.constructor.apply(this, arguments);
24463     };
24464     Roo.extend(f, Roo.data.Record);
24465     var p = f.prototype;
24466     p.fields = new Roo.util.MixedCollection(false, function(field){
24467         return field.name;
24468     });
24469     for(var i = 0, len = o.length; i < len; i++){
24470         p.fields.add(new Roo.data.Field(o[i]));
24471     }
24472     f.getField = function(name){
24473         return p.fields.get(name);  
24474     };
24475     return f;
24476 };
24477
24478 Roo.data.Record.AUTO_ID = 1000;
24479 Roo.data.Record.EDIT = 'edit';
24480 Roo.data.Record.REJECT = 'reject';
24481 Roo.data.Record.COMMIT = 'commit';
24482
24483 Roo.data.Record.prototype = {
24484     /**
24485      * Readonly flag - true if this record has been modified.
24486      * @type Boolean
24487      */
24488     dirty : false,
24489     editing : false,
24490     error: null,
24491     modified: null,
24492
24493     // private
24494     join : function(store){
24495         this.store = store;
24496     },
24497
24498     /**
24499      * Set the named field to the specified value.
24500      * @param {String} name The name of the field to set.
24501      * @param {Object} value The value to set the field to.
24502      */
24503     set : function(name, value){
24504         if(this.data[name] == value){
24505             return;
24506         }
24507         this.dirty = true;
24508         if(!this.modified){
24509             this.modified = {};
24510         }
24511         if(typeof this.modified[name] == 'undefined'){
24512             this.modified[name] = this.data[name];
24513         }
24514         this.data[name] = value;
24515         if(!this.editing && this.store){
24516             this.store.afterEdit(this);
24517         }       
24518     },
24519
24520     /**
24521      * Get the value of the named field.
24522      * @param {String} name The name of the field to get the value of.
24523      * @return {Object} The value of the field.
24524      */
24525     get : function(name){
24526         return this.data[name]; 
24527     },
24528
24529     // private
24530     beginEdit : function(){
24531         this.editing = true;
24532         this.modified = {}; 
24533     },
24534
24535     // private
24536     cancelEdit : function(){
24537         this.editing = false;
24538         delete this.modified;
24539     },
24540
24541     // private
24542     endEdit : function(){
24543         this.editing = false;
24544         if(this.dirty && this.store){
24545             this.store.afterEdit(this);
24546         }
24547     },
24548
24549     /**
24550      * Usually called by the {@link Roo.data.Store} which owns the Record.
24551      * Rejects all changes made to the Record since either creation, or the last commit operation.
24552      * Modified fields are reverted to their original values.
24553      * <p>
24554      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24555      * of reject operations.
24556      */
24557     reject : function(){
24558         var m = this.modified;
24559         for(var n in m){
24560             if(typeof m[n] != "function"){
24561                 this.data[n] = m[n];
24562             }
24563         }
24564         this.dirty = false;
24565         delete this.modified;
24566         this.editing = false;
24567         if(this.store){
24568             this.store.afterReject(this);
24569         }
24570     },
24571
24572     /**
24573      * Usually called by the {@link Roo.data.Store} which owns the Record.
24574      * Commits all changes made to the Record since either creation, or the last commit operation.
24575      * <p>
24576      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24577      * of commit operations.
24578      */
24579     commit : function(){
24580         this.dirty = false;
24581         delete this.modified;
24582         this.editing = false;
24583         if(this.store){
24584             this.store.afterCommit(this);
24585         }
24586     },
24587
24588     // private
24589     hasError : function(){
24590         return this.error != null;
24591     },
24592
24593     // private
24594     clearError : function(){
24595         this.error = null;
24596     },
24597
24598     /**
24599      * Creates a copy of this record.
24600      * @param {String} id (optional) A new record id if you don't want to use this record's id
24601      * @return {Record}
24602      */
24603     copy : function(newId) {
24604         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24605     }
24606 };/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616
24617
24618
24619 /**
24620  * @class Roo.data.Store
24621  * @extends Roo.util.Observable
24622  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24623  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24624  * <p>
24625  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
24626  * has no knowledge of the format of the data returned by the Proxy.<br>
24627  * <p>
24628  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24629  * instances from the data object. These records are cached and made available through accessor functions.
24630  * @constructor
24631  * Creates a new Store.
24632  * @param {Object} config A config object containing the objects needed for the Store to access data,
24633  * and read the data into Records.
24634  */
24635 Roo.data.Store = function(config){
24636     this.data = new Roo.util.MixedCollection(false);
24637     this.data.getKey = function(o){
24638         return o.id;
24639     };
24640     this.baseParams = {};
24641     // private
24642     this.paramNames = {
24643         "start" : "start",
24644         "limit" : "limit",
24645         "sort" : "sort",
24646         "dir" : "dir",
24647         "multisort" : "_multisort"
24648     };
24649
24650     if(config && config.data){
24651         this.inlineData = config.data;
24652         delete config.data;
24653     }
24654
24655     Roo.apply(this, config);
24656     
24657     if(this.reader){ // reader passed
24658         this.reader = Roo.factory(this.reader, Roo.data);
24659         this.reader.xmodule = this.xmodule || false;
24660         if(!this.recordType){
24661             this.recordType = this.reader.recordType;
24662         }
24663         if(this.reader.onMetaChange){
24664             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24665         }
24666     }
24667
24668     if(this.recordType){
24669         this.fields = this.recordType.prototype.fields;
24670     }
24671     this.modified = [];
24672
24673     this.addEvents({
24674         /**
24675          * @event datachanged
24676          * Fires when the data cache has changed, and a widget which is using this Store
24677          * as a Record cache should refresh its view.
24678          * @param {Store} this
24679          */
24680         datachanged : true,
24681         /**
24682          * @event metachange
24683          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24684          * @param {Store} this
24685          * @param {Object} meta The JSON metadata
24686          */
24687         metachange : true,
24688         /**
24689          * @event add
24690          * Fires when Records have been added to the Store
24691          * @param {Store} this
24692          * @param {Roo.data.Record[]} records The array of Records added
24693          * @param {Number} index The index at which the record(s) were added
24694          */
24695         add : true,
24696         /**
24697          * @event remove
24698          * Fires when a Record has been removed from the Store
24699          * @param {Store} this
24700          * @param {Roo.data.Record} record The Record that was removed
24701          * @param {Number} index The index at which the record was removed
24702          */
24703         remove : true,
24704         /**
24705          * @event update
24706          * Fires when a Record has been updated
24707          * @param {Store} this
24708          * @param {Roo.data.Record} record The Record that was updated
24709          * @param {String} operation The update operation being performed.  Value may be one of:
24710          * <pre><code>
24711  Roo.data.Record.EDIT
24712  Roo.data.Record.REJECT
24713  Roo.data.Record.COMMIT
24714          * </code></pre>
24715          */
24716         update : true,
24717         /**
24718          * @event clear
24719          * Fires when the data cache has been cleared.
24720          * @param {Store} this
24721          */
24722         clear : true,
24723         /**
24724          * @event beforeload
24725          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24726          * the load action will be canceled.
24727          * @param {Store} this
24728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24729          */
24730         beforeload : true,
24731         /**
24732          * @event beforeloadadd
24733          * Fires after a new set of Records has been loaded.
24734          * @param {Store} this
24735          * @param {Roo.data.Record[]} records The Records that were loaded
24736          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24737          */
24738         beforeloadadd : true,
24739         /**
24740          * @event load
24741          * Fires after a new set of Records has been loaded, before they are added to the store.
24742          * @param {Store} this
24743          * @param {Roo.data.Record[]} records The Records that were loaded
24744          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24745          * @params {Object} return from reader
24746          */
24747         load : true,
24748         /**
24749          * @event loadexception
24750          * Fires if an exception occurs in the Proxy during loading.
24751          * Called with the signature of the Proxy's "loadexception" event.
24752          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24753          * 
24754          * @param {Proxy} 
24755          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24756          * @param {Object} load options 
24757          * @param {Object} jsonData from your request (normally this contains the Exception)
24758          */
24759         loadexception : true
24760     });
24761     
24762     if(this.proxy){
24763         this.proxy = Roo.factory(this.proxy, Roo.data);
24764         this.proxy.xmodule = this.xmodule || false;
24765         this.relayEvents(this.proxy,  ["loadexception"]);
24766     }
24767     this.sortToggle = {};
24768     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24769
24770     Roo.data.Store.superclass.constructor.call(this);
24771
24772     if(this.inlineData){
24773         this.loadData(this.inlineData);
24774         delete this.inlineData;
24775     }
24776 };
24777
24778 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24779      /**
24780     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24781     * without a remote query - used by combo/forms at present.
24782     */
24783     
24784     /**
24785     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24786     */
24787     /**
24788     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24789     */
24790     /**
24791     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24792     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24793     */
24794     /**
24795     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24796     * on any HTTP request
24797     */
24798     /**
24799     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24800     */
24801     /**
24802     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24803     */
24804     multiSort: false,
24805     /**
24806     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24807     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24808     */
24809     remoteSort : false,
24810
24811     /**
24812     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24813      * loaded or when a record is removed. (defaults to false).
24814     */
24815     pruneModifiedRecords : false,
24816
24817     // private
24818     lastOptions : null,
24819
24820     /**
24821      * Add Records to the Store and fires the add event.
24822      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24823      */
24824     add : function(records){
24825         records = [].concat(records);
24826         for(var i = 0, len = records.length; i < len; i++){
24827             records[i].join(this);
24828         }
24829         var index = this.data.length;
24830         this.data.addAll(records);
24831         this.fireEvent("add", this, records, index);
24832     },
24833
24834     /**
24835      * Remove a Record from the Store and fires the remove event.
24836      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24837      */
24838     remove : function(record){
24839         var index = this.data.indexOf(record);
24840         this.data.removeAt(index);
24841  
24842         if(this.pruneModifiedRecords){
24843             this.modified.remove(record);
24844         }
24845         this.fireEvent("remove", this, record, index);
24846     },
24847
24848     /**
24849      * Remove all Records from the Store and fires the clear event.
24850      */
24851     removeAll : function(){
24852         this.data.clear();
24853         if(this.pruneModifiedRecords){
24854             this.modified = [];
24855         }
24856         this.fireEvent("clear", this);
24857     },
24858
24859     /**
24860      * Inserts Records to the Store at the given index and fires the add event.
24861      * @param {Number} index The start index at which to insert the passed Records.
24862      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24863      */
24864     insert : function(index, records){
24865         records = [].concat(records);
24866         for(var i = 0, len = records.length; i < len; i++){
24867             this.data.insert(index, records[i]);
24868             records[i].join(this);
24869         }
24870         this.fireEvent("add", this, records, index);
24871     },
24872
24873     /**
24874      * Get the index within the cache of the passed Record.
24875      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24876      * @return {Number} The index of the passed Record. Returns -1 if not found.
24877      */
24878     indexOf : function(record){
24879         return this.data.indexOf(record);
24880     },
24881
24882     /**
24883      * Get the index within the cache of the Record with the passed id.
24884      * @param {String} id The id of the Record to find.
24885      * @return {Number} The index of the Record. Returns -1 if not found.
24886      */
24887     indexOfId : function(id){
24888         return this.data.indexOfKey(id);
24889     },
24890
24891     /**
24892      * Get the Record with the specified id.
24893      * @param {String} id The id of the Record to find.
24894      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24895      */
24896     getById : function(id){
24897         return this.data.key(id);
24898     },
24899
24900     /**
24901      * Get the Record at the specified index.
24902      * @param {Number} index The index of the Record to find.
24903      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24904      */
24905     getAt : function(index){
24906         return this.data.itemAt(index);
24907     },
24908
24909     /**
24910      * Returns a range of Records between specified indices.
24911      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24912      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24913      * @return {Roo.data.Record[]} An array of Records
24914      */
24915     getRange : function(start, end){
24916         return this.data.getRange(start, end);
24917     },
24918
24919     // private
24920     storeOptions : function(o){
24921         o = Roo.apply({}, o);
24922         delete o.callback;
24923         delete o.scope;
24924         this.lastOptions = o;
24925     },
24926
24927     /**
24928      * Loads the Record cache from the configured Proxy using the configured Reader.
24929      * <p>
24930      * If using remote paging, then the first load call must specify the <em>start</em>
24931      * and <em>limit</em> properties in the options.params property to establish the initial
24932      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24933      * <p>
24934      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24935      * and this call will return before the new data has been loaded. Perform any post-processing
24936      * in a callback function, or in a "load" event handler.</strong>
24937      * <p>
24938      * @param {Object} options An object containing properties which control loading options:<ul>
24939      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24940      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24941      * <pre>
24942                 {
24943                     data : data,  // array of key=>value data like JsonReader
24944                     total : data.length,
24945                     success : true
24946                     
24947                 }
24948         </pre>
24949             }.</li>
24950      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24951      * passed the following arguments:<ul>
24952      * <li>r : Roo.data.Record[]</li>
24953      * <li>options: Options object from the load call</li>
24954      * <li>success: Boolean success indicator</li></ul></li>
24955      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24956      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24957      * </ul>
24958      */
24959     load : function(options){
24960         options = options || {};
24961         if(this.fireEvent("beforeload", this, options) !== false){
24962             this.storeOptions(options);
24963             var p = Roo.apply(options.params || {}, this.baseParams);
24964             // if meta was not loaded from remote source.. try requesting it.
24965             if (!this.reader.metaFromRemote) {
24966                 p._requestMeta = 1;
24967             }
24968             if(this.sortInfo && this.remoteSort){
24969                 var pn = this.paramNames;
24970                 p[pn["sort"]] = this.sortInfo.field;
24971                 p[pn["dir"]] = this.sortInfo.direction;
24972             }
24973             if (this.multiSort) {
24974                 var pn = this.paramNames;
24975                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24976             }
24977             
24978             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24979         }
24980     },
24981
24982     /**
24983      * Reloads the Record cache from the configured Proxy using the configured Reader and
24984      * the options from the last load operation performed.
24985      * @param {Object} options (optional) An object containing properties which may override the options
24986      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24987      * the most recently used options are reused).
24988      */
24989     reload : function(options){
24990         this.load(Roo.applyIf(options||{}, this.lastOptions));
24991     },
24992
24993     // private
24994     // Called as a callback by the Reader during a load operation.
24995     loadRecords : function(o, options, success){
24996          
24997         if(!o){
24998             if(success !== false){
24999                 this.fireEvent("load", this, [], options, o);
25000             }
25001             if(options.callback){
25002                 options.callback.call(options.scope || this, [], options, false);
25003             }
25004             return;
25005         }
25006         // if data returned failure - throw an exception.
25007         if (o.success === false) {
25008             // show a message if no listener is registered.
25009             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25010                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25011             }
25012             // loadmask wil be hooked into this..
25013             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25014             return;
25015         }
25016         var r = o.records, t = o.totalRecords || r.length;
25017         
25018         this.fireEvent("beforeloadadd", this, r, options, o);
25019         
25020         if(!options || options.add !== true){
25021             if(this.pruneModifiedRecords){
25022                 this.modified = [];
25023             }
25024             for(var i = 0, len = r.length; i < len; i++){
25025                 r[i].join(this);
25026             }
25027             if(this.snapshot){
25028                 this.data = this.snapshot;
25029                 delete this.snapshot;
25030             }
25031             this.data.clear();
25032             this.data.addAll(r);
25033             this.totalLength = t;
25034             this.applySort();
25035             this.fireEvent("datachanged", this);
25036         }else{
25037             this.totalLength = Math.max(t, this.data.length+r.length);
25038             this.add(r);
25039         }
25040         
25041         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25042                 
25043             var e = new Roo.data.Record({});
25044
25045             e.set(this.parent.displayField, this.parent.emptyTitle);
25046             e.set(this.parent.valueField, '');
25047
25048             this.insert(0, e);
25049         }
25050             
25051         this.fireEvent("load", this, r, options, o);
25052         if(options.callback){
25053             options.callback.call(options.scope || this, r, options, true);
25054         }
25055     },
25056
25057
25058     /**
25059      * Loads data from a passed data block. A Reader which understands the format of the data
25060      * must have been configured in the constructor.
25061      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25062      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25063      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25064      */
25065     loadData : function(o, append){
25066         var r = this.reader.readRecords(o);
25067         this.loadRecords(r, {add: append}, true);
25068     },
25069     
25070      /**
25071      * using 'cn' the nested child reader read the child array into it's child stores.
25072      * @param {Object} rec The record with a 'children array
25073      */
25074     loadDataFromChildren : function(rec)
25075     {
25076         this.loadData(this.reader.toLoadData(rec));
25077     },
25078     
25079
25080     /**
25081      * Gets the number of cached records.
25082      * <p>
25083      * <em>If using paging, this may not be the total size of the dataset. If the data object
25084      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25085      * the data set size</em>
25086      */
25087     getCount : function(){
25088         return this.data.length || 0;
25089     },
25090
25091     /**
25092      * Gets the total number of records in the dataset as returned by the server.
25093      * <p>
25094      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25095      * the dataset size</em>
25096      */
25097     getTotalCount : function(){
25098         return this.totalLength || 0;
25099     },
25100
25101     /**
25102      * Returns the sort state of the Store as an object with two properties:
25103      * <pre><code>
25104  field {String} The name of the field by which the Records are sorted
25105  direction {String} The sort order, "ASC" or "DESC"
25106      * </code></pre>
25107      */
25108     getSortState : function(){
25109         return this.sortInfo;
25110     },
25111
25112     // private
25113     applySort : function(){
25114         if(this.sortInfo && !this.remoteSort){
25115             var s = this.sortInfo, f = s.field;
25116             var st = this.fields.get(f).sortType;
25117             var fn = function(r1, r2){
25118                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25119                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25120             };
25121             this.data.sort(s.direction, fn);
25122             if(this.snapshot && this.snapshot != this.data){
25123                 this.snapshot.sort(s.direction, fn);
25124             }
25125         }
25126     },
25127
25128     /**
25129      * Sets the default sort column and order to be used by the next load operation.
25130      * @param {String} fieldName The name of the field to sort by.
25131      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25132      */
25133     setDefaultSort : function(field, dir){
25134         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25135     },
25136
25137     /**
25138      * Sort the Records.
25139      * If remote sorting is used, the sort is performed on the server, and the cache is
25140      * reloaded. If local sorting is used, the cache is sorted internally.
25141      * @param {String} fieldName The name of the field to sort by.
25142      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25143      */
25144     sort : function(fieldName, dir){
25145         var f = this.fields.get(fieldName);
25146         if(!dir){
25147             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25148             
25149             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25150                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25151             }else{
25152                 dir = f.sortDir;
25153             }
25154         }
25155         this.sortToggle[f.name] = dir;
25156         this.sortInfo = {field: f.name, direction: dir};
25157         if(!this.remoteSort){
25158             this.applySort();
25159             this.fireEvent("datachanged", this);
25160         }else{
25161             this.load(this.lastOptions);
25162         }
25163     },
25164
25165     /**
25166      * Calls the specified function for each of the Records in the cache.
25167      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25168      * Returning <em>false</em> aborts and exits the iteration.
25169      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25170      */
25171     each : function(fn, scope){
25172         this.data.each(fn, scope);
25173     },
25174
25175     /**
25176      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25177      * (e.g., during paging).
25178      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25179      */
25180     getModifiedRecords : function(){
25181         return this.modified;
25182     },
25183
25184     // private
25185     createFilterFn : function(property, value, anyMatch){
25186         if(!value.exec){ // not a regex
25187             value = String(value);
25188             if(value.length == 0){
25189                 return false;
25190             }
25191             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25192         }
25193         return function(r){
25194             return value.test(r.data[property]);
25195         };
25196     },
25197
25198     /**
25199      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25200      * @param {String} property A field on your records
25201      * @param {Number} start The record index to start at (defaults to 0)
25202      * @param {Number} end The last record index to include (defaults to length - 1)
25203      * @return {Number} The sum
25204      */
25205     sum : function(property, start, end){
25206         var rs = this.data.items, v = 0;
25207         start = start || 0;
25208         end = (end || end === 0) ? end : rs.length-1;
25209
25210         for(var i = start; i <= end; i++){
25211             v += (rs[i].data[property] || 0);
25212         }
25213         return v;
25214     },
25215
25216     /**
25217      * Filter the records by a specified property.
25218      * @param {String} field A field on your records
25219      * @param {String/RegExp} value Either a string that the field
25220      * should start with or a RegExp to test against the field
25221      * @param {Boolean} anyMatch True to match any part not just the beginning
25222      */
25223     filter : function(property, value, anyMatch){
25224         var fn = this.createFilterFn(property, value, anyMatch);
25225         return fn ? this.filterBy(fn) : this.clearFilter();
25226     },
25227
25228     /**
25229      * Filter by a function. The specified function will be called with each
25230      * record in this data source. If the function returns true the record is included,
25231      * otherwise it is filtered.
25232      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25233      * @param {Object} scope (optional) The scope of the function (defaults to this)
25234      */
25235     filterBy : function(fn, scope){
25236         this.snapshot = this.snapshot || this.data;
25237         this.data = this.queryBy(fn, scope||this);
25238         this.fireEvent("datachanged", this);
25239     },
25240
25241     /**
25242      * Query the records by a specified property.
25243      * @param {String} field A field on your records
25244      * @param {String/RegExp} value Either a string that the field
25245      * should start with or a RegExp to test against the field
25246      * @param {Boolean} anyMatch True to match any part not just the beginning
25247      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25248      */
25249     query : function(property, value, anyMatch){
25250         var fn = this.createFilterFn(property, value, anyMatch);
25251         return fn ? this.queryBy(fn) : this.data.clone();
25252     },
25253
25254     /**
25255      * Query by a function. The specified function will be called with each
25256      * record in this data source. If the function returns true the record is included
25257      * in the results.
25258      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25259      * @param {Object} scope (optional) The scope of the function (defaults to this)
25260       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25261      **/
25262     queryBy : function(fn, scope){
25263         var data = this.snapshot || this.data;
25264         return data.filterBy(fn, scope||this);
25265     },
25266
25267     /**
25268      * Collects unique values for a particular dataIndex from this store.
25269      * @param {String} dataIndex The property to collect
25270      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25271      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25272      * @return {Array} An array of the unique values
25273      **/
25274     collect : function(dataIndex, allowNull, bypassFilter){
25275         var d = (bypassFilter === true && this.snapshot) ?
25276                 this.snapshot.items : this.data.items;
25277         var v, sv, r = [], l = {};
25278         for(var i = 0, len = d.length; i < len; i++){
25279             v = d[i].data[dataIndex];
25280             sv = String(v);
25281             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25282                 l[sv] = true;
25283                 r[r.length] = v;
25284             }
25285         }
25286         return r;
25287     },
25288
25289     /**
25290      * Revert to a view of the Record cache with no filtering applied.
25291      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25292      */
25293     clearFilter : function(suppressEvent){
25294         if(this.snapshot && this.snapshot != this.data){
25295             this.data = this.snapshot;
25296             delete this.snapshot;
25297             if(suppressEvent !== true){
25298                 this.fireEvent("datachanged", this);
25299             }
25300         }
25301     },
25302
25303     // private
25304     afterEdit : function(record){
25305         if(this.modified.indexOf(record) == -1){
25306             this.modified.push(record);
25307         }
25308         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25309     },
25310     
25311     // private
25312     afterReject : function(record){
25313         this.modified.remove(record);
25314         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25315     },
25316
25317     // private
25318     afterCommit : function(record){
25319         this.modified.remove(record);
25320         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25321     },
25322
25323     /**
25324      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25325      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25326      */
25327     commitChanges : function(){
25328         var m = this.modified.slice(0);
25329         this.modified = [];
25330         for(var i = 0, len = m.length; i < len; i++){
25331             m[i].commit();
25332         }
25333     },
25334
25335     /**
25336      * Cancel outstanding changes on all changed records.
25337      */
25338     rejectChanges : function(){
25339         var m = this.modified.slice(0);
25340         this.modified = [];
25341         for(var i = 0, len = m.length; i < len; i++){
25342             m[i].reject();
25343         }
25344     },
25345
25346     onMetaChange : function(meta, rtype, o){
25347         this.recordType = rtype;
25348         this.fields = rtype.prototype.fields;
25349         delete this.snapshot;
25350         this.sortInfo = meta.sortInfo || this.sortInfo;
25351         this.modified = [];
25352         this.fireEvent('metachange', this, this.reader.meta);
25353     },
25354     
25355     moveIndex : function(data, type)
25356     {
25357         var index = this.indexOf(data);
25358         
25359         var newIndex = index + type;
25360         
25361         this.remove(data);
25362         
25363         this.insert(newIndex, data);
25364         
25365     }
25366 });/*
25367  * Based on:
25368  * Ext JS Library 1.1.1
25369  * Copyright(c) 2006-2007, Ext JS, LLC.
25370  *
25371  * Originally Released Under LGPL - original licence link has changed is not relivant.
25372  *
25373  * Fork - LGPL
25374  * <script type="text/javascript">
25375  */
25376
25377 /**
25378  * @class Roo.data.SimpleStore
25379  * @extends Roo.data.Store
25380  * Small helper class to make creating Stores from Array data easier.
25381  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25382  * @cfg {Array} fields An array of field definition objects, or field name strings.
25383  * @cfg {Object} an existing reader (eg. copied from another store)
25384  * @cfg {Array} data The multi-dimensional array of data
25385  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25386  * @cfg {Roo.data.Reader} reader  [not-required] 
25387  * @constructor
25388  * @param {Object} config
25389  */
25390 Roo.data.SimpleStore = function(config)
25391 {
25392     Roo.data.SimpleStore.superclass.constructor.call(this, {
25393         isLocal : true,
25394         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25395                 id: config.id
25396             },
25397             Roo.data.Record.create(config.fields)
25398         ),
25399         proxy : new Roo.data.MemoryProxy(config.data)
25400     });
25401     this.load();
25402 };
25403 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25404  * Based on:
25405  * Ext JS Library 1.1.1
25406  * Copyright(c) 2006-2007, Ext JS, LLC.
25407  *
25408  * Originally Released Under LGPL - original licence link has changed is not relivant.
25409  *
25410  * Fork - LGPL
25411  * <script type="text/javascript">
25412  */
25413
25414 /**
25415 /**
25416  * @extends Roo.data.Store
25417  * @class Roo.data.JsonStore
25418  * Small helper class to make creating Stores for JSON data easier. <br/>
25419 <pre><code>
25420 var store = new Roo.data.JsonStore({
25421     url: 'get-images.php',
25422     root: 'images',
25423     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25424 });
25425 </code></pre>
25426  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25427  * JsonReader and HttpProxy (unless inline data is provided).</b>
25428  * @cfg {Array} fields An array of field definition objects, or field name strings.
25429  * @constructor
25430  * @param {Object} config
25431  */
25432 Roo.data.JsonStore = function(c){
25433     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25434         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25435         reader: new Roo.data.JsonReader(c, c.fields)
25436     }));
25437 };
25438 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25439  * Based on:
25440  * Ext JS Library 1.1.1
25441  * Copyright(c) 2006-2007, Ext JS, LLC.
25442  *
25443  * Originally Released Under LGPL - original licence link has changed is not relivant.
25444  *
25445  * Fork - LGPL
25446  * <script type="text/javascript">
25447  */
25448
25449  
25450 Roo.data.Field = function(config){
25451     if(typeof config == "string"){
25452         config = {name: config};
25453     }
25454     Roo.apply(this, config);
25455     
25456     if(!this.type){
25457         this.type = "auto";
25458     }
25459     
25460     var st = Roo.data.SortTypes;
25461     // named sortTypes are supported, here we look them up
25462     if(typeof this.sortType == "string"){
25463         this.sortType = st[this.sortType];
25464     }
25465     
25466     // set default sortType for strings and dates
25467     if(!this.sortType){
25468         switch(this.type){
25469             case "string":
25470                 this.sortType = st.asUCString;
25471                 break;
25472             case "date":
25473                 this.sortType = st.asDate;
25474                 break;
25475             default:
25476                 this.sortType = st.none;
25477         }
25478     }
25479
25480     // define once
25481     var stripRe = /[\$,%]/g;
25482
25483     // prebuilt conversion function for this field, instead of
25484     // switching every time we're reading a value
25485     if(!this.convert){
25486         var cv, dateFormat = this.dateFormat;
25487         switch(this.type){
25488             case "":
25489             case "auto":
25490             case undefined:
25491                 cv = function(v){ return v; };
25492                 break;
25493             case "string":
25494                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25495                 break;
25496             case "int":
25497                 cv = function(v){
25498                     return v !== undefined && v !== null && v !== '' ?
25499                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25500                     };
25501                 break;
25502             case "float":
25503                 cv = function(v){
25504                     return v !== undefined && v !== null && v !== '' ?
25505                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25506                     };
25507                 break;
25508             case "bool":
25509             case "boolean":
25510                 cv = function(v){ return v === true || v === "true" || v == 1; };
25511                 break;
25512             case "date":
25513                 cv = function(v){
25514                     if(!v){
25515                         return '';
25516                     }
25517                     if(v instanceof Date){
25518                         return v;
25519                     }
25520                     if(dateFormat){
25521                         if(dateFormat == "timestamp"){
25522                             return new Date(v*1000);
25523                         }
25524                         return Date.parseDate(v, dateFormat);
25525                     }
25526                     var parsed = Date.parse(v);
25527                     return parsed ? new Date(parsed) : null;
25528                 };
25529              break;
25530             
25531         }
25532         this.convert = cv;
25533     }
25534 };
25535
25536 Roo.data.Field.prototype = {
25537     dateFormat: null,
25538     defaultValue: "",
25539     mapping: null,
25540     sortType : null,
25541     sortDir : "ASC"
25542 };/*
25543  * Based on:
25544  * Ext JS Library 1.1.1
25545  * Copyright(c) 2006-2007, Ext JS, LLC.
25546  *
25547  * Originally Released Under LGPL - original licence link has changed is not relivant.
25548  *
25549  * Fork - LGPL
25550  * <script type="text/javascript">
25551  */
25552  
25553 // Base class for reading structured data from a data source.  This class is intended to be
25554 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25555
25556 /**
25557  * @class Roo.data.DataReader
25558  * @abstract
25559  * Base class for reading structured data from a data source.  This class is intended to be
25560  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25561  */
25562
25563 Roo.data.DataReader = function(meta, recordType){
25564     
25565     this.meta = meta;
25566     
25567     this.recordType = recordType instanceof Array ? 
25568         Roo.data.Record.create(recordType) : recordType;
25569 };
25570
25571 Roo.data.DataReader.prototype = {
25572     
25573     
25574     readerType : 'Data',
25575      /**
25576      * Create an empty record
25577      * @param {Object} data (optional) - overlay some values
25578      * @return {Roo.data.Record} record created.
25579      */
25580     newRow :  function(d) {
25581         var da =  {};
25582         this.recordType.prototype.fields.each(function(c) {
25583             switch( c.type) {
25584                 case 'int' : da[c.name] = 0; break;
25585                 case 'date' : da[c.name] = new Date(); break;
25586                 case 'float' : da[c.name] = 0.0; break;
25587                 case 'boolean' : da[c.name] = false; break;
25588                 default : da[c.name] = ""; break;
25589             }
25590             
25591         });
25592         return new this.recordType(Roo.apply(da, d));
25593     }
25594     
25595     
25596 };/*
25597  * Based on:
25598  * Ext JS Library 1.1.1
25599  * Copyright(c) 2006-2007, Ext JS, LLC.
25600  *
25601  * Originally Released Under LGPL - original licence link has changed is not relivant.
25602  *
25603  * Fork - LGPL
25604  * <script type="text/javascript">
25605  */
25606
25607 /**
25608  * @class Roo.data.DataProxy
25609  * @extends Roo.util.Observable
25610  * @abstract
25611  * This class is an abstract base class for implementations which provide retrieval of
25612  * unformatted data objects.<br>
25613  * <p>
25614  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25615  * (of the appropriate type which knows how to parse the data object) to provide a block of
25616  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25617  * <p>
25618  * Custom implementations must implement the load method as described in
25619  * {@link Roo.data.HttpProxy#load}.
25620  */
25621 Roo.data.DataProxy = function(){
25622     this.addEvents({
25623         /**
25624          * @event beforeload
25625          * Fires before a network request is made to retrieve a data object.
25626          * @param {Object} This DataProxy object.
25627          * @param {Object} params The params parameter to the load function.
25628          */
25629         beforeload : true,
25630         /**
25631          * @event load
25632          * Fires before the load method's callback is called.
25633          * @param {Object} This DataProxy object.
25634          * @param {Object} o The data object.
25635          * @param {Object} arg The callback argument object passed to the load function.
25636          */
25637         load : true,
25638         /**
25639          * @event loadexception
25640          * Fires if an Exception occurs during data retrieval.
25641          * @param {Object} This DataProxy object.
25642          * @param {Object} o The data object.
25643          * @param {Object} arg The callback argument object passed to the load function.
25644          * @param {Object} e The Exception.
25645          */
25646         loadexception : true
25647     });
25648     Roo.data.DataProxy.superclass.constructor.call(this);
25649 };
25650
25651 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25652
25653     /**
25654      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25655      */
25656 /*
25657  * Based on:
25658  * Ext JS Library 1.1.1
25659  * Copyright(c) 2006-2007, Ext JS, LLC.
25660  *
25661  * Originally Released Under LGPL - original licence link has changed is not relivant.
25662  *
25663  * Fork - LGPL
25664  * <script type="text/javascript">
25665  */
25666 /**
25667  * @class Roo.data.MemoryProxy
25668  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25669  * to the Reader when its load method is called.
25670  * @constructor
25671  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25672  */
25673 Roo.data.MemoryProxy = function(data){
25674     if (data.data) {
25675         data = data.data;
25676     }
25677     Roo.data.MemoryProxy.superclass.constructor.call(this);
25678     this.data = data;
25679 };
25680
25681 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25682     
25683     /**
25684      * Load data from the requested source (in this case an in-memory
25685      * data object passed to the constructor), read the data object into
25686      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25687      * process that block using the passed callback.
25688      * @param {Object} params This parameter is not used by the MemoryProxy class.
25689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25690      * object into a block of Roo.data.Records.
25691      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25692      * The function must be passed <ul>
25693      * <li>The Record block object</li>
25694      * <li>The "arg" argument from the load function</li>
25695      * <li>A boolean success indicator</li>
25696      * </ul>
25697      * @param {Object} scope The scope in which to call the callback
25698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25699      */
25700     load : function(params, reader, callback, scope, arg){
25701         params = params || {};
25702         var result;
25703         try {
25704             result = reader.readRecords(params.data ? params.data :this.data);
25705         }catch(e){
25706             this.fireEvent("loadexception", this, arg, null, e);
25707             callback.call(scope, null, arg, false);
25708             return;
25709         }
25710         callback.call(scope, result, arg, true);
25711     },
25712     
25713     // private
25714     update : function(params, records){
25715         
25716     }
25717 });/*
25718  * Based on:
25719  * Ext JS Library 1.1.1
25720  * Copyright(c) 2006-2007, Ext JS, LLC.
25721  *
25722  * Originally Released Under LGPL - original licence link has changed is not relivant.
25723  *
25724  * Fork - LGPL
25725  * <script type="text/javascript">
25726  */
25727 /**
25728  * @class Roo.data.HttpProxy
25729  * @extends Roo.data.DataProxy
25730  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25731  * configured to reference a certain URL.<br><br>
25732  * <p>
25733  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25734  * from which the running page was served.<br><br>
25735  * <p>
25736  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25737  * <p>
25738  * Be aware that to enable the browser to parse an XML document, the server must set
25739  * the Content-Type header in the HTTP response to "text/xml".
25740  * @constructor
25741  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25742  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25743  * will be used to make the request.
25744  */
25745 Roo.data.HttpProxy = function(conn){
25746     Roo.data.HttpProxy.superclass.constructor.call(this);
25747     // is conn a conn config or a real conn?
25748     this.conn = conn;
25749     this.useAjax = !conn || !conn.events;
25750   
25751 };
25752
25753 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25754     // thse are take from connection...
25755     
25756     /**
25757      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25758      */
25759     /**
25760      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25761      * extra parameters to each request made by this object. (defaults to undefined)
25762      */
25763     /**
25764      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25765      *  to each request made by this object. (defaults to undefined)
25766      */
25767     /**
25768      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
25769      */
25770     /**
25771      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25772      */
25773      /**
25774      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25775      * @type Boolean
25776      */
25777   
25778
25779     /**
25780      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25781      * @type Boolean
25782      */
25783     /**
25784      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25785      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25786      * a finer-grained basis than the DataProxy events.
25787      */
25788     getConnection : function(){
25789         return this.useAjax ? Roo.Ajax : this.conn;
25790     },
25791
25792     /**
25793      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25794      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25795      * process that block using the passed callback.
25796      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25797      * for the request to the remote server.
25798      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25799      * object into a block of Roo.data.Records.
25800      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25801      * The function must be passed <ul>
25802      * <li>The Record block object</li>
25803      * <li>The "arg" argument from the load function</li>
25804      * <li>A boolean success indicator</li>
25805      * </ul>
25806      * @param {Object} scope The scope in which to call the callback
25807      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25808      */
25809     load : function(params, reader, callback, scope, arg){
25810         if(this.fireEvent("beforeload", this, params) !== false){
25811             var  o = {
25812                 params : params || {},
25813                 request: {
25814                     callback : callback,
25815                     scope : scope,
25816                     arg : arg
25817                 },
25818                 reader: reader,
25819                 callback : this.loadResponse,
25820                 scope: this
25821             };
25822             if(this.useAjax){
25823                 Roo.applyIf(o, this.conn);
25824                 if(this.activeRequest){
25825                     Roo.Ajax.abort(this.activeRequest);
25826                 }
25827                 this.activeRequest = Roo.Ajax.request(o);
25828             }else{
25829                 this.conn.request(o);
25830             }
25831         }else{
25832             callback.call(scope||this, null, arg, false);
25833         }
25834     },
25835
25836     // private
25837     loadResponse : function(o, success, response){
25838         delete this.activeRequest;
25839         if(!success){
25840             this.fireEvent("loadexception", this, o, response);
25841             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25842             return;
25843         }
25844         var result;
25845         try {
25846             result = o.reader.read(response);
25847         }catch(e){
25848             o.success = false;
25849             o.raw = { errorMsg : response.responseText };
25850             this.fireEvent("loadexception", this, o, response, e);
25851             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25852             return;
25853         }
25854         
25855         this.fireEvent("load", this, o, o.request.arg);
25856         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25857     },
25858
25859     // private
25860     update : function(dataSet){
25861
25862     },
25863
25864     // private
25865     updateResponse : function(dataSet){
25866
25867     }
25868 });/*
25869  * Based on:
25870  * Ext JS Library 1.1.1
25871  * Copyright(c) 2006-2007, Ext JS, LLC.
25872  *
25873  * Originally Released Under LGPL - original licence link has changed is not relivant.
25874  *
25875  * Fork - LGPL
25876  * <script type="text/javascript">
25877  */
25878
25879 /**
25880  * @class Roo.data.ScriptTagProxy
25881  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25882  * other than the originating domain of the running page.<br><br>
25883  * <p>
25884  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
25885  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25886  * <p>
25887  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25888  * source code that is used as the source inside a &lt;script> tag.<br><br>
25889  * <p>
25890  * In order for the browser to process the returned data, the server must wrap the data object
25891  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25892  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25893  * depending on whether the callback name was passed:
25894  * <p>
25895  * <pre><code>
25896 boolean scriptTag = false;
25897 String cb = request.getParameter("callback");
25898 if (cb != null) {
25899     scriptTag = true;
25900     response.setContentType("text/javascript");
25901 } else {
25902     response.setContentType("application/x-json");
25903 }
25904 Writer out = response.getWriter();
25905 if (scriptTag) {
25906     out.write(cb + "(");
25907 }
25908 out.print(dataBlock.toJsonString());
25909 if (scriptTag) {
25910     out.write(");");
25911 }
25912 </pre></code>
25913  *
25914  * @constructor
25915  * @param {Object} config A configuration object.
25916  */
25917 Roo.data.ScriptTagProxy = function(config){
25918     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25919     Roo.apply(this, config);
25920     this.head = document.getElementsByTagName("head")[0];
25921 };
25922
25923 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25924
25925 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25926     /**
25927      * @cfg {String} url The URL from which to request the data object.
25928      */
25929     /**
25930      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25931      */
25932     timeout : 30000,
25933     /**
25934      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25935      * the server the name of the callback function set up by the load call to process the returned data object.
25936      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25937      * javascript output which calls this named function passing the data object as its only parameter.
25938      */
25939     callbackParam : "callback",
25940     /**
25941      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25942      * name to the request.
25943      */
25944     nocache : true,
25945
25946     /**
25947      * Load data from the configured URL, read the data object into
25948      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25949      * process that block using the passed callback.
25950      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25951      * for the request to the remote server.
25952      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25953      * object into a block of Roo.data.Records.
25954      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25955      * The function must be passed <ul>
25956      * <li>The Record block object</li>
25957      * <li>The "arg" argument from the load function</li>
25958      * <li>A boolean success indicator</li>
25959      * </ul>
25960      * @param {Object} scope The scope in which to call the callback
25961      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25962      */
25963     load : function(params, reader, callback, scope, arg){
25964         if(this.fireEvent("beforeload", this, params) !== false){
25965
25966             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25967
25968             var url = this.url;
25969             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25970             if(this.nocache){
25971                 url += "&_dc=" + (new Date().getTime());
25972             }
25973             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25974             var trans = {
25975                 id : transId,
25976                 cb : "stcCallback"+transId,
25977                 scriptId : "stcScript"+transId,
25978                 params : params,
25979                 arg : arg,
25980                 url : url,
25981                 callback : callback,
25982                 scope : scope,
25983                 reader : reader
25984             };
25985             var conn = this;
25986
25987             window[trans.cb] = function(o){
25988                 conn.handleResponse(o, trans);
25989             };
25990
25991             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25992
25993             if(this.autoAbort !== false){
25994                 this.abort();
25995             }
25996
25997             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25998
25999             var script = document.createElement("script");
26000             script.setAttribute("src", url);
26001             script.setAttribute("type", "text/javascript");
26002             script.setAttribute("id", trans.scriptId);
26003             this.head.appendChild(script);
26004
26005             this.trans = trans;
26006         }else{
26007             callback.call(scope||this, null, arg, false);
26008         }
26009     },
26010
26011     // private
26012     isLoading : function(){
26013         return this.trans ? true : false;
26014     },
26015
26016     /**
26017      * Abort the current server request.
26018      */
26019     abort : function(){
26020         if(this.isLoading()){
26021             this.destroyTrans(this.trans);
26022         }
26023     },
26024
26025     // private
26026     destroyTrans : function(trans, isLoaded){
26027         this.head.removeChild(document.getElementById(trans.scriptId));
26028         clearTimeout(trans.timeoutId);
26029         if(isLoaded){
26030             window[trans.cb] = undefined;
26031             try{
26032                 delete window[trans.cb];
26033             }catch(e){}
26034         }else{
26035             // if hasn't been loaded, wait for load to remove it to prevent script error
26036             window[trans.cb] = function(){
26037                 window[trans.cb] = undefined;
26038                 try{
26039                     delete window[trans.cb];
26040                 }catch(e){}
26041             };
26042         }
26043     },
26044
26045     // private
26046     handleResponse : function(o, trans){
26047         this.trans = false;
26048         this.destroyTrans(trans, true);
26049         var result;
26050         try {
26051             result = trans.reader.readRecords(o);
26052         }catch(e){
26053             this.fireEvent("loadexception", this, o, trans.arg, e);
26054             trans.callback.call(trans.scope||window, null, trans.arg, false);
26055             return;
26056         }
26057         this.fireEvent("load", this, o, trans.arg);
26058         trans.callback.call(trans.scope||window, result, trans.arg, true);
26059     },
26060
26061     // private
26062     handleFailure : function(trans){
26063         this.trans = false;
26064         this.destroyTrans(trans, false);
26065         this.fireEvent("loadexception", this, null, trans.arg);
26066         trans.callback.call(trans.scope||window, null, trans.arg, false);
26067     }
26068 });/*
26069  * Based on:
26070  * Ext JS Library 1.1.1
26071  * Copyright(c) 2006-2007, Ext JS, LLC.
26072  *
26073  * Originally Released Under LGPL - original licence link has changed is not relivant.
26074  *
26075  * Fork - LGPL
26076  * <script type="text/javascript">
26077  */
26078
26079 /**
26080  * @class Roo.data.JsonReader
26081  * @extends Roo.data.DataReader
26082  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26083  * based on mappings in a provided Roo.data.Record constructor.
26084  * 
26085  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26086  * in the reply previously. 
26087  * 
26088  * <p>
26089  * Example code:
26090  * <pre><code>
26091 var RecordDef = Roo.data.Record.create([
26092     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26093     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26094 ]);
26095 var myReader = new Roo.data.JsonReader({
26096     totalProperty: "results",    // The property which contains the total dataset size (optional)
26097     root: "rows",                // The property which contains an Array of row objects
26098     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26099 }, RecordDef);
26100 </code></pre>
26101  * <p>
26102  * This would consume a JSON file like this:
26103  * <pre><code>
26104 { 'results': 2, 'rows': [
26105     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26106     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26107 }
26108 </code></pre>
26109  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26110  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26111  * paged from the remote server.
26112  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26113  * @cfg {String} root name of the property which contains the Array of row objects.
26114  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26115  * @cfg {Array} fields Array of field definition objects
26116  * @constructor
26117  * Create a new JsonReader
26118  * @param {Object} meta Metadata configuration options
26119  * @param {Object} recordType Either an Array of field definition objects,
26120  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26121  */
26122 Roo.data.JsonReader = function(meta, recordType){
26123     
26124     meta = meta || {};
26125     // set some defaults:
26126     Roo.applyIf(meta, {
26127         totalProperty: 'total',
26128         successProperty : 'success',
26129         root : 'data',
26130         id : 'id'
26131     });
26132     
26133     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26134 };
26135 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26136     
26137     readerType : 'Json',
26138     
26139     /**
26140      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26141      * Used by Store query builder to append _requestMeta to params.
26142      * 
26143      */
26144     metaFromRemote : false,
26145     /**
26146      * This method is only used by a DataProxy which has retrieved data from a remote server.
26147      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26148      * @return {Object} data A data block which is used by an Roo.data.Store object as
26149      * a cache of Roo.data.Records.
26150      */
26151     read : function(response){
26152         var json = response.responseText;
26153        
26154         var o = /* eval:var:o */ eval("("+json+")");
26155         if(!o) {
26156             throw {message: "JsonReader.read: Json object not found"};
26157         }
26158         
26159         if(o.metaData){
26160             
26161             delete this.ef;
26162             this.metaFromRemote = true;
26163             this.meta = o.metaData;
26164             this.recordType = Roo.data.Record.create(o.metaData.fields);
26165             this.onMetaChange(this.meta, this.recordType, o);
26166         }
26167         return this.readRecords(o);
26168     },
26169
26170     // private function a store will implement
26171     onMetaChange : function(meta, recordType, o){
26172
26173     },
26174
26175     /**
26176          * @ignore
26177          */
26178     simpleAccess: function(obj, subsc) {
26179         return obj[subsc];
26180     },
26181
26182         /**
26183          * @ignore
26184          */
26185     getJsonAccessor: function(){
26186         var re = /[\[\.]/;
26187         return function(expr) {
26188             try {
26189                 return(re.test(expr))
26190                     ? new Function("obj", "return obj." + expr)
26191                     : function(obj){
26192                         return obj[expr];
26193                     };
26194             } catch(e){}
26195             return Roo.emptyFn;
26196         };
26197     }(),
26198
26199     /**
26200      * Create a data block containing Roo.data.Records from an XML document.
26201      * @param {Object} o An object which contains an Array of row objects in the property specified
26202      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26203      * which contains the total size of the dataset.
26204      * @return {Object} data A data block which is used by an Roo.data.Store object as
26205      * a cache of Roo.data.Records.
26206      */
26207     readRecords : function(o){
26208         /**
26209          * After any data loads, the raw JSON data is available for further custom processing.
26210          * @type Object
26211          */
26212         this.o = o;
26213         var s = this.meta, Record = this.recordType,
26214             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26215
26216 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26217         if (!this.ef) {
26218             if(s.totalProperty) {
26219                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26220                 }
26221                 if(s.successProperty) {
26222                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26223                 }
26224                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26225                 if (s.id) {
26226                         var g = this.getJsonAccessor(s.id);
26227                         this.getId = function(rec) {
26228                                 var r = g(rec);  
26229                                 return (r === undefined || r === "") ? null : r;
26230                         };
26231                 } else {
26232                         this.getId = function(){return null;};
26233                 }
26234             this.ef = [];
26235             for(var jj = 0; jj < fl; jj++){
26236                 f = fi[jj];
26237                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26238                 this.ef[jj] = this.getJsonAccessor(map);
26239             }
26240         }
26241
26242         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26243         if(s.totalProperty){
26244             var vt = parseInt(this.getTotal(o), 10);
26245             if(!isNaN(vt)){
26246                 totalRecords = vt;
26247             }
26248         }
26249         if(s.successProperty){
26250             var vs = this.getSuccess(o);
26251             if(vs === false || vs === 'false'){
26252                 success = false;
26253             }
26254         }
26255         var records = [];
26256         for(var i = 0; i < c; i++){
26257             var n = root[i];
26258             var values = {};
26259             var id = this.getId(n);
26260             for(var j = 0; j < fl; j++){
26261                 f = fi[j];
26262                                 var v = this.ef[j](n);
26263                                 if (!f.convert) {
26264                                         Roo.log('missing convert for ' + f.name);
26265                                         Roo.log(f);
26266                                         continue;
26267                                 }
26268                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26269             }
26270                         if (!Record) {
26271                                 return {
26272                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26273                                         success : false,
26274                                         records : [],
26275                                         totalRecords : 0
26276                                 };
26277                         }
26278             var record = new Record(values, id);
26279             record.json = n;
26280             records[i] = record;
26281         }
26282         return {
26283             raw : o,
26284             success : success,
26285             records : records,
26286             totalRecords : totalRecords
26287         };
26288     },
26289     // used when loading children.. @see loadDataFromChildren
26290     toLoadData: function(rec)
26291     {
26292         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26293         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26294         return { data : data, total : data.length };
26295         
26296     }
26297 });/*
26298  * Based on:
26299  * Ext JS Library 1.1.1
26300  * Copyright(c) 2006-2007, Ext JS, LLC.
26301  *
26302  * Originally Released Under LGPL - original licence link has changed is not relivant.
26303  *
26304  * Fork - LGPL
26305  * <script type="text/javascript">
26306  */
26307
26308 /**
26309  * @class Roo.data.XmlReader
26310  * @extends Roo.data.DataReader
26311  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26312  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26313  * <p>
26314  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26315  * header in the HTTP response must be set to "text/xml".</em>
26316  * <p>
26317  * Example code:
26318  * <pre><code>
26319 var RecordDef = Roo.data.Record.create([
26320    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26321    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26322 ]);
26323 var myReader = new Roo.data.XmlReader({
26324    totalRecords: "results", // The element which contains the total dataset size (optional)
26325    record: "row",           // The repeated element which contains row information
26326    id: "id"                 // The element within the row that provides an ID for the record (optional)
26327 }, RecordDef);
26328 </code></pre>
26329  * <p>
26330  * This would consume an XML file like this:
26331  * <pre><code>
26332 &lt;?xml?>
26333 &lt;dataset>
26334  &lt;results>2&lt;/results>
26335  &lt;row>
26336    &lt;id>1&lt;/id>
26337    &lt;name>Bill&lt;/name>
26338    &lt;occupation>Gardener&lt;/occupation>
26339  &lt;/row>
26340  &lt;row>
26341    &lt;id>2&lt;/id>
26342    &lt;name>Ben&lt;/name>
26343    &lt;occupation>Horticulturalist&lt;/occupation>
26344  &lt;/row>
26345 &lt;/dataset>
26346 </code></pre>
26347  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26348  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26349  * paged from the remote server.
26350  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26351  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26352  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26353  * a record identifier value.
26354  * @constructor
26355  * Create a new XmlReader
26356  * @param {Object} meta Metadata configuration options
26357  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26358  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26359  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26360  */
26361 Roo.data.XmlReader = function(meta, recordType){
26362     meta = meta || {};
26363     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26364 };
26365 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26366     
26367     readerType : 'Xml',
26368     
26369     /**
26370      * This method is only used by a DataProxy which has retrieved data from a remote server.
26371          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26372          * to contain a method called 'responseXML' that returns an XML document object.
26373      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26374      * a cache of Roo.data.Records.
26375      */
26376     read : function(response){
26377         var doc = response.responseXML;
26378         if(!doc) {
26379             throw {message: "XmlReader.read: XML Document not available"};
26380         }
26381         return this.readRecords(doc);
26382     },
26383
26384     /**
26385      * Create a data block containing Roo.data.Records from an XML document.
26386          * @param {Object} doc A parsed XML document.
26387      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26388      * a cache of Roo.data.Records.
26389      */
26390     readRecords : function(doc){
26391         /**
26392          * After any data loads/reads, the raw XML Document is available for further custom processing.
26393          * @type XMLDocument
26394          */
26395         this.xmlData = doc;
26396         var root = doc.documentElement || doc;
26397         var q = Roo.DomQuery;
26398         var recordType = this.recordType, fields = recordType.prototype.fields;
26399         var sid = this.meta.id;
26400         var totalRecords = 0, success = true;
26401         if(this.meta.totalRecords){
26402             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26403         }
26404         
26405         if(this.meta.success){
26406             var sv = q.selectValue(this.meta.success, root, true);
26407             success = sv !== false && sv !== 'false';
26408         }
26409         var records = [];
26410         var ns = q.select(this.meta.record, root);
26411         for(var i = 0, len = ns.length; i < len; i++) {
26412                 var n = ns[i];
26413                 var values = {};
26414                 var id = sid ? q.selectValue(sid, n) : undefined;
26415                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26416                     var f = fields.items[j];
26417                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26418                     v = f.convert(v);
26419                     values[f.name] = v;
26420                 }
26421                 var record = new recordType(values, id);
26422                 record.node = n;
26423                 records[records.length] = record;
26424             }
26425
26426             return {
26427                 success : success,
26428                 records : records,
26429                 totalRecords : totalRecords || records.length
26430             };
26431     }
26432 });/*
26433  * Based on:
26434  * Ext JS Library 1.1.1
26435  * Copyright(c) 2006-2007, Ext JS, LLC.
26436  *
26437  * Originally Released Under LGPL - original licence link has changed is not relivant.
26438  *
26439  * Fork - LGPL
26440  * <script type="text/javascript">
26441  */
26442
26443 /**
26444  * @class Roo.data.ArrayReader
26445  * @extends Roo.data.DataReader
26446  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26447  * Each element of that Array represents a row of data fields. The
26448  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26449  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26450  * <p>
26451  * Example code:.
26452  * <pre><code>
26453 var RecordDef = Roo.data.Record.create([
26454     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26455     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26456 ]);
26457 var myReader = new Roo.data.ArrayReader({
26458     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26459 }, RecordDef);
26460 </code></pre>
26461  * <p>
26462  * This would consume an Array like this:
26463  * <pre><code>
26464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26465   </code></pre>
26466  
26467  * @constructor
26468  * Create a new JsonReader
26469  * @param {Object} meta Metadata configuration options.
26470  * @param {Object|Array} recordType Either an Array of field definition objects
26471  * 
26472  * @cfg {Array} fields Array of field definition objects
26473  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26474  * as specified to {@link Roo.data.Record#create},
26475  * or an {@link Roo.data.Record} object
26476  *
26477  * 
26478  * created using {@link Roo.data.Record#create}.
26479  */
26480 Roo.data.ArrayReader = function(meta, recordType)
26481 {    
26482     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26483 };
26484
26485 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26486     
26487       /**
26488      * Create a data block containing Roo.data.Records from an XML document.
26489      * @param {Object} o An Array of row objects which represents the dataset.
26490      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26491      * a cache of Roo.data.Records.
26492      */
26493     readRecords : function(o)
26494     {
26495         var sid = this.meta ? this.meta.id : null;
26496         var recordType = this.recordType, fields = recordType.prototype.fields;
26497         var records = [];
26498         var root = o;
26499         for(var i = 0; i < root.length; i++){
26500             var n = root[i];
26501             var values = {};
26502             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26503             for(var j = 0, jlen = fields.length; j < jlen; j++){
26504                 var f = fields.items[j];
26505                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26506                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26507                 v = f.convert(v);
26508                 values[f.name] = v;
26509             }
26510             var record = new recordType(values, id);
26511             record.json = n;
26512             records[records.length] = record;
26513         }
26514         return {
26515             records : records,
26516             totalRecords : records.length
26517         };
26518     },
26519     // used when loading children.. @see loadDataFromChildren
26520     toLoadData: function(rec)
26521     {
26522         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26523         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26524         
26525     }
26526     
26527     
26528 });/*
26529  * Based on:
26530  * Ext JS Library 1.1.1
26531  * Copyright(c) 2006-2007, Ext JS, LLC.
26532  *
26533  * Originally Released Under LGPL - original licence link has changed is not relivant.
26534  *
26535  * Fork - LGPL
26536  * <script type="text/javascript">
26537  */
26538
26539
26540 /**
26541  * @class Roo.data.Tree
26542  * @extends Roo.util.Observable
26543  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26544  * in the tree have most standard DOM functionality.
26545  * @constructor
26546  * @param {Node} root (optional) The root node
26547  */
26548 Roo.data.Tree = function(root){
26549    this.nodeHash = {};
26550    /**
26551     * The root node for this tree
26552     * @type Node
26553     */
26554    this.root = null;
26555    if(root){
26556        this.setRootNode(root);
26557    }
26558    this.addEvents({
26559        /**
26560         * @event append
26561         * Fires when a new child node is appended to a node in this tree.
26562         * @param {Tree} tree The owner tree
26563         * @param {Node} parent The parent node
26564         * @param {Node} node The newly appended node
26565         * @param {Number} index The index of the newly appended node
26566         */
26567        "append" : true,
26568        /**
26569         * @event remove
26570         * Fires when a child node is removed from a node in this tree.
26571         * @param {Tree} tree The owner tree
26572         * @param {Node} parent The parent node
26573         * @param {Node} node The child node removed
26574         */
26575        "remove" : true,
26576        /**
26577         * @event move
26578         * Fires when a node is moved to a new location in the tree
26579         * @param {Tree} tree The owner tree
26580         * @param {Node} node The node moved
26581         * @param {Node} oldParent The old parent of this node
26582         * @param {Node} newParent The new parent of this node
26583         * @param {Number} index The index it was moved to
26584         */
26585        "move" : true,
26586        /**
26587         * @event insert
26588         * Fires when a new child node is inserted in a node in this tree.
26589         * @param {Tree} tree The owner tree
26590         * @param {Node} parent The parent node
26591         * @param {Node} node The child node inserted
26592         * @param {Node} refNode The child node the node was inserted before
26593         */
26594        "insert" : true,
26595        /**
26596         * @event beforeappend
26597         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26598         * @param {Tree} tree The owner tree
26599         * @param {Node} parent The parent node
26600         * @param {Node} node The child node to be appended
26601         */
26602        "beforeappend" : true,
26603        /**
26604         * @event beforeremove
26605         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26606         * @param {Tree} tree The owner tree
26607         * @param {Node} parent The parent node
26608         * @param {Node} node The child node to be removed
26609         */
26610        "beforeremove" : true,
26611        /**
26612         * @event beforemove
26613         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26614         * @param {Tree} tree The owner tree
26615         * @param {Node} node The node being moved
26616         * @param {Node} oldParent The parent of the node
26617         * @param {Node} newParent The new parent the node is moving to
26618         * @param {Number} index The index it is being moved to
26619         */
26620        "beforemove" : true,
26621        /**
26622         * @event beforeinsert
26623         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26624         * @param {Tree} tree The owner tree
26625         * @param {Node} parent The parent node
26626         * @param {Node} node The child node to be inserted
26627         * @param {Node} refNode The child node the node is being inserted before
26628         */
26629        "beforeinsert" : true
26630    });
26631
26632     Roo.data.Tree.superclass.constructor.call(this);
26633 };
26634
26635 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26636     pathSeparator: "/",
26637
26638     proxyNodeEvent : function(){
26639         return this.fireEvent.apply(this, arguments);
26640     },
26641
26642     /**
26643      * Returns the root node for this tree.
26644      * @return {Node}
26645      */
26646     getRootNode : function(){
26647         return this.root;
26648     },
26649
26650     /**
26651      * Sets the root node for this tree.
26652      * @param {Node} node
26653      * @return {Node}
26654      */
26655     setRootNode : function(node){
26656         this.root = node;
26657         node.ownerTree = this;
26658         node.isRoot = true;
26659         this.registerNode(node);
26660         return node;
26661     },
26662
26663     /**
26664      * Gets a node in this tree by its id.
26665      * @param {String} id
26666      * @return {Node}
26667      */
26668     getNodeById : function(id){
26669         return this.nodeHash[id];
26670     },
26671
26672     registerNode : function(node){
26673         this.nodeHash[node.id] = node;
26674     },
26675
26676     unregisterNode : function(node){
26677         delete this.nodeHash[node.id];
26678     },
26679
26680     toString : function(){
26681         return "[Tree"+(this.id?" "+this.id:"")+"]";
26682     }
26683 });
26684
26685 /**
26686  * @class Roo.data.Node
26687  * @extends Roo.util.Observable
26688  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26689  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26690  * @constructor
26691  * @param {Object} attributes The attributes/config for the node
26692  */
26693 Roo.data.Node = function(attributes){
26694     /**
26695      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26696      * @type {Object}
26697      */
26698     this.attributes = attributes || {};
26699     this.leaf = this.attributes.leaf;
26700     /**
26701      * The node id. @type String
26702      */
26703     this.id = this.attributes.id;
26704     if(!this.id){
26705         this.id = Roo.id(null, "ynode-");
26706         this.attributes.id = this.id;
26707     }
26708      
26709     
26710     /**
26711      * All child nodes of this node. @type Array
26712      */
26713     this.childNodes = [];
26714     if(!this.childNodes.indexOf){ // indexOf is a must
26715         this.childNodes.indexOf = function(o){
26716             for(var i = 0, len = this.length; i < len; i++){
26717                 if(this[i] == o) {
26718                     return i;
26719                 }
26720             }
26721             return -1;
26722         };
26723     }
26724     /**
26725      * The parent node for this node. @type Node
26726      */
26727     this.parentNode = null;
26728     /**
26729      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26730      */
26731     this.firstChild = null;
26732     /**
26733      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26734      */
26735     this.lastChild = null;
26736     /**
26737      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26738      */
26739     this.previousSibling = null;
26740     /**
26741      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26742      */
26743     this.nextSibling = null;
26744
26745     this.addEvents({
26746        /**
26747         * @event append
26748         * Fires when a new child node is appended
26749         * @param {Tree} tree The owner tree
26750         * @param {Node} this This node
26751         * @param {Node} node The newly appended node
26752         * @param {Number} index The index of the newly appended node
26753         */
26754        "append" : true,
26755        /**
26756         * @event remove
26757         * Fires when a child node is removed
26758         * @param {Tree} tree The owner tree
26759         * @param {Node} this This node
26760         * @param {Node} node The removed node
26761         */
26762        "remove" : true,
26763        /**
26764         * @event move
26765         * Fires when this node is moved to a new location in the tree
26766         * @param {Tree} tree The owner tree
26767         * @param {Node} this This node
26768         * @param {Node} oldParent The old parent of this node
26769         * @param {Node} newParent The new parent of this node
26770         * @param {Number} index The index it was moved to
26771         */
26772        "move" : true,
26773        /**
26774         * @event insert
26775         * Fires when a new child node is inserted.
26776         * @param {Tree} tree The owner tree
26777         * @param {Node} this This node
26778         * @param {Node} node The child node inserted
26779         * @param {Node} refNode The child node the node was inserted before
26780         */
26781        "insert" : true,
26782        /**
26783         * @event beforeappend
26784         * Fires before a new child is appended, return false to cancel the append.
26785         * @param {Tree} tree The owner tree
26786         * @param {Node} this This node
26787         * @param {Node} node The child node to be appended
26788         */
26789        "beforeappend" : true,
26790        /**
26791         * @event beforeremove
26792         * Fires before a child is removed, return false to cancel the remove.
26793         * @param {Tree} tree The owner tree
26794         * @param {Node} this This node
26795         * @param {Node} node The child node to be removed
26796         */
26797        "beforeremove" : true,
26798        /**
26799         * @event beforemove
26800         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26801         * @param {Tree} tree The owner tree
26802         * @param {Node} this This node
26803         * @param {Node} oldParent The parent of this node
26804         * @param {Node} newParent The new parent this node is moving to
26805         * @param {Number} index The index it is being moved to
26806         */
26807        "beforemove" : true,
26808        /**
26809         * @event beforeinsert
26810         * Fires before a new child is inserted, return false to cancel the insert.
26811         * @param {Tree} tree The owner tree
26812         * @param {Node} this This node
26813         * @param {Node} node The child node to be inserted
26814         * @param {Node} refNode The child node the node is being inserted before
26815         */
26816        "beforeinsert" : true
26817    });
26818     this.listeners = this.attributes.listeners;
26819     Roo.data.Node.superclass.constructor.call(this);
26820 };
26821
26822 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26823     fireEvent : function(evtName){
26824         // first do standard event for this node
26825         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26826             return false;
26827         }
26828         // then bubble it up to the tree if the event wasn't cancelled
26829         var ot = this.getOwnerTree();
26830         if(ot){
26831             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26832                 return false;
26833             }
26834         }
26835         return true;
26836     },
26837
26838     /**
26839      * Returns true if this node is a leaf
26840      * @return {Boolean}
26841      */
26842     isLeaf : function(){
26843         return this.leaf === true;
26844     },
26845
26846     // private
26847     setFirstChild : function(node){
26848         this.firstChild = node;
26849     },
26850
26851     //private
26852     setLastChild : function(node){
26853         this.lastChild = node;
26854     },
26855
26856
26857     /**
26858      * Returns true if this node is the last child of its parent
26859      * @return {Boolean}
26860      */
26861     isLast : function(){
26862        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26863     },
26864
26865     /**
26866      * Returns true if this node is the first child of its parent
26867      * @return {Boolean}
26868      */
26869     isFirst : function(){
26870        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26871     },
26872
26873     hasChildNodes : function(){
26874         return !this.isLeaf() && this.childNodes.length > 0;
26875     },
26876
26877     /**
26878      * Insert node(s) as the last child node of this node.
26879      * @param {Node/Array} node The node or Array of nodes to append
26880      * @return {Node} The appended node if single append, or null if an array was passed
26881      */
26882     appendChild : function(node){
26883         var multi = false;
26884         if(node instanceof Array){
26885             multi = node;
26886         }else if(arguments.length > 1){
26887             multi = arguments;
26888         }
26889         
26890         // if passed an array or multiple args do them one by one
26891         if(multi){
26892             for(var i = 0, len = multi.length; i < len; i++) {
26893                 this.appendChild(multi[i]);
26894             }
26895         }else{
26896             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26897                 return false;
26898             }
26899             var index = this.childNodes.length;
26900             var oldParent = node.parentNode;
26901             // it's a move, make sure we move it cleanly
26902             if(oldParent){
26903                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26904                     return false;
26905                 }
26906                 oldParent.removeChild(node);
26907             }
26908             
26909             index = this.childNodes.length;
26910             if(index == 0){
26911                 this.setFirstChild(node);
26912             }
26913             this.childNodes.push(node);
26914             node.parentNode = this;
26915             var ps = this.childNodes[index-1];
26916             if(ps){
26917                 node.previousSibling = ps;
26918                 ps.nextSibling = node;
26919             }else{
26920                 node.previousSibling = null;
26921             }
26922             node.nextSibling = null;
26923             this.setLastChild(node);
26924             node.setOwnerTree(this.getOwnerTree());
26925             this.fireEvent("append", this.ownerTree, this, node, index);
26926             if(this.ownerTree) {
26927                 this.ownerTree.fireEvent("appendnode", this, node, index);
26928             }
26929             if(oldParent){
26930                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26931             }
26932             return node;
26933         }
26934     },
26935
26936     /**
26937      * Removes a child node from this node.
26938      * @param {Node} node The node to remove
26939      * @return {Node} The removed node
26940      */
26941     removeChild : function(node){
26942         var index = this.childNodes.indexOf(node);
26943         if(index == -1){
26944             return false;
26945         }
26946         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26947             return false;
26948         }
26949
26950         // remove it from childNodes collection
26951         this.childNodes.splice(index, 1);
26952
26953         // update siblings
26954         if(node.previousSibling){
26955             node.previousSibling.nextSibling = node.nextSibling;
26956         }
26957         if(node.nextSibling){
26958             node.nextSibling.previousSibling = node.previousSibling;
26959         }
26960
26961         // update child refs
26962         if(this.firstChild == node){
26963             this.setFirstChild(node.nextSibling);
26964         }
26965         if(this.lastChild == node){
26966             this.setLastChild(node.previousSibling);
26967         }
26968
26969         node.setOwnerTree(null);
26970         // clear any references from the node
26971         node.parentNode = null;
26972         node.previousSibling = null;
26973         node.nextSibling = null;
26974         this.fireEvent("remove", this.ownerTree, this, node);
26975         return node;
26976     },
26977
26978     /**
26979      * Inserts the first node before the second node in this nodes childNodes collection.
26980      * @param {Node} node The node to insert
26981      * @param {Node} refNode The node to insert before (if null the node is appended)
26982      * @return {Node} The inserted node
26983      */
26984     insertBefore : function(node, refNode){
26985         if(!refNode){ // like standard Dom, refNode can be null for append
26986             return this.appendChild(node);
26987         }
26988         // nothing to do
26989         if(node == refNode){
26990             return false;
26991         }
26992
26993         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26994             return false;
26995         }
26996         var index = this.childNodes.indexOf(refNode);
26997         var oldParent = node.parentNode;
26998         var refIndex = index;
26999
27000         // when moving internally, indexes will change after remove
27001         if(oldParent == this && this.childNodes.indexOf(node) < index){
27002             refIndex--;
27003         }
27004
27005         // it's a move, make sure we move it cleanly
27006         if(oldParent){
27007             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27008                 return false;
27009             }
27010             oldParent.removeChild(node);
27011         }
27012         if(refIndex == 0){
27013             this.setFirstChild(node);
27014         }
27015         this.childNodes.splice(refIndex, 0, node);
27016         node.parentNode = this;
27017         var ps = this.childNodes[refIndex-1];
27018         if(ps){
27019             node.previousSibling = ps;
27020             ps.nextSibling = node;
27021         }else{
27022             node.previousSibling = null;
27023         }
27024         node.nextSibling = refNode;
27025         refNode.previousSibling = node;
27026         node.setOwnerTree(this.getOwnerTree());
27027         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27028         if(oldParent){
27029             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27030         }
27031         return node;
27032     },
27033
27034     /**
27035      * Returns the child node at the specified index.
27036      * @param {Number} index
27037      * @return {Node}
27038      */
27039     item : function(index){
27040         return this.childNodes[index];
27041     },
27042
27043     /**
27044      * Replaces one child node in this node with another.
27045      * @param {Node} newChild The replacement node
27046      * @param {Node} oldChild The node to replace
27047      * @return {Node} The replaced node
27048      */
27049     replaceChild : function(newChild, oldChild){
27050         this.insertBefore(newChild, oldChild);
27051         this.removeChild(oldChild);
27052         return oldChild;
27053     },
27054
27055     /**
27056      * Returns the index of a child node
27057      * @param {Node} node
27058      * @return {Number} The index of the node or -1 if it was not found
27059      */
27060     indexOf : function(child){
27061         return this.childNodes.indexOf(child);
27062     },
27063
27064     /**
27065      * Returns the tree this node is in.
27066      * @return {Tree}
27067      */
27068     getOwnerTree : function(){
27069         // if it doesn't have one, look for one
27070         if(!this.ownerTree){
27071             var p = this;
27072             while(p){
27073                 if(p.ownerTree){
27074                     this.ownerTree = p.ownerTree;
27075                     break;
27076                 }
27077                 p = p.parentNode;
27078             }
27079         }
27080         return this.ownerTree;
27081     },
27082
27083     /**
27084      * Returns depth of this node (the root node has a depth of 0)
27085      * @return {Number}
27086      */
27087     getDepth : function(){
27088         var depth = 0;
27089         var p = this;
27090         while(p.parentNode){
27091             ++depth;
27092             p = p.parentNode;
27093         }
27094         return depth;
27095     },
27096
27097     // private
27098     setOwnerTree : function(tree){
27099         // if it's move, we need to update everyone
27100         if(tree != this.ownerTree){
27101             if(this.ownerTree){
27102                 this.ownerTree.unregisterNode(this);
27103             }
27104             this.ownerTree = tree;
27105             var cs = this.childNodes;
27106             for(var i = 0, len = cs.length; i < len; i++) {
27107                 cs[i].setOwnerTree(tree);
27108             }
27109             if(tree){
27110                 tree.registerNode(this);
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27117      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27118      * @return {String} The path
27119      */
27120     getPath : function(attr){
27121         attr = attr || "id";
27122         var p = this.parentNode;
27123         var b = [this.attributes[attr]];
27124         while(p){
27125             b.unshift(p.attributes[attr]);
27126             p = p.parentNode;
27127         }
27128         var sep = this.getOwnerTree().pathSeparator;
27129         return sep + b.join(sep);
27130     },
27131
27132     /**
27133      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27134      * function call will be the scope provided or the current node. The arguments to the function
27135      * will be the args provided or the current node. If the function returns false at any point,
27136      * the bubble is stopped.
27137      * @param {Function} fn The function to call
27138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27140      */
27141     bubble : function(fn, scope, args){
27142         var p = this;
27143         while(p){
27144             if(fn.call(scope || p, args || p) === false){
27145                 break;
27146             }
27147             p = p.parentNode;
27148         }
27149     },
27150
27151     /**
27152      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27153      * function call will be the scope provided or the current node. The arguments to the function
27154      * will be the args provided or the current node. If the function returns false at any point,
27155      * the cascade is stopped on that branch.
27156      * @param {Function} fn The function to call
27157      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27158      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27159      */
27160     cascade : function(fn, scope, args){
27161         if(fn.call(scope || this, args || this) !== false){
27162             var cs = this.childNodes;
27163             for(var i = 0, len = cs.length; i < len; i++) {
27164                 cs[i].cascade(fn, scope, args);
27165             }
27166         }
27167     },
27168
27169     /**
27170      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27171      * function call will be the scope provided or the current node. The arguments to the function
27172      * will be the args provided or the current node. If the function returns false at any point,
27173      * the iteration stops.
27174      * @param {Function} fn The function to call
27175      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27176      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27177      */
27178     eachChild : function(fn, scope, args){
27179         var cs = this.childNodes;
27180         for(var i = 0, len = cs.length; i < len; i++) {
27181                 if(fn.call(scope || this, args || cs[i]) === false){
27182                     break;
27183                 }
27184         }
27185     },
27186
27187     /**
27188      * Finds the first child that has the attribute with the specified value.
27189      * @param {String} attribute The attribute name
27190      * @param {Mixed} value The value to search for
27191      * @return {Node} The found child or null if none was found
27192      */
27193     findChild : function(attribute, value){
27194         var cs = this.childNodes;
27195         for(var i = 0, len = cs.length; i < len; i++) {
27196                 if(cs[i].attributes[attribute] == value){
27197                     return cs[i];
27198                 }
27199         }
27200         return null;
27201     },
27202
27203     /**
27204      * Finds the first child by a custom function. The child matches if the function passed
27205      * returns true.
27206      * @param {Function} fn
27207      * @param {Object} scope (optional)
27208      * @return {Node} The found child or null if none was found
27209      */
27210     findChildBy : function(fn, scope){
27211         var cs = this.childNodes;
27212         for(var i = 0, len = cs.length; i < len; i++) {
27213                 if(fn.call(scope||cs[i], cs[i]) === true){
27214                     return cs[i];
27215                 }
27216         }
27217         return null;
27218     },
27219
27220     /**
27221      * Sorts this nodes children using the supplied sort function
27222      * @param {Function} fn
27223      * @param {Object} scope (optional)
27224      */
27225     sort : function(fn, scope){
27226         var cs = this.childNodes;
27227         var len = cs.length;
27228         if(len > 0){
27229             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27230             cs.sort(sortFn);
27231             for(var i = 0; i < len; i++){
27232                 var n = cs[i];
27233                 n.previousSibling = cs[i-1];
27234                 n.nextSibling = cs[i+1];
27235                 if(i == 0){
27236                     this.setFirstChild(n);
27237                 }
27238                 if(i == len-1){
27239                     this.setLastChild(n);
27240                 }
27241             }
27242         }
27243     },
27244
27245     /**
27246      * Returns true if this node is an ancestor (at any point) of the passed node.
27247      * @param {Node} node
27248      * @return {Boolean}
27249      */
27250     contains : function(node){
27251         return node.isAncestor(this);
27252     },
27253
27254     /**
27255      * Returns true if the passed node is an ancestor (at any point) of this node.
27256      * @param {Node} node
27257      * @return {Boolean}
27258      */
27259     isAncestor : function(node){
27260         var p = this.parentNode;
27261         while(p){
27262             if(p == node){
27263                 return true;
27264             }
27265             p = p.parentNode;
27266         }
27267         return false;
27268     },
27269
27270     toString : function(){
27271         return "[Node"+(this.id?" "+this.id:"")+"]";
27272     }
27273 });/*
27274  * Based on:
27275  * Ext JS Library 1.1.1
27276  * Copyright(c) 2006-2007, Ext JS, LLC.
27277  *
27278  * Originally Released Under LGPL - original licence link has changed is not relivant.
27279  *
27280  * Fork - LGPL
27281  * <script type="text/javascript">
27282  */
27283
27284
27285 /**
27286  * @class Roo.Shadow
27287  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27288  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27289  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27290  * @constructor
27291  * Create a new Shadow
27292  * @param {Object} config The config object
27293  */
27294 Roo.Shadow = function(config){
27295     Roo.apply(this, config);
27296     if(typeof this.mode != "string"){
27297         this.mode = this.defaultMode;
27298     }
27299     var o = this.offset, a = {h: 0};
27300     var rad = Math.floor(this.offset/2);
27301     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27302         case "drop":
27303             a.w = 0;
27304             a.l = a.t = o;
27305             a.t -= 1;
27306             if(Roo.isIE){
27307                 a.l -= this.offset + rad;
27308                 a.t -= this.offset + rad;
27309                 a.w -= rad;
27310                 a.h -= rad;
27311                 a.t += 1;
27312             }
27313         break;
27314         case "sides":
27315             a.w = (o*2);
27316             a.l = -o;
27317             a.t = o-1;
27318             if(Roo.isIE){
27319                 a.l -= (this.offset - rad);
27320                 a.t -= this.offset + rad;
27321                 a.l += 1;
27322                 a.w -= (this.offset - rad)*2;
27323                 a.w -= rad + 1;
27324                 a.h -= 1;
27325             }
27326         break;
27327         case "frame":
27328             a.w = a.h = (o*2);
27329             a.l = a.t = -o;
27330             a.t += 1;
27331             a.h -= 2;
27332             if(Roo.isIE){
27333                 a.l -= (this.offset - rad);
27334                 a.t -= (this.offset - rad);
27335                 a.l += 1;
27336                 a.w -= (this.offset + rad + 1);
27337                 a.h -= (this.offset + rad);
27338                 a.h += 1;
27339             }
27340         break;
27341     };
27342
27343     this.adjusts = a;
27344 };
27345
27346 Roo.Shadow.prototype = {
27347     /**
27348      * @cfg {String} mode
27349      * The shadow display mode.  Supports the following options:<br />
27350      * sides: Shadow displays on both sides and bottom only<br />
27351      * frame: Shadow displays equally on all four sides<br />
27352      * drop: Traditional bottom-right drop shadow (default)
27353      */
27354     mode: false,
27355     /**
27356      * @cfg {String} offset
27357      * The number of pixels to offset the shadow from the element (defaults to 4)
27358      */
27359     offset: 4,
27360
27361     // private
27362     defaultMode: "drop",
27363
27364     /**
27365      * Displays the shadow under the target element
27366      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27367      */
27368     show : function(target){
27369         target = Roo.get(target);
27370         if(!this.el){
27371             this.el = Roo.Shadow.Pool.pull();
27372             if(this.el.dom.nextSibling != target.dom){
27373                 this.el.insertBefore(target);
27374             }
27375         }
27376         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27377         if(Roo.isIE){
27378             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27379         }
27380         this.realign(
27381             target.getLeft(true),
27382             target.getTop(true),
27383             target.getWidth(),
27384             target.getHeight()
27385         );
27386         this.el.dom.style.display = "block";
27387     },
27388
27389     /**
27390      * Returns true if the shadow is visible, else false
27391      */
27392     isVisible : function(){
27393         return this.el ? true : false;  
27394     },
27395
27396     /**
27397      * Direct alignment when values are already available. Show must be called at least once before
27398      * calling this method to ensure it is initialized.
27399      * @param {Number} left The target element left position
27400      * @param {Number} top The target element top position
27401      * @param {Number} width The target element width
27402      * @param {Number} height The target element height
27403      */
27404     realign : function(l, t, w, h){
27405         if(!this.el){
27406             return;
27407         }
27408         var a = this.adjusts, d = this.el.dom, s = d.style;
27409         var iea = 0;
27410         s.left = (l+a.l)+"px";
27411         s.top = (t+a.t)+"px";
27412         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27413  
27414         if(s.width != sws || s.height != shs){
27415             s.width = sws;
27416             s.height = shs;
27417             if(!Roo.isIE){
27418                 var cn = d.childNodes;
27419                 var sww = Math.max(0, (sw-12))+"px";
27420                 cn[0].childNodes[1].style.width = sww;
27421                 cn[1].childNodes[1].style.width = sww;
27422                 cn[2].childNodes[1].style.width = sww;
27423                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27424             }
27425         }
27426     },
27427
27428     /**
27429      * Hides this shadow
27430      */
27431     hide : function(){
27432         if(this.el){
27433             this.el.dom.style.display = "none";
27434             Roo.Shadow.Pool.push(this.el);
27435             delete this.el;
27436         }
27437     },
27438
27439     /**
27440      * Adjust the z-index of this shadow
27441      * @param {Number} zindex The new z-index
27442      */
27443     setZIndex : function(z){
27444         this.zIndex = z;
27445         if(this.el){
27446             this.el.setStyle("z-index", z);
27447         }
27448     }
27449 };
27450
27451 // Private utility class that manages the internal Shadow cache
27452 Roo.Shadow.Pool = function(){
27453     var p = [];
27454     var markup = Roo.isIE ?
27455                  '<div class="x-ie-shadow"></div>' :
27456                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
27457     return {
27458         pull : function(){
27459             var sh = p.shift();
27460             if(!sh){
27461                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27462                 sh.autoBoxAdjust = false;
27463             }
27464             return sh;
27465         },
27466
27467         push : function(sh){
27468             p.push(sh);
27469         }
27470     };
27471 }();/*
27472  * Based on:
27473  * Ext JS Library 1.1.1
27474  * Copyright(c) 2006-2007, Ext JS, LLC.
27475  *
27476  * Originally Released Under LGPL - original licence link has changed is not relivant.
27477  *
27478  * Fork - LGPL
27479  * <script type="text/javascript">
27480  */
27481
27482
27483 /**
27484  * @class Roo.SplitBar
27485  * @extends Roo.util.Observable
27486  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27487  * <br><br>
27488  * Usage:
27489  * <pre><code>
27490 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27491                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27492 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27493 split.minSize = 100;
27494 split.maxSize = 600;
27495 split.animate = true;
27496 split.on('moved', splitterMoved);
27497 </code></pre>
27498  * @constructor
27499  * Create a new SplitBar
27500  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27501  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27502  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27503  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27504                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27505                         position of the SplitBar).
27506  */
27507 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27508     
27509     /** @private */
27510     this.el = Roo.get(dragElement, true);
27511     this.el.dom.unselectable = "on";
27512     /** @private */
27513     this.resizingEl = Roo.get(resizingElement, true);
27514
27515     /**
27516      * @private
27517      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27518      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27519      * @type Number
27520      */
27521     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27522     
27523     /**
27524      * The minimum size of the resizing element. (Defaults to 0)
27525      * @type Number
27526      */
27527     this.minSize = 0;
27528     
27529     /**
27530      * The maximum size of the resizing element. (Defaults to 2000)
27531      * @type Number
27532      */
27533     this.maxSize = 2000;
27534     
27535     /**
27536      * Whether to animate the transition to the new size
27537      * @type Boolean
27538      */
27539     this.animate = false;
27540     
27541     /**
27542      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27543      * @type Boolean
27544      */
27545     this.useShim = false;
27546     
27547     /** @private */
27548     this.shim = null;
27549     
27550     if(!existingProxy){
27551         /** @private */
27552         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27553     }else{
27554         this.proxy = Roo.get(existingProxy).dom;
27555     }
27556     /** @private */
27557     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27558     
27559     /** @private */
27560     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27561     
27562     /** @private */
27563     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27564     
27565     /** @private */
27566     this.dragSpecs = {};
27567     
27568     /**
27569      * @private The adapter to use to positon and resize elements
27570      */
27571     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27572     this.adapter.init(this);
27573     
27574     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27575         /** @private */
27576         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27577         this.el.addClass("x-splitbar-h");
27578     }else{
27579         /** @private */
27580         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27581         this.el.addClass("x-splitbar-v");
27582     }
27583     
27584     this.addEvents({
27585         /**
27586          * @event resize
27587          * Fires when the splitter is moved (alias for {@link #event-moved})
27588          * @param {Roo.SplitBar} this
27589          * @param {Number} newSize the new width or height
27590          */
27591         "resize" : true,
27592         /**
27593          * @event moved
27594          * Fires when the splitter is moved
27595          * @param {Roo.SplitBar} this
27596          * @param {Number} newSize the new width or height
27597          */
27598         "moved" : true,
27599         /**
27600          * @event beforeresize
27601          * Fires before the splitter is dragged
27602          * @param {Roo.SplitBar} this
27603          */
27604         "beforeresize" : true,
27605
27606         "beforeapply" : true
27607     });
27608
27609     Roo.util.Observable.call(this);
27610 };
27611
27612 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27613     onStartProxyDrag : function(x, y){
27614         this.fireEvent("beforeresize", this);
27615         if(!this.overlay){
27616             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27617             o.unselectable();
27618             o.enableDisplayMode("block");
27619             // all splitbars share the same overlay
27620             Roo.SplitBar.prototype.overlay = o;
27621         }
27622         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27623         this.overlay.show();
27624         Roo.get(this.proxy).setDisplayed("block");
27625         var size = this.adapter.getElementSize(this);
27626         this.activeMinSize = this.getMinimumSize();;
27627         this.activeMaxSize = this.getMaximumSize();;
27628         var c1 = size - this.activeMinSize;
27629         var c2 = Math.max(this.activeMaxSize - size, 0);
27630         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27631             this.dd.resetConstraints();
27632             this.dd.setXConstraint(
27633                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27634                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27635             );
27636             this.dd.setYConstraint(0, 0);
27637         }else{
27638             this.dd.resetConstraints();
27639             this.dd.setXConstraint(0, 0);
27640             this.dd.setYConstraint(
27641                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27642                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27643             );
27644          }
27645         this.dragSpecs.startSize = size;
27646         this.dragSpecs.startPoint = [x, y];
27647         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27648     },
27649     
27650     /** 
27651      * @private Called after the drag operation by the DDProxy
27652      */
27653     onEndProxyDrag : function(e){
27654         Roo.get(this.proxy).setDisplayed(false);
27655         var endPoint = Roo.lib.Event.getXY(e);
27656         if(this.overlay){
27657             this.overlay.hide();
27658         }
27659         var newSize;
27660         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27661             newSize = this.dragSpecs.startSize + 
27662                 (this.placement == Roo.SplitBar.LEFT ?
27663                     endPoint[0] - this.dragSpecs.startPoint[0] :
27664                     this.dragSpecs.startPoint[0] - endPoint[0]
27665                 );
27666         }else{
27667             newSize = this.dragSpecs.startSize + 
27668                 (this.placement == Roo.SplitBar.TOP ?
27669                     endPoint[1] - this.dragSpecs.startPoint[1] :
27670                     this.dragSpecs.startPoint[1] - endPoint[1]
27671                 );
27672         }
27673         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27674         if(newSize != this.dragSpecs.startSize){
27675             if(this.fireEvent('beforeapply', this, newSize) !== false){
27676                 this.adapter.setElementSize(this, newSize);
27677                 this.fireEvent("moved", this, newSize);
27678                 this.fireEvent("resize", this, newSize);
27679             }
27680         }
27681     },
27682     
27683     /**
27684      * Get the adapter this SplitBar uses
27685      * @return The adapter object
27686      */
27687     getAdapter : function(){
27688         return this.adapter;
27689     },
27690     
27691     /**
27692      * Set the adapter this SplitBar uses
27693      * @param {Object} adapter A SplitBar adapter object
27694      */
27695     setAdapter : function(adapter){
27696         this.adapter = adapter;
27697         this.adapter.init(this);
27698     },
27699     
27700     /**
27701      * Gets the minimum size for the resizing element
27702      * @return {Number} The minimum size
27703      */
27704     getMinimumSize : function(){
27705         return this.minSize;
27706     },
27707     
27708     /**
27709      * Sets the minimum size for the resizing element
27710      * @param {Number} minSize The minimum size
27711      */
27712     setMinimumSize : function(minSize){
27713         this.minSize = minSize;
27714     },
27715     
27716     /**
27717      * Gets the maximum size for the resizing element
27718      * @return {Number} The maximum size
27719      */
27720     getMaximumSize : function(){
27721         return this.maxSize;
27722     },
27723     
27724     /**
27725      * Sets the maximum size for the resizing element
27726      * @param {Number} maxSize The maximum size
27727      */
27728     setMaximumSize : function(maxSize){
27729         this.maxSize = maxSize;
27730     },
27731     
27732     /**
27733      * Sets the initialize size for the resizing element
27734      * @param {Number} size The initial size
27735      */
27736     setCurrentSize : function(size){
27737         var oldAnimate = this.animate;
27738         this.animate = false;
27739         this.adapter.setElementSize(this, size);
27740         this.animate = oldAnimate;
27741     },
27742     
27743     /**
27744      * Destroy this splitbar. 
27745      * @param {Boolean} removeEl True to remove the element
27746      */
27747     destroy : function(removeEl){
27748         if(this.shim){
27749             this.shim.remove();
27750         }
27751         this.dd.unreg();
27752         this.proxy.parentNode.removeChild(this.proxy);
27753         if(removeEl){
27754             this.el.remove();
27755         }
27756     }
27757 });
27758
27759 /**
27760  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
27761  */
27762 Roo.SplitBar.createProxy = function(dir){
27763     var proxy = new Roo.Element(document.createElement("div"));
27764     proxy.unselectable();
27765     var cls = 'x-splitbar-proxy';
27766     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27767     document.body.appendChild(proxy.dom);
27768     return proxy.dom;
27769 };
27770
27771 /** 
27772  * @class Roo.SplitBar.BasicLayoutAdapter
27773  * Default Adapter. It assumes the splitter and resizing element are not positioned
27774  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27775  */
27776 Roo.SplitBar.BasicLayoutAdapter = function(){
27777 };
27778
27779 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27780     // do nothing for now
27781     init : function(s){
27782     
27783     },
27784     /**
27785      * Called before drag operations to get the current size of the resizing element. 
27786      * @param {Roo.SplitBar} s The SplitBar using this adapter
27787      */
27788      getElementSize : function(s){
27789         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27790             return s.resizingEl.getWidth();
27791         }else{
27792             return s.resizingEl.getHeight();
27793         }
27794     },
27795     
27796     /**
27797      * Called after drag operations to set the size of the resizing element.
27798      * @param {Roo.SplitBar} s The SplitBar using this adapter
27799      * @param {Number} newSize The new size to set
27800      * @param {Function} onComplete A function to be invoked when resizing is complete
27801      */
27802     setElementSize : function(s, newSize, onComplete){
27803         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27804             if(!s.animate){
27805                 s.resizingEl.setWidth(newSize);
27806                 if(onComplete){
27807                     onComplete(s, newSize);
27808                 }
27809             }else{
27810                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27811             }
27812         }else{
27813             
27814             if(!s.animate){
27815                 s.resizingEl.setHeight(newSize);
27816                 if(onComplete){
27817                     onComplete(s, newSize);
27818                 }
27819             }else{
27820                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27821             }
27822         }
27823     }
27824 };
27825
27826 /** 
27827  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27828  * @extends Roo.SplitBar.BasicLayoutAdapter
27829  * Adapter that  moves the splitter element to align with the resized sizing element. 
27830  * Used with an absolute positioned SplitBar.
27831  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27832  * document.body, make sure you assign an id to the body element.
27833  */
27834 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27835     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27836     this.container = Roo.get(container);
27837 };
27838
27839 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27840     init : function(s){
27841         this.basic.init(s);
27842     },
27843     
27844     getElementSize : function(s){
27845         return this.basic.getElementSize(s);
27846     },
27847     
27848     setElementSize : function(s, newSize, onComplete){
27849         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27850     },
27851     
27852     moveSplitter : function(s){
27853         var yes = Roo.SplitBar;
27854         switch(s.placement){
27855             case yes.LEFT:
27856                 s.el.setX(s.resizingEl.getRight());
27857                 break;
27858             case yes.RIGHT:
27859                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27860                 break;
27861             case yes.TOP:
27862                 s.el.setY(s.resizingEl.getBottom());
27863                 break;
27864             case yes.BOTTOM:
27865                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27866                 break;
27867         }
27868     }
27869 };
27870
27871 /**
27872  * Orientation constant - Create a vertical SplitBar
27873  * @static
27874  * @type Number
27875  */
27876 Roo.SplitBar.VERTICAL = 1;
27877
27878 /**
27879  * Orientation constant - Create a horizontal SplitBar
27880  * @static
27881  * @type Number
27882  */
27883 Roo.SplitBar.HORIZONTAL = 2;
27884
27885 /**
27886  * Placement constant - The resizing element is to the left of the splitter element
27887  * @static
27888  * @type Number
27889  */
27890 Roo.SplitBar.LEFT = 1;
27891
27892 /**
27893  * Placement constant - The resizing element is to the right of the splitter element
27894  * @static
27895  * @type Number
27896  */
27897 Roo.SplitBar.RIGHT = 2;
27898
27899 /**
27900  * Placement constant - The resizing element is positioned above the splitter element
27901  * @static
27902  * @type Number
27903  */
27904 Roo.SplitBar.TOP = 3;
27905
27906 /**
27907  * Placement constant - The resizing element is positioned under splitter element
27908  * @static
27909  * @type Number
27910  */
27911 Roo.SplitBar.BOTTOM = 4;
27912 /*
27913  * Based on:
27914  * Ext JS Library 1.1.1
27915  * Copyright(c) 2006-2007, Ext JS, LLC.
27916  *
27917  * Originally Released Under LGPL - original licence link has changed is not relivant.
27918  *
27919  * Fork - LGPL
27920  * <script type="text/javascript">
27921  */
27922
27923 /**
27924  * @class Roo.View
27925  * @extends Roo.util.Observable
27926  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27927  * This class also supports single and multi selection modes. <br>
27928  * Create a data model bound view:
27929  <pre><code>
27930  var store = new Roo.data.Store(...);
27931
27932  var view = new Roo.View({
27933     el : "my-element",
27934     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27935  
27936     singleSelect: true,
27937     selectedClass: "ydataview-selected",
27938     store: store
27939  });
27940
27941  // listen for node click?
27942  view.on("click", function(vw, index, node, e){
27943  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27944  });
27945
27946  // load XML data
27947  dataModel.load("foobar.xml");
27948  </code></pre>
27949  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27950  * <br><br>
27951  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27952  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27953  * 
27954  * Note: old style constructor is still suported (container, template, config)
27955  * 
27956  * @constructor
27957  * Create a new View
27958  * @param {Object} config The config object
27959  * 
27960  */
27961 Roo.View = function(config, depreciated_tpl, depreciated_config){
27962     
27963     this.parent = false;
27964     
27965     if (typeof(depreciated_tpl) == 'undefined') {
27966         // new way.. - universal constructor.
27967         Roo.apply(this, config);
27968         this.el  = Roo.get(this.el);
27969     } else {
27970         // old format..
27971         this.el  = Roo.get(config);
27972         this.tpl = depreciated_tpl;
27973         Roo.apply(this, depreciated_config);
27974     }
27975     this.wrapEl  = this.el.wrap().wrap();
27976     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27977     
27978     
27979     if(typeof(this.tpl) == "string"){
27980         this.tpl = new Roo.Template(this.tpl);
27981     } else {
27982         // support xtype ctors..
27983         this.tpl = new Roo.factory(this.tpl, Roo);
27984     }
27985     
27986     
27987     this.tpl.compile();
27988     
27989     /** @private */
27990     this.addEvents({
27991         /**
27992          * @event beforeclick
27993          * Fires before a click is processed. Returns false to cancel the default action.
27994          * @param {Roo.View} this
27995          * @param {Number} index The index of the target node
27996          * @param {HTMLElement} node The target node
27997          * @param {Roo.EventObject} e The raw event object
27998          */
27999             "beforeclick" : true,
28000         /**
28001          * @event click
28002          * Fires when a template node is clicked.
28003          * @param {Roo.View} this
28004          * @param {Number} index The index of the target node
28005          * @param {HTMLElement} node The target node
28006          * @param {Roo.EventObject} e The raw event object
28007          */
28008             "click" : true,
28009         /**
28010          * @event dblclick
28011          * Fires when a template node is double clicked.
28012          * @param {Roo.View} this
28013          * @param {Number} index The index of the target node
28014          * @param {HTMLElement} node The target node
28015          * @param {Roo.EventObject} e The raw event object
28016          */
28017             "dblclick" : true,
28018         /**
28019          * @event contextmenu
28020          * Fires when a template node is right clicked.
28021          * @param {Roo.View} this
28022          * @param {Number} index The index of the target node
28023          * @param {HTMLElement} node The target node
28024          * @param {Roo.EventObject} e The raw event object
28025          */
28026             "contextmenu" : true,
28027         /**
28028          * @event selectionchange
28029          * Fires when the selected nodes change.
28030          * @param {Roo.View} this
28031          * @param {Array} selections Array of the selected nodes
28032          */
28033             "selectionchange" : true,
28034     
28035         /**
28036          * @event beforeselect
28037          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28038          * @param {Roo.View} this
28039          * @param {HTMLElement} node The node to be selected
28040          * @param {Array} selections Array of currently selected nodes
28041          */
28042             "beforeselect" : true,
28043         /**
28044          * @event preparedata
28045          * Fires on every row to render, to allow you to change the data.
28046          * @param {Roo.View} this
28047          * @param {Object} data to be rendered (change this)
28048          */
28049           "preparedata" : true
28050           
28051           
28052         });
28053
28054
28055
28056     this.el.on({
28057         "click": this.onClick,
28058         "dblclick": this.onDblClick,
28059         "contextmenu": this.onContextMenu,
28060         scope:this
28061     });
28062
28063     this.selections = [];
28064     this.nodes = [];
28065     this.cmp = new Roo.CompositeElementLite([]);
28066     if(this.store){
28067         this.store = Roo.factory(this.store, Roo.data);
28068         this.setStore(this.store, true);
28069     }
28070     
28071     if ( this.footer && this.footer.xtype) {
28072            
28073          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28074         
28075         this.footer.dataSource = this.store;
28076         this.footer.container = fctr;
28077         this.footer = Roo.factory(this.footer, Roo);
28078         fctr.insertFirst(this.el);
28079         
28080         // this is a bit insane - as the paging toolbar seems to detach the el..
28081 //        dom.parentNode.parentNode.parentNode
28082          // they get detached?
28083     }
28084     
28085     
28086     Roo.View.superclass.constructor.call(this);
28087     
28088     
28089 };
28090
28091 Roo.extend(Roo.View, Roo.util.Observable, {
28092     
28093      /**
28094      * @cfg {Roo.data.Store} store Data store to load data from.
28095      */
28096     store : false,
28097     
28098     /**
28099      * @cfg {String|Roo.Element} el The container element.
28100      */
28101     el : '',
28102     
28103     /**
28104      * @cfg {String|Roo.Template} tpl The template used by this View 
28105      */
28106     tpl : false,
28107     /**
28108      * @cfg {String} dataName the named area of the template to use as the data area
28109      *                          Works with domtemplates roo-name="name"
28110      */
28111     dataName: false,
28112     /**
28113      * @cfg {String} selectedClass The css class to add to selected nodes
28114      */
28115     selectedClass : "x-view-selected",
28116      /**
28117      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28118      */
28119     emptyText : "",
28120     
28121     /**
28122      * @cfg {String} text to display on mask (default Loading)
28123      */
28124     mask : false,
28125     /**
28126      * @cfg {Boolean} multiSelect Allow multiple selection
28127      */
28128     multiSelect : false,
28129     /**
28130      * @cfg {Boolean} singleSelect Allow single selection
28131      */
28132     singleSelect:  false,
28133     
28134     /**
28135      * @cfg {Boolean} toggleSelect - selecting 
28136      */
28137     toggleSelect : false,
28138     
28139     /**
28140      * @cfg {Boolean} tickable - selecting 
28141      */
28142     tickable : false,
28143     
28144     /**
28145      * Returns the element this view is bound to.
28146      * @return {Roo.Element}
28147      */
28148     getEl : function(){
28149         return this.wrapEl;
28150     },
28151     
28152     
28153
28154     /**
28155      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28156      */
28157     refresh : function(){
28158         //Roo.log('refresh');
28159         var t = this.tpl;
28160         
28161         // if we are using something like 'domtemplate', then
28162         // the what gets used is:
28163         // t.applySubtemplate(NAME, data, wrapping data..)
28164         // the outer template then get' applied with
28165         //     the store 'extra data'
28166         // and the body get's added to the
28167         //      roo-name="data" node?
28168         //      <span class='roo-tpl-{name}'></span> ?????
28169         
28170         
28171         
28172         this.clearSelections();
28173         this.el.update("");
28174         var html = [];
28175         var records = this.store.getRange();
28176         if(records.length < 1) {
28177             
28178             // is this valid??  = should it render a template??
28179             
28180             this.el.update(this.emptyText);
28181             return;
28182         }
28183         var el = this.el;
28184         if (this.dataName) {
28185             this.el.update(t.apply(this.store.meta)); //????
28186             el = this.el.child('.roo-tpl-' + this.dataName);
28187         }
28188         
28189         for(var i = 0, len = records.length; i < len; i++){
28190             var data = this.prepareData(records[i].data, i, records[i]);
28191             this.fireEvent("preparedata", this, data, i, records[i]);
28192             
28193             var d = Roo.apply({}, data);
28194             
28195             if(this.tickable){
28196                 Roo.apply(d, {'roo-id' : Roo.id()});
28197                 
28198                 var _this = this;
28199             
28200                 Roo.each(this.parent.item, function(item){
28201                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28202                         return;
28203                     }
28204                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28205                 });
28206             }
28207             
28208             html[html.length] = Roo.util.Format.trim(
28209                 this.dataName ?
28210                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28211                     t.apply(d)
28212             );
28213         }
28214         
28215         
28216         
28217         el.update(html.join(""));
28218         this.nodes = el.dom.childNodes;
28219         this.updateIndexes(0);
28220     },
28221     
28222
28223     /**
28224      * Function to override to reformat the data that is sent to
28225      * the template for each node.
28226      * DEPRICATED - use the preparedata event handler.
28227      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28228      * a JSON object for an UpdateManager bound view).
28229      */
28230     prepareData : function(data, index, record)
28231     {
28232         this.fireEvent("preparedata", this, data, index, record);
28233         return data;
28234     },
28235
28236     onUpdate : function(ds, record){
28237         // Roo.log('on update');   
28238         this.clearSelections();
28239         var index = this.store.indexOf(record);
28240         var n = this.nodes[index];
28241         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28242         n.parentNode.removeChild(n);
28243         this.updateIndexes(index, index);
28244     },
28245
28246     
28247     
28248 // --------- FIXME     
28249     onAdd : function(ds, records, index)
28250     {
28251         //Roo.log(['on Add', ds, records, index] );        
28252         this.clearSelections();
28253         if(this.nodes.length == 0){
28254             this.refresh();
28255             return;
28256         }
28257         var n = this.nodes[index];
28258         for(var i = 0, len = records.length; i < len; i++){
28259             var d = this.prepareData(records[i].data, i, records[i]);
28260             if(n){
28261                 this.tpl.insertBefore(n, d);
28262             }else{
28263                 
28264                 this.tpl.append(this.el, d);
28265             }
28266         }
28267         this.updateIndexes(index);
28268     },
28269
28270     onRemove : function(ds, record, index){
28271        // Roo.log('onRemove');
28272         this.clearSelections();
28273         var el = this.dataName  ?
28274             this.el.child('.roo-tpl-' + this.dataName) :
28275             this.el; 
28276         
28277         el.dom.removeChild(this.nodes[index]);
28278         this.updateIndexes(index);
28279     },
28280
28281     /**
28282      * Refresh an individual node.
28283      * @param {Number} index
28284      */
28285     refreshNode : function(index){
28286         this.onUpdate(this.store, this.store.getAt(index));
28287     },
28288
28289     updateIndexes : function(startIndex, endIndex){
28290         var ns = this.nodes;
28291         startIndex = startIndex || 0;
28292         endIndex = endIndex || ns.length - 1;
28293         for(var i = startIndex; i <= endIndex; i++){
28294             ns[i].nodeIndex = i;
28295         }
28296     },
28297
28298     /**
28299      * Changes the data store this view uses and refresh the view.
28300      * @param {Store} store
28301      */
28302     setStore : function(store, initial){
28303         if(!initial && this.store){
28304             this.store.un("datachanged", this.refresh);
28305             this.store.un("add", this.onAdd);
28306             this.store.un("remove", this.onRemove);
28307             this.store.un("update", this.onUpdate);
28308             this.store.un("clear", this.refresh);
28309             this.store.un("beforeload", this.onBeforeLoad);
28310             this.store.un("load", this.onLoad);
28311             this.store.un("loadexception", this.onLoad);
28312         }
28313         if(store){
28314           
28315             store.on("datachanged", this.refresh, this);
28316             store.on("add", this.onAdd, this);
28317             store.on("remove", this.onRemove, this);
28318             store.on("update", this.onUpdate, this);
28319             store.on("clear", this.refresh, this);
28320             store.on("beforeload", this.onBeforeLoad, this);
28321             store.on("load", this.onLoad, this);
28322             store.on("loadexception", this.onLoad, this);
28323         }
28324         
28325         if(store){
28326             this.refresh();
28327         }
28328     },
28329     /**
28330      * onbeforeLoad - masks the loading area.
28331      *
28332      */
28333     onBeforeLoad : function(store,opts)
28334     {
28335          //Roo.log('onBeforeLoad');   
28336         if (!opts.add) {
28337             this.el.update("");
28338         }
28339         this.el.mask(this.mask ? this.mask : "Loading" ); 
28340     },
28341     onLoad : function ()
28342     {
28343         this.el.unmask();
28344     },
28345     
28346
28347     /**
28348      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28349      * @param {HTMLElement} node
28350      * @return {HTMLElement} The template node
28351      */
28352     findItemFromChild : function(node){
28353         var el = this.dataName  ?
28354             this.el.child('.roo-tpl-' + this.dataName,true) :
28355             this.el.dom; 
28356         
28357         if(!node || node.parentNode == el){
28358                     return node;
28359             }
28360             var p = node.parentNode;
28361             while(p && p != el){
28362             if(p.parentNode == el){
28363                 return p;
28364             }
28365             p = p.parentNode;
28366         }
28367             return null;
28368     },
28369
28370     /** @ignore */
28371     onClick : function(e){
28372         var item = this.findItemFromChild(e.getTarget());
28373         if(item){
28374             var index = this.indexOf(item);
28375             if(this.onItemClick(item, index, e) !== false){
28376                 this.fireEvent("click", this, index, item, e);
28377             }
28378         }else{
28379             this.clearSelections();
28380         }
28381     },
28382
28383     /** @ignore */
28384     onContextMenu : function(e){
28385         var item = this.findItemFromChild(e.getTarget());
28386         if(item){
28387             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28388         }
28389     },
28390
28391     /** @ignore */
28392     onDblClick : function(e){
28393         var item = this.findItemFromChild(e.getTarget());
28394         if(item){
28395             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28396         }
28397     },
28398
28399     onItemClick : function(item, index, e)
28400     {
28401         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28402             return false;
28403         }
28404         if (this.toggleSelect) {
28405             var m = this.isSelected(item) ? 'unselect' : 'select';
28406             //Roo.log(m);
28407             var _t = this;
28408             _t[m](item, true, false);
28409             return true;
28410         }
28411         if(this.multiSelect || this.singleSelect){
28412             if(this.multiSelect && e.shiftKey && this.lastSelection){
28413                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28414             }else{
28415                 this.select(item, this.multiSelect && e.ctrlKey);
28416                 this.lastSelection = item;
28417             }
28418             
28419             if(!this.tickable){
28420                 e.preventDefault();
28421             }
28422             
28423         }
28424         return true;
28425     },
28426
28427     /**
28428      * Get the number of selected nodes.
28429      * @return {Number}
28430      */
28431     getSelectionCount : function(){
28432         return this.selections.length;
28433     },
28434
28435     /**
28436      * Get the currently selected nodes.
28437      * @return {Array} An array of HTMLElements
28438      */
28439     getSelectedNodes : function(){
28440         return this.selections;
28441     },
28442
28443     /**
28444      * Get the indexes of the selected nodes.
28445      * @return {Array}
28446      */
28447     getSelectedIndexes : function(){
28448         var indexes = [], s = this.selections;
28449         for(var i = 0, len = s.length; i < len; i++){
28450             indexes.push(s[i].nodeIndex);
28451         }
28452         return indexes;
28453     },
28454
28455     /**
28456      * Clear all selections
28457      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28458      */
28459     clearSelections : function(suppressEvent){
28460         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28461             this.cmp.elements = this.selections;
28462             this.cmp.removeClass(this.selectedClass);
28463             this.selections = [];
28464             if(!suppressEvent){
28465                 this.fireEvent("selectionchange", this, this.selections);
28466             }
28467         }
28468     },
28469
28470     /**
28471      * Returns true if the passed node is selected
28472      * @param {HTMLElement/Number} node The node or node index
28473      * @return {Boolean}
28474      */
28475     isSelected : function(node){
28476         var s = this.selections;
28477         if(s.length < 1){
28478             return false;
28479         }
28480         node = this.getNode(node);
28481         return s.indexOf(node) !== -1;
28482     },
28483
28484     /**
28485      * Selects nodes.
28486      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28487      * @param {Boolean} keepExisting (optional) true to keep existing selections
28488      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28489      */
28490     select : function(nodeInfo, keepExisting, suppressEvent){
28491         if(nodeInfo instanceof Array){
28492             if(!keepExisting){
28493                 this.clearSelections(true);
28494             }
28495             for(var i = 0, len = nodeInfo.length; i < len; i++){
28496                 this.select(nodeInfo[i], true, true);
28497             }
28498             return;
28499         } 
28500         var node = this.getNode(nodeInfo);
28501         if(!node || this.isSelected(node)){
28502             return; // already selected.
28503         }
28504         if(!keepExisting){
28505             this.clearSelections(true);
28506         }
28507         
28508         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28509             Roo.fly(node).addClass(this.selectedClass);
28510             this.selections.push(node);
28511             if(!suppressEvent){
28512                 this.fireEvent("selectionchange", this, this.selections);
28513             }
28514         }
28515         
28516         
28517     },
28518       /**
28519      * Unselects nodes.
28520      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28521      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28522      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28523      */
28524     unselect : function(nodeInfo, keepExisting, suppressEvent)
28525     {
28526         if(nodeInfo instanceof Array){
28527             Roo.each(this.selections, function(s) {
28528                 this.unselect(s, nodeInfo);
28529             }, this);
28530             return;
28531         }
28532         var node = this.getNode(nodeInfo);
28533         if(!node || !this.isSelected(node)){
28534             //Roo.log("not selected");
28535             return; // not selected.
28536         }
28537         // fireevent???
28538         var ns = [];
28539         Roo.each(this.selections, function(s) {
28540             if (s == node ) {
28541                 Roo.fly(node).removeClass(this.selectedClass);
28542
28543                 return;
28544             }
28545             ns.push(s);
28546         },this);
28547         
28548         this.selections= ns;
28549         this.fireEvent("selectionchange", this, this.selections);
28550     },
28551
28552     /**
28553      * Gets a template node.
28554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28555      * @return {HTMLElement} The node or null if it wasn't found
28556      */
28557     getNode : function(nodeInfo){
28558         if(typeof nodeInfo == "string"){
28559             return document.getElementById(nodeInfo);
28560         }else if(typeof nodeInfo == "number"){
28561             return this.nodes[nodeInfo];
28562         }
28563         return nodeInfo;
28564     },
28565
28566     /**
28567      * Gets a range template nodes.
28568      * @param {Number} startIndex
28569      * @param {Number} endIndex
28570      * @return {Array} An array of nodes
28571      */
28572     getNodes : function(start, end){
28573         var ns = this.nodes;
28574         start = start || 0;
28575         end = typeof end == "undefined" ? ns.length - 1 : end;
28576         var nodes = [];
28577         if(start <= end){
28578             for(var i = start; i <= end; i++){
28579                 nodes.push(ns[i]);
28580             }
28581         } else{
28582             for(var i = start; i >= end; i--){
28583                 nodes.push(ns[i]);
28584             }
28585         }
28586         return nodes;
28587     },
28588
28589     /**
28590      * Finds the index of the passed node
28591      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28592      * @return {Number} The index of the node or -1
28593      */
28594     indexOf : function(node){
28595         node = this.getNode(node);
28596         if(typeof node.nodeIndex == "number"){
28597             return node.nodeIndex;
28598         }
28599         var ns = this.nodes;
28600         for(var i = 0, len = ns.length; i < len; i++){
28601             if(ns[i] == node){
28602                 return i;
28603             }
28604         }
28605         return -1;
28606     }
28607 });
28608 /*
28609  * Based on:
28610  * Ext JS Library 1.1.1
28611  * Copyright(c) 2006-2007, Ext JS, LLC.
28612  *
28613  * Originally Released Under LGPL - original licence link has changed is not relivant.
28614  *
28615  * Fork - LGPL
28616  * <script type="text/javascript">
28617  */
28618
28619 /**
28620  * @class Roo.JsonView
28621  * @extends Roo.View
28622  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28623 <pre><code>
28624 var view = new Roo.JsonView({
28625     container: "my-element",
28626     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28627     multiSelect: true, 
28628     jsonRoot: "data" 
28629 });
28630
28631 // listen for node click?
28632 view.on("click", function(vw, index, node, e){
28633     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28634 });
28635
28636 // direct load of JSON data
28637 view.load("foobar.php");
28638
28639 // Example from my blog list
28640 var tpl = new Roo.Template(
28641     '&lt;div class="entry"&gt;' +
28642     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28643     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28644     "&lt;/div&gt;&lt;hr /&gt;"
28645 );
28646
28647 var moreView = new Roo.JsonView({
28648     container :  "entry-list", 
28649     template : tpl,
28650     jsonRoot: "posts"
28651 });
28652 moreView.on("beforerender", this.sortEntries, this);
28653 moreView.load({
28654     url: "/blog/get-posts.php",
28655     params: "allposts=true",
28656     text: "Loading Blog Entries..."
28657 });
28658 </code></pre>
28659
28660 * Note: old code is supported with arguments : (container, template, config)
28661
28662
28663  * @constructor
28664  * Create a new JsonView
28665  * 
28666  * @param {Object} config The config object
28667  * 
28668  */
28669 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28670     
28671     
28672     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28673
28674     var um = this.el.getUpdateManager();
28675     um.setRenderer(this);
28676     um.on("update", this.onLoad, this);
28677     um.on("failure", this.onLoadException, this);
28678
28679     /**
28680      * @event beforerender
28681      * Fires before rendering of the downloaded JSON data.
28682      * @param {Roo.JsonView} this
28683      * @param {Object} data The JSON data loaded
28684      */
28685     /**
28686      * @event load
28687      * Fires when data is loaded.
28688      * @param {Roo.JsonView} this
28689      * @param {Object} data The JSON data loaded
28690      * @param {Object} response The raw Connect response object
28691      */
28692     /**
28693      * @event loadexception
28694      * Fires when loading fails.
28695      * @param {Roo.JsonView} this
28696      * @param {Object} response The raw Connect response object
28697      */
28698     this.addEvents({
28699         'beforerender' : true,
28700         'load' : true,
28701         'loadexception' : true
28702     });
28703 };
28704 Roo.extend(Roo.JsonView, Roo.View, {
28705     /**
28706      * @type {String} The root property in the loaded JSON object that contains the data
28707      */
28708     jsonRoot : "",
28709
28710     /**
28711      * Refreshes the view.
28712      */
28713     refresh : function(){
28714         this.clearSelections();
28715         this.el.update("");
28716         var html = [];
28717         var o = this.jsonData;
28718         if(o && o.length > 0){
28719             for(var i = 0, len = o.length; i < len; i++){
28720                 var data = this.prepareData(o[i], i, o);
28721                 html[html.length] = this.tpl.apply(data);
28722             }
28723         }else{
28724             html.push(this.emptyText);
28725         }
28726         this.el.update(html.join(""));
28727         this.nodes = this.el.dom.childNodes;
28728         this.updateIndexes(0);
28729     },
28730
28731     /**
28732      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
28733      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
28734      <pre><code>
28735      view.load({
28736          url: "your-url.php",
28737          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28738          callback: yourFunction,
28739          scope: yourObject, //(optional scope)
28740          discardUrl: false,
28741          nocache: false,
28742          text: "Loading...",
28743          timeout: 30,
28744          scripts: false
28745      });
28746      </code></pre>
28747      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28748      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
28749      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
28750      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28751      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
28752      */
28753     load : function(){
28754         var um = this.el.getUpdateManager();
28755         um.update.apply(um, arguments);
28756     },
28757
28758     // note - render is a standard framework call...
28759     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28760     render : function(el, response){
28761         
28762         this.clearSelections();
28763         this.el.update("");
28764         var o;
28765         try{
28766             if (response != '') {
28767                 o = Roo.util.JSON.decode(response.responseText);
28768                 if(this.jsonRoot){
28769                     
28770                     o = o[this.jsonRoot];
28771                 }
28772             }
28773         } catch(e){
28774         }
28775         /**
28776          * The current JSON data or null
28777          */
28778         this.jsonData = o;
28779         this.beforeRender();
28780         this.refresh();
28781     },
28782
28783 /**
28784  * Get the number of records in the current JSON dataset
28785  * @return {Number}
28786  */
28787     getCount : function(){
28788         return this.jsonData ? this.jsonData.length : 0;
28789     },
28790
28791 /**
28792  * Returns the JSON object for the specified node(s)
28793  * @param {HTMLElement/Array} node The node or an array of nodes
28794  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28795  * you get the JSON object for the node
28796  */
28797     getNodeData : function(node){
28798         if(node instanceof Array){
28799             var data = [];
28800             for(var i = 0, len = node.length; i < len; i++){
28801                 data.push(this.getNodeData(node[i]));
28802             }
28803             return data;
28804         }
28805         return this.jsonData[this.indexOf(node)] || null;
28806     },
28807
28808     beforeRender : function(){
28809         this.snapshot = this.jsonData;
28810         if(this.sortInfo){
28811             this.sort.apply(this, this.sortInfo);
28812         }
28813         this.fireEvent("beforerender", this, this.jsonData);
28814     },
28815
28816     onLoad : function(el, o){
28817         this.fireEvent("load", this, this.jsonData, o);
28818     },
28819
28820     onLoadException : function(el, o){
28821         this.fireEvent("loadexception", this, o);
28822     },
28823
28824 /**
28825  * Filter the data by a specific property.
28826  * @param {String} property A property on your JSON objects
28827  * @param {String/RegExp} value Either string that the property values
28828  * should start with, or a RegExp to test against the property
28829  */
28830     filter : function(property, value){
28831         if(this.jsonData){
28832             var data = [];
28833             var ss = this.snapshot;
28834             if(typeof value == "string"){
28835                 var vlen = value.length;
28836                 if(vlen == 0){
28837                     this.clearFilter();
28838                     return;
28839                 }
28840                 value = value.toLowerCase();
28841                 for(var i = 0, len = ss.length; i < len; i++){
28842                     var o = ss[i];
28843                     if(o[property].substr(0, vlen).toLowerCase() == value){
28844                         data.push(o);
28845                     }
28846                 }
28847             } else if(value.exec){ // regex?
28848                 for(var i = 0, len = ss.length; i < len; i++){
28849                     var o = ss[i];
28850                     if(value.test(o[property])){
28851                         data.push(o);
28852                     }
28853                 }
28854             } else{
28855                 return;
28856             }
28857             this.jsonData = data;
28858             this.refresh();
28859         }
28860     },
28861
28862 /**
28863  * Filter by a function. The passed function will be called with each
28864  * object in the current dataset. If the function returns true the value is kept,
28865  * otherwise it is filtered.
28866  * @param {Function} fn
28867  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28868  */
28869     filterBy : function(fn, scope){
28870         if(this.jsonData){
28871             var data = [];
28872             var ss = this.snapshot;
28873             for(var i = 0, len = ss.length; i < len; i++){
28874                 var o = ss[i];
28875                 if(fn.call(scope || this, o)){
28876                     data.push(o);
28877                 }
28878             }
28879             this.jsonData = data;
28880             this.refresh();
28881         }
28882     },
28883
28884 /**
28885  * Clears the current filter.
28886  */
28887     clearFilter : function(){
28888         if(this.snapshot && this.jsonData != this.snapshot){
28889             this.jsonData = this.snapshot;
28890             this.refresh();
28891         }
28892     },
28893
28894
28895 /**
28896  * Sorts the data for this view and refreshes it.
28897  * @param {String} property A property on your JSON objects to sort on
28898  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28899  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28900  */
28901     sort : function(property, dir, sortType){
28902         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28903         if(this.jsonData){
28904             var p = property;
28905             var dsc = dir && dir.toLowerCase() == "desc";
28906             var f = function(o1, o2){
28907                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28908                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28909                 ;
28910                 if(v1 < v2){
28911                     return dsc ? +1 : -1;
28912                 } else if(v1 > v2){
28913                     return dsc ? -1 : +1;
28914                 } else{
28915                     return 0;
28916                 }
28917             };
28918             this.jsonData.sort(f);
28919             this.refresh();
28920             if(this.jsonData != this.snapshot){
28921                 this.snapshot.sort(f);
28922             }
28923         }
28924     }
28925 });/*
28926  * Based on:
28927  * Ext JS Library 1.1.1
28928  * Copyright(c) 2006-2007, Ext JS, LLC.
28929  *
28930  * Originally Released Under LGPL - original licence link has changed is not relivant.
28931  *
28932  * Fork - LGPL
28933  * <script type="text/javascript">
28934  */
28935  
28936
28937 /**
28938  * @class Roo.ColorPalette
28939  * @extends Roo.Component
28940  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28941  * Here's an example of typical usage:
28942  * <pre><code>
28943 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28944 cp.render('my-div');
28945
28946 cp.on('select', function(palette, selColor){
28947     // do something with selColor
28948 });
28949 </code></pre>
28950  * @constructor
28951  * Create a new ColorPalette
28952  * @param {Object} config The config object
28953  */
28954 Roo.ColorPalette = function(config){
28955     Roo.ColorPalette.superclass.constructor.call(this, config);
28956     this.addEvents({
28957         /**
28958              * @event select
28959              * Fires when a color is selected
28960              * @param {ColorPalette} this
28961              * @param {String} color The 6-digit color hex code (without the # symbol)
28962              */
28963         select: true
28964     });
28965
28966     if(this.handler){
28967         this.on("select", this.handler, this.scope, true);
28968     }
28969 };
28970 Roo.extend(Roo.ColorPalette, Roo.Component, {
28971     /**
28972      * @cfg {String} itemCls
28973      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28974      */
28975     itemCls : "x-color-palette",
28976     /**
28977      * @cfg {String} value
28978      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28979      * the hex codes are case-sensitive.
28980      */
28981     value : null,
28982     clickEvent:'click',
28983     // private
28984     ctype: "Roo.ColorPalette",
28985
28986     /**
28987      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28988      */
28989     allowReselect : false,
28990
28991     /**
28992      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28993      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28994      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28995      * of colors with the width setting until the box is symmetrical.</p>
28996      * <p>You can override individual colors if needed:</p>
28997      * <pre><code>
28998 var cp = new Roo.ColorPalette();
28999 cp.colors[0] = "FF0000";  // change the first box to red
29000 </code></pre>
29001
29002 Or you can provide a custom array of your own for complete control:
29003 <pre><code>
29004 var cp = new Roo.ColorPalette();
29005 cp.colors = ["000000", "993300", "333300"];
29006 </code></pre>
29007      * @type Array
29008      */
29009     colors : [
29010         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29011         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29012         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29013         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29014         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29015     ],
29016
29017     // private
29018     onRender : function(container, position){
29019         var t = new Roo.MasterTemplate(
29020             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29021         );
29022         var c = this.colors;
29023         for(var i = 0, len = c.length; i < len; i++){
29024             t.add([c[i]]);
29025         }
29026         var el = document.createElement("div");
29027         el.className = this.itemCls;
29028         t.overwrite(el);
29029         container.dom.insertBefore(el, position);
29030         this.el = Roo.get(el);
29031         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29032         if(this.clickEvent != 'click'){
29033             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29034         }
29035     },
29036
29037     // private
29038     afterRender : function(){
29039         Roo.ColorPalette.superclass.afterRender.call(this);
29040         if(this.value){
29041             var s = this.value;
29042             this.value = null;
29043             this.select(s);
29044         }
29045     },
29046
29047     // private
29048     handleClick : function(e, t){
29049         e.preventDefault();
29050         if(!this.disabled){
29051             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29052             this.select(c.toUpperCase());
29053         }
29054     },
29055
29056     /**
29057      * Selects the specified color in the palette (fires the select event)
29058      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29059      */
29060     select : function(color){
29061         color = color.replace("#", "");
29062         if(color != this.value || this.allowReselect){
29063             var el = this.el;
29064             if(this.value){
29065                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29066             }
29067             el.child("a.color-"+color).addClass("x-color-palette-sel");
29068             this.value = color;
29069             this.fireEvent("select", this, color);
29070         }
29071     }
29072 });/*
29073  * Based on:
29074  * Ext JS Library 1.1.1
29075  * Copyright(c) 2006-2007, Ext JS, LLC.
29076  *
29077  * Originally Released Under LGPL - original licence link has changed is not relivant.
29078  *
29079  * Fork - LGPL
29080  * <script type="text/javascript">
29081  */
29082  
29083 /**
29084  * @class Roo.DatePicker
29085  * @extends Roo.Component
29086  * Simple date picker class.
29087  * @constructor
29088  * Create a new DatePicker
29089  * @param {Object} config The config object
29090  */
29091 Roo.DatePicker = function(config){
29092     Roo.DatePicker.superclass.constructor.call(this, config);
29093
29094     this.value = config && config.value ?
29095                  config.value.clearTime() : new Date().clearTime();
29096
29097     this.addEvents({
29098         /**
29099              * @event select
29100              * Fires when a date is selected
29101              * @param {DatePicker} this
29102              * @param {Date} date The selected date
29103              */
29104         'select': true,
29105         /**
29106              * @event monthchange
29107              * Fires when the displayed month changes 
29108              * @param {DatePicker} this
29109              * @param {Date} date The selected month
29110              */
29111         'monthchange': true
29112     });
29113
29114     if(this.handler){
29115         this.on("select", this.handler,  this.scope || this);
29116     }
29117     // build the disabledDatesRE
29118     if(!this.disabledDatesRE && this.disabledDates){
29119         var dd = this.disabledDates;
29120         var re = "(?:";
29121         for(var i = 0; i < dd.length; i++){
29122             re += dd[i];
29123             if(i != dd.length-1) {
29124                 re += "|";
29125             }
29126         }
29127         this.disabledDatesRE = new RegExp(re + ")");
29128     }
29129 };
29130
29131 Roo.extend(Roo.DatePicker, Roo.Component, {
29132     /**
29133      * @cfg {String} todayText
29134      * The text to display on the button that selects the current date (defaults to "Today")
29135      */
29136     todayText : "Today",
29137     /**
29138      * @cfg {String} okText
29139      * The text to display on the ok button
29140      */
29141     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29142     /**
29143      * @cfg {String} cancelText
29144      * The text to display on the cancel button
29145      */
29146     cancelText : "Cancel",
29147     /**
29148      * @cfg {String} todayTip
29149      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29150      */
29151     todayTip : "{0} (Spacebar)",
29152     /**
29153      * @cfg {Date} minDate
29154      * Minimum allowable date (JavaScript date object, defaults to null)
29155      */
29156     minDate : null,
29157     /**
29158      * @cfg {Date} maxDate
29159      * Maximum allowable date (JavaScript date object, defaults to null)
29160      */
29161     maxDate : null,
29162     /**
29163      * @cfg {String} minText
29164      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29165      */
29166     minText : "This date is before the minimum date",
29167     /**
29168      * @cfg {String} maxText
29169      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29170      */
29171     maxText : "This date is after the maximum date",
29172     /**
29173      * @cfg {String} format
29174      * The default date format string which can be overriden for localization support.  The format must be
29175      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29176      */
29177     format : "m/d/y",
29178     /**
29179      * @cfg {Array} disabledDays
29180      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29181      */
29182     disabledDays : null,
29183     /**
29184      * @cfg {String} disabledDaysText
29185      * The tooltip to display when the date falls on a disabled day (defaults to "")
29186      */
29187     disabledDaysText : "",
29188     /**
29189      * @cfg {RegExp} disabledDatesRE
29190      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29191      */
29192     disabledDatesRE : null,
29193     /**
29194      * @cfg {String} disabledDatesText
29195      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29196      */
29197     disabledDatesText : "",
29198     /**
29199      * @cfg {Boolean} constrainToViewport
29200      * True to constrain the date picker to the viewport (defaults to true)
29201      */
29202     constrainToViewport : true,
29203     /**
29204      * @cfg {Array} monthNames
29205      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29206      */
29207     monthNames : Date.monthNames,
29208     /**
29209      * @cfg {Array} dayNames
29210      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29211      */
29212     dayNames : Date.dayNames,
29213     /**
29214      * @cfg {String} nextText
29215      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29216      */
29217     nextText: 'Next Month (Control+Right)',
29218     /**
29219      * @cfg {String} prevText
29220      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29221      */
29222     prevText: 'Previous Month (Control+Left)',
29223     /**
29224      * @cfg {String} monthYearText
29225      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29226      */
29227     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29228     /**
29229      * @cfg {Number} startDay
29230      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29231      */
29232     startDay : 0,
29233     /**
29234      * @cfg {Bool} showClear
29235      * Show a clear button (usefull for date form elements that can be blank.)
29236      */
29237     
29238     showClear: false,
29239     
29240     /**
29241      * Sets the value of the date field
29242      * @param {Date} value The date to set
29243      */
29244     setValue : function(value){
29245         var old = this.value;
29246         
29247         if (typeof(value) == 'string') {
29248          
29249             value = Date.parseDate(value, this.format);
29250         }
29251         if (!value) {
29252             value = new Date();
29253         }
29254         
29255         this.value = value.clearTime(true);
29256         if(this.el){
29257             this.update(this.value);
29258         }
29259     },
29260
29261     /**
29262      * Gets the current selected value of the date field
29263      * @return {Date} The selected date
29264      */
29265     getValue : function(){
29266         return this.value;
29267     },
29268
29269     // private
29270     focus : function(){
29271         if(this.el){
29272             this.update(this.activeDate);
29273         }
29274     },
29275
29276     // privateval
29277     onRender : function(container, position){
29278         
29279         var m = [
29280              '<table cellspacing="0">',
29281                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
29282                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29283         var dn = this.dayNames;
29284         for(var i = 0; i < 7; i++){
29285             var d = this.startDay+i;
29286             if(d > 6){
29287                 d = d-7;
29288             }
29289             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29290         }
29291         m[m.length] = "</tr></thead><tbody><tr>";
29292         for(var i = 0; i < 42; i++) {
29293             if(i % 7 == 0 && i != 0){
29294                 m[m.length] = "</tr><tr>";
29295             }
29296             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29297         }
29298         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29299             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29300
29301         var el = document.createElement("div");
29302         el.className = "x-date-picker";
29303         el.innerHTML = m.join("");
29304
29305         container.dom.insertBefore(el, position);
29306
29307         this.el = Roo.get(el);
29308         this.eventEl = Roo.get(el.firstChild);
29309
29310         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29311             handler: this.showPrevMonth,
29312             scope: this,
29313             preventDefault:true,
29314             stopDefault:true
29315         });
29316
29317         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29318             handler: this.showNextMonth,
29319             scope: this,
29320             preventDefault:true,
29321             stopDefault:true
29322         });
29323
29324         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29325
29326         this.monthPicker = this.el.down('div.x-date-mp');
29327         this.monthPicker.enableDisplayMode('block');
29328         
29329         var kn = new Roo.KeyNav(this.eventEl, {
29330             "left" : function(e){
29331                 e.ctrlKey ?
29332                     this.showPrevMonth() :
29333                     this.update(this.activeDate.add("d", -1));
29334             },
29335
29336             "right" : function(e){
29337                 e.ctrlKey ?
29338                     this.showNextMonth() :
29339                     this.update(this.activeDate.add("d", 1));
29340             },
29341
29342             "up" : function(e){
29343                 e.ctrlKey ?
29344                     this.showNextYear() :
29345                     this.update(this.activeDate.add("d", -7));
29346             },
29347
29348             "down" : function(e){
29349                 e.ctrlKey ?
29350                     this.showPrevYear() :
29351                     this.update(this.activeDate.add("d", 7));
29352             },
29353
29354             "pageUp" : function(e){
29355                 this.showNextMonth();
29356             },
29357
29358             "pageDown" : function(e){
29359                 this.showPrevMonth();
29360             },
29361
29362             "enter" : function(e){
29363                 e.stopPropagation();
29364                 return true;
29365             },
29366
29367             scope : this
29368         });
29369
29370         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29371
29372         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29373
29374         this.el.unselectable();
29375         
29376         this.cells = this.el.select("table.x-date-inner tbody td");
29377         this.textNodes = this.el.query("table.x-date-inner tbody span");
29378
29379         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29380             text: "&#160;",
29381             tooltip: this.monthYearText
29382         });
29383
29384         this.mbtn.on('click', this.showMonthPicker, this);
29385         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29386
29387
29388         var today = (new Date()).dateFormat(this.format);
29389         
29390         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29391         if (this.showClear) {
29392             baseTb.add( new Roo.Toolbar.Fill());
29393         }
29394         baseTb.add({
29395             text: String.format(this.todayText, today),
29396             tooltip: String.format(this.todayTip, today),
29397             handler: this.selectToday,
29398             scope: this
29399         });
29400         
29401         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29402             
29403         //});
29404         if (this.showClear) {
29405             
29406             baseTb.add( new Roo.Toolbar.Fill());
29407             baseTb.add({
29408                 text: '&#160;',
29409                 cls: 'x-btn-icon x-btn-clear',
29410                 handler: function() {
29411                     //this.value = '';
29412                     this.fireEvent("select", this, '');
29413                 },
29414                 scope: this
29415             });
29416         }
29417         
29418         
29419         if(Roo.isIE){
29420             this.el.repaint();
29421         }
29422         this.update(this.value);
29423     },
29424
29425     createMonthPicker : function(){
29426         if(!this.monthPicker.dom.firstChild){
29427             var buf = ['<table border="0" cellspacing="0">'];
29428             for(var i = 0; i < 6; i++){
29429                 buf.push(
29430                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29431                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29432                     i == 0 ?
29433                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
29434                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29435                 );
29436             }
29437             buf.push(
29438                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29439                     this.okText,
29440                     '</button><button type="button" class="x-date-mp-cancel">',
29441                     this.cancelText,
29442                     '</button></td></tr>',
29443                 '</table>'
29444             );
29445             this.monthPicker.update(buf.join(''));
29446             this.monthPicker.on('click', this.onMonthClick, this);
29447             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29448
29449             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29450             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29451
29452             this.mpMonths.each(function(m, a, i){
29453                 i += 1;
29454                 if((i%2) == 0){
29455                     m.dom.xmonth = 5 + Math.round(i * .5);
29456                 }else{
29457                     m.dom.xmonth = Math.round((i-1) * .5);
29458                 }
29459             });
29460         }
29461     },
29462
29463     showMonthPicker : function(){
29464         this.createMonthPicker();
29465         var size = this.el.getSize();
29466         this.monthPicker.setSize(size);
29467         this.monthPicker.child('table').setSize(size);
29468
29469         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29470         this.updateMPMonth(this.mpSelMonth);
29471         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29472         this.updateMPYear(this.mpSelYear);
29473
29474         this.monthPicker.slideIn('t', {duration:.2});
29475     },
29476
29477     updateMPYear : function(y){
29478         this.mpyear = y;
29479         var ys = this.mpYears.elements;
29480         for(var i = 1; i <= 10; i++){
29481             var td = ys[i-1], y2;
29482             if((i%2) == 0){
29483                 y2 = y + Math.round(i * .5);
29484                 td.firstChild.innerHTML = y2;
29485                 td.xyear = y2;
29486             }else{
29487                 y2 = y - (5-Math.round(i * .5));
29488                 td.firstChild.innerHTML = y2;
29489                 td.xyear = y2;
29490             }
29491             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29492         }
29493     },
29494
29495     updateMPMonth : function(sm){
29496         this.mpMonths.each(function(m, a, i){
29497             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29498         });
29499     },
29500
29501     selectMPMonth: function(m){
29502         
29503     },
29504
29505     onMonthClick : function(e, t){
29506         e.stopEvent();
29507         var el = new Roo.Element(t), pn;
29508         if(el.is('button.x-date-mp-cancel')){
29509             this.hideMonthPicker();
29510         }
29511         else if(el.is('button.x-date-mp-ok')){
29512             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29513             this.hideMonthPicker();
29514         }
29515         else if(pn = el.up('td.x-date-mp-month', 2)){
29516             this.mpMonths.removeClass('x-date-mp-sel');
29517             pn.addClass('x-date-mp-sel');
29518             this.mpSelMonth = pn.dom.xmonth;
29519         }
29520         else if(pn = el.up('td.x-date-mp-year', 2)){
29521             this.mpYears.removeClass('x-date-mp-sel');
29522             pn.addClass('x-date-mp-sel');
29523             this.mpSelYear = pn.dom.xyear;
29524         }
29525         else if(el.is('a.x-date-mp-prev')){
29526             this.updateMPYear(this.mpyear-10);
29527         }
29528         else if(el.is('a.x-date-mp-next')){
29529             this.updateMPYear(this.mpyear+10);
29530         }
29531     },
29532
29533     onMonthDblClick : function(e, t){
29534         e.stopEvent();
29535         var el = new Roo.Element(t), pn;
29536         if(pn = el.up('td.x-date-mp-month', 2)){
29537             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29538             this.hideMonthPicker();
29539         }
29540         else if(pn = el.up('td.x-date-mp-year', 2)){
29541             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29542             this.hideMonthPicker();
29543         }
29544     },
29545
29546     hideMonthPicker : function(disableAnim){
29547         if(this.monthPicker){
29548             if(disableAnim === true){
29549                 this.monthPicker.hide();
29550             }else{
29551                 this.monthPicker.slideOut('t', {duration:.2});
29552             }
29553         }
29554     },
29555
29556     // private
29557     showPrevMonth : function(e){
29558         this.update(this.activeDate.add("mo", -1));
29559     },
29560
29561     // private
29562     showNextMonth : function(e){
29563         this.update(this.activeDate.add("mo", 1));
29564     },
29565
29566     // private
29567     showPrevYear : function(){
29568         this.update(this.activeDate.add("y", -1));
29569     },
29570
29571     // private
29572     showNextYear : function(){
29573         this.update(this.activeDate.add("y", 1));
29574     },
29575
29576     // private
29577     handleMouseWheel : function(e){
29578         var delta = e.getWheelDelta();
29579         if(delta > 0){
29580             this.showPrevMonth();
29581             e.stopEvent();
29582         } else if(delta < 0){
29583             this.showNextMonth();
29584             e.stopEvent();
29585         }
29586     },
29587
29588     // private
29589     handleDateClick : function(e, t){
29590         e.stopEvent();
29591         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29592             this.setValue(new Date(t.dateValue));
29593             this.fireEvent("select", this, this.value);
29594         }
29595     },
29596
29597     // private
29598     selectToday : function(){
29599         this.setValue(new Date().clearTime());
29600         this.fireEvent("select", this, this.value);
29601     },
29602
29603     // private
29604     update : function(date)
29605     {
29606         var vd = this.activeDate;
29607         this.activeDate = date;
29608         if(vd && this.el){
29609             var t = date.getTime();
29610             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29611                 this.cells.removeClass("x-date-selected");
29612                 this.cells.each(function(c){
29613                    if(c.dom.firstChild.dateValue == t){
29614                        c.addClass("x-date-selected");
29615                        setTimeout(function(){
29616                             try{c.dom.firstChild.focus();}catch(e){}
29617                        }, 50);
29618                        return false;
29619                    }
29620                 });
29621                 return;
29622             }
29623         }
29624         
29625         var days = date.getDaysInMonth();
29626         var firstOfMonth = date.getFirstDateOfMonth();
29627         var startingPos = firstOfMonth.getDay()-this.startDay;
29628
29629         if(startingPos <= this.startDay){
29630             startingPos += 7;
29631         }
29632
29633         var pm = date.add("mo", -1);
29634         var prevStart = pm.getDaysInMonth()-startingPos;
29635
29636         var cells = this.cells.elements;
29637         var textEls = this.textNodes;
29638         days += startingPos;
29639
29640         // convert everything to numbers so it's fast
29641         var day = 86400000;
29642         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29643         var today = new Date().clearTime().getTime();
29644         var sel = date.clearTime().getTime();
29645         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29646         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29647         var ddMatch = this.disabledDatesRE;
29648         var ddText = this.disabledDatesText;
29649         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29650         var ddaysText = this.disabledDaysText;
29651         var format = this.format;
29652
29653         var setCellClass = function(cal, cell){
29654             cell.title = "";
29655             var t = d.getTime();
29656             cell.firstChild.dateValue = t;
29657             if(t == today){
29658                 cell.className += " x-date-today";
29659                 cell.title = cal.todayText;
29660             }
29661             if(t == sel){
29662                 cell.className += " x-date-selected";
29663                 setTimeout(function(){
29664                     try{cell.firstChild.focus();}catch(e){}
29665                 }, 50);
29666             }
29667             // disabling
29668             if(t < min) {
29669                 cell.className = " x-date-disabled";
29670                 cell.title = cal.minText;
29671                 return;
29672             }
29673             if(t > max) {
29674                 cell.className = " x-date-disabled";
29675                 cell.title = cal.maxText;
29676                 return;
29677             }
29678             if(ddays){
29679                 if(ddays.indexOf(d.getDay()) != -1){
29680                     cell.title = ddaysText;
29681                     cell.className = " x-date-disabled";
29682                 }
29683             }
29684             if(ddMatch && format){
29685                 var fvalue = d.dateFormat(format);
29686                 if(ddMatch.test(fvalue)){
29687                     cell.title = ddText.replace("%0", fvalue);
29688                     cell.className = " x-date-disabled";
29689                 }
29690             }
29691         };
29692
29693         var i = 0;
29694         for(; i < startingPos; i++) {
29695             textEls[i].innerHTML = (++prevStart);
29696             d.setDate(d.getDate()+1);
29697             cells[i].className = "x-date-prevday";
29698             setCellClass(this, cells[i]);
29699         }
29700         for(; i < days; i++){
29701             intDay = i - startingPos + 1;
29702             textEls[i].innerHTML = (intDay);
29703             d.setDate(d.getDate()+1);
29704             cells[i].className = "x-date-active";
29705             setCellClass(this, cells[i]);
29706         }
29707         var extraDays = 0;
29708         for(; i < 42; i++) {
29709              textEls[i].innerHTML = (++extraDays);
29710              d.setDate(d.getDate()+1);
29711              cells[i].className = "x-date-nextday";
29712              setCellClass(this, cells[i]);
29713         }
29714
29715         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29716         this.fireEvent('monthchange', this, date);
29717         
29718         if(!this.internalRender){
29719             var main = this.el.dom.firstChild;
29720             var w = main.offsetWidth;
29721             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29722             Roo.fly(main).setWidth(w);
29723             this.internalRender = true;
29724             // opera does not respect the auto grow header center column
29725             // then, after it gets a width opera refuses to recalculate
29726             // without a second pass
29727             if(Roo.isOpera && !this.secondPass){
29728                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29729                 this.secondPass = true;
29730                 this.update.defer(10, this, [date]);
29731             }
29732         }
29733         
29734         
29735     }
29736 });        /*
29737  * Based on:
29738  * Ext JS Library 1.1.1
29739  * Copyright(c) 2006-2007, Ext JS, LLC.
29740  *
29741  * Originally Released Under LGPL - original licence link has changed is not relivant.
29742  *
29743  * Fork - LGPL
29744  * <script type="text/javascript">
29745  */
29746 /**
29747  * @class Roo.TabPanel
29748  * @extends Roo.util.Observable
29749  * A lightweight tab container.
29750  * <br><br>
29751  * Usage:
29752  * <pre><code>
29753 // basic tabs 1, built from existing content
29754 var tabs = new Roo.TabPanel("tabs1");
29755 tabs.addTab("script", "View Script");
29756 tabs.addTab("markup", "View Markup");
29757 tabs.activate("script");
29758
29759 // more advanced tabs, built from javascript
29760 var jtabs = new Roo.TabPanel("jtabs");
29761 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29762
29763 // set up the UpdateManager
29764 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29765 var updater = tab2.getUpdateManager();
29766 updater.setDefaultUrl("ajax1.htm");
29767 tab2.on('activate', updater.refresh, updater, true);
29768
29769 // Use setUrl for Ajax loading
29770 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29771 tab3.setUrl("ajax2.htm", null, true);
29772
29773 // Disabled tab
29774 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29775 tab4.disable();
29776
29777 jtabs.activate("jtabs-1");
29778  * </code></pre>
29779  * @constructor
29780  * Create a new TabPanel.
29781  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29782  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29783  */
29784 Roo.TabPanel = function(container, config){
29785     /**
29786     * The container element for this TabPanel.
29787     * @type Roo.Element
29788     */
29789     this.el = Roo.get(container, true);
29790     if(config){
29791         if(typeof config == "boolean"){
29792             this.tabPosition = config ? "bottom" : "top";
29793         }else{
29794             Roo.apply(this, config);
29795         }
29796     }
29797     if(this.tabPosition == "bottom"){
29798         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29799         this.el.addClass("x-tabs-bottom");
29800     }
29801     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29802     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29803     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29804     if(Roo.isIE){
29805         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29806     }
29807     if(this.tabPosition != "bottom"){
29808         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29809          * @type Roo.Element
29810          */
29811         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29812         this.el.addClass("x-tabs-top");
29813     }
29814     this.items = [];
29815
29816     this.bodyEl.setStyle("position", "relative");
29817
29818     this.active = null;
29819     this.activateDelegate = this.activate.createDelegate(this);
29820
29821     this.addEvents({
29822         /**
29823          * @event tabchange
29824          * Fires when the active tab changes
29825          * @param {Roo.TabPanel} this
29826          * @param {Roo.TabPanelItem} activePanel The new active tab
29827          */
29828         "tabchange": true,
29829         /**
29830          * @event beforetabchange
29831          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29832          * @param {Roo.TabPanel} this
29833          * @param {Object} e Set cancel to true on this object to cancel the tab change
29834          * @param {Roo.TabPanelItem} tab The tab being changed to
29835          */
29836         "beforetabchange" : true
29837     });
29838
29839     Roo.EventManager.onWindowResize(this.onResize, this);
29840     this.cpad = this.el.getPadding("lr");
29841     this.hiddenCount = 0;
29842
29843
29844     // toolbar on the tabbar support...
29845     if (this.toolbar) {
29846         var tcfg = this.toolbar;
29847         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29848         this.toolbar = new Roo.Toolbar(tcfg);
29849         if (Roo.isSafari) {
29850             var tbl = tcfg.container.child('table', true);
29851             tbl.setAttribute('width', '100%');
29852         }
29853         
29854     }
29855    
29856
29857
29858     Roo.TabPanel.superclass.constructor.call(this);
29859 };
29860
29861 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29862     /*
29863      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29864      */
29865     tabPosition : "top",
29866     /*
29867      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29868      */
29869     currentTabWidth : 0,
29870     /*
29871      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29872      */
29873     minTabWidth : 40,
29874     /*
29875      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29876      */
29877     maxTabWidth : 250,
29878     /*
29879      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29880      */
29881     preferredTabWidth : 175,
29882     /*
29883      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29884      */
29885     resizeTabs : false,
29886     /*
29887      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29888      */
29889     monitorResize : true,
29890     /*
29891      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29892      */
29893     toolbar : false,
29894
29895     /**
29896      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29897      * @param {String} id The id of the div to use <b>or create</b>
29898      * @param {String} text The text for the tab
29899      * @param {String} content (optional) Content to put in the TabPanelItem body
29900      * @param {Boolean} closable (optional) True to create a close icon on the tab
29901      * @return {Roo.TabPanelItem} The created TabPanelItem
29902      */
29903     addTab : function(id, text, content, closable){
29904         var item = new Roo.TabPanelItem(this, id, text, closable);
29905         this.addTabItem(item);
29906         if(content){
29907             item.setContent(content);
29908         }
29909         return item;
29910     },
29911
29912     /**
29913      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29914      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29915      * @return {Roo.TabPanelItem}
29916      */
29917     getTab : function(id){
29918         return this.items[id];
29919     },
29920
29921     /**
29922      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29923      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29924      */
29925     hideTab : function(id){
29926         var t = this.items[id];
29927         if(!t.isHidden()){
29928            t.setHidden(true);
29929            this.hiddenCount++;
29930            this.autoSizeTabs();
29931         }
29932     },
29933
29934     /**
29935      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29936      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29937      */
29938     unhideTab : function(id){
29939         var t = this.items[id];
29940         if(t.isHidden()){
29941            t.setHidden(false);
29942            this.hiddenCount--;
29943            this.autoSizeTabs();
29944         }
29945     },
29946
29947     /**
29948      * Adds an existing {@link Roo.TabPanelItem}.
29949      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29950      */
29951     addTabItem : function(item){
29952         this.items[item.id] = item;
29953         this.items.push(item);
29954         if(this.resizeTabs){
29955            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29956            this.autoSizeTabs();
29957         }else{
29958             item.autoSize();
29959         }
29960     },
29961
29962     /**
29963      * Removes a {@link Roo.TabPanelItem}.
29964      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29965      */
29966     removeTab : function(id){
29967         var items = this.items;
29968         var tab = items[id];
29969         if(!tab) { return; }
29970         var index = items.indexOf(tab);
29971         if(this.active == tab && items.length > 1){
29972             var newTab = this.getNextAvailable(index);
29973             if(newTab) {
29974                 newTab.activate();
29975             }
29976         }
29977         this.stripEl.dom.removeChild(tab.pnode.dom);
29978         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29979             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29980         }
29981         items.splice(index, 1);
29982         delete this.items[tab.id];
29983         tab.fireEvent("close", tab);
29984         tab.purgeListeners();
29985         this.autoSizeTabs();
29986     },
29987
29988     getNextAvailable : function(start){
29989         var items = this.items;
29990         var index = start;
29991         // look for a next tab that will slide over to
29992         // replace the one being removed
29993         while(index < items.length){
29994             var item = items[++index];
29995             if(item && !item.isHidden()){
29996                 return item;
29997             }
29998         }
29999         // if one isn't found select the previous tab (on the left)
30000         index = start;
30001         while(index >= 0){
30002             var item = items[--index];
30003             if(item && !item.isHidden()){
30004                 return item;
30005             }
30006         }
30007         return null;
30008     },
30009
30010     /**
30011      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30012      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30013      */
30014     disableTab : function(id){
30015         var tab = this.items[id];
30016         if(tab && this.active != tab){
30017             tab.disable();
30018         }
30019     },
30020
30021     /**
30022      * Enables a {@link Roo.TabPanelItem} that is disabled.
30023      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30024      */
30025     enableTab : function(id){
30026         var tab = this.items[id];
30027         tab.enable();
30028     },
30029
30030     /**
30031      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30032      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30033      * @return {Roo.TabPanelItem} The TabPanelItem.
30034      */
30035     activate : function(id){
30036         var tab = this.items[id];
30037         if(!tab){
30038             return null;
30039         }
30040         if(tab == this.active || tab.disabled){
30041             return tab;
30042         }
30043         var e = {};
30044         this.fireEvent("beforetabchange", this, e, tab);
30045         if(e.cancel !== true && !tab.disabled){
30046             if(this.active){
30047                 this.active.hide();
30048             }
30049             this.active = this.items[id];
30050             this.active.show();
30051             this.fireEvent("tabchange", this, this.active);
30052         }
30053         return tab;
30054     },
30055
30056     /**
30057      * Gets the active {@link Roo.TabPanelItem}.
30058      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30059      */
30060     getActiveTab : function(){
30061         return this.active;
30062     },
30063
30064     /**
30065      * Updates the tab body element to fit the height of the container element
30066      * for overflow scrolling
30067      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30068      */
30069     syncHeight : function(targetHeight){
30070         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30071         var bm = this.bodyEl.getMargins();
30072         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30073         this.bodyEl.setHeight(newHeight);
30074         return newHeight;
30075     },
30076
30077     onResize : function(){
30078         if(this.monitorResize){
30079             this.autoSizeTabs();
30080         }
30081     },
30082
30083     /**
30084      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30085      */
30086     beginUpdate : function(){
30087         this.updating = true;
30088     },
30089
30090     /**
30091      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30092      */
30093     endUpdate : function(){
30094         this.updating = false;
30095         this.autoSizeTabs();
30096     },
30097
30098     /**
30099      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30100      */
30101     autoSizeTabs : function(){
30102         var count = this.items.length;
30103         var vcount = count - this.hiddenCount;
30104         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30105             return;
30106         }
30107         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30108         var availWidth = Math.floor(w / vcount);
30109         var b = this.stripBody;
30110         if(b.getWidth() > w){
30111             var tabs = this.items;
30112             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30113             if(availWidth < this.minTabWidth){
30114                 /*if(!this.sleft){    // incomplete scrolling code
30115                     this.createScrollButtons();
30116                 }
30117                 this.showScroll();
30118                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30119             }
30120         }else{
30121             if(this.currentTabWidth < this.preferredTabWidth){
30122                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30123             }
30124         }
30125     },
30126
30127     /**
30128      * Returns the number of tabs in this TabPanel.
30129      * @return {Number}
30130      */
30131      getCount : function(){
30132          return this.items.length;
30133      },
30134
30135     /**
30136      * Resizes all the tabs to the passed width
30137      * @param {Number} The new width
30138      */
30139     setTabWidth : function(width){
30140         this.currentTabWidth = width;
30141         for(var i = 0, len = this.items.length; i < len; i++) {
30142                 if(!this.items[i].isHidden()) {
30143                 this.items[i].setWidth(width);
30144             }
30145         }
30146     },
30147
30148     /**
30149      * Destroys this TabPanel
30150      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30151      */
30152     destroy : function(removeEl){
30153         Roo.EventManager.removeResizeListener(this.onResize, this);
30154         for(var i = 0, len = this.items.length; i < len; i++){
30155             this.items[i].purgeListeners();
30156         }
30157         if(removeEl === true){
30158             this.el.update("");
30159             this.el.remove();
30160         }
30161     }
30162 });
30163
30164 /**
30165  * @class Roo.TabPanelItem
30166  * @extends Roo.util.Observable
30167  * Represents an individual item (tab plus body) in a TabPanel.
30168  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30169  * @param {String} id The id of this TabPanelItem
30170  * @param {String} text The text for the tab of this TabPanelItem
30171  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30172  */
30173 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30174     /**
30175      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30176      * @type Roo.TabPanel
30177      */
30178     this.tabPanel = tabPanel;
30179     /**
30180      * The id for this TabPanelItem
30181      * @type String
30182      */
30183     this.id = id;
30184     /** @private */
30185     this.disabled = false;
30186     /** @private */
30187     this.text = text;
30188     /** @private */
30189     this.loaded = false;
30190     this.closable = closable;
30191
30192     /**
30193      * The body element for this TabPanelItem.
30194      * @type Roo.Element
30195      */
30196     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30197     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30198     this.bodyEl.setStyle("display", "block");
30199     this.bodyEl.setStyle("zoom", "1");
30200     this.hideAction();
30201
30202     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30203     /** @private */
30204     this.el = Roo.get(els.el, true);
30205     this.inner = Roo.get(els.inner, true);
30206     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30207     this.pnode = Roo.get(els.el.parentNode, true);
30208     this.el.on("mousedown", this.onTabMouseDown, this);
30209     this.el.on("click", this.onTabClick, this);
30210     /** @private */
30211     if(closable){
30212         var c = Roo.get(els.close, true);
30213         c.dom.title = this.closeText;
30214         c.addClassOnOver("close-over");
30215         c.on("click", this.closeClick, this);
30216      }
30217
30218     this.addEvents({
30219          /**
30220          * @event activate
30221          * Fires when this tab becomes the active tab.
30222          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30223          * @param {Roo.TabPanelItem} this
30224          */
30225         "activate": true,
30226         /**
30227          * @event beforeclose
30228          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30229          * @param {Roo.TabPanelItem} this
30230          * @param {Object} e Set cancel to true on this object to cancel the close.
30231          */
30232         "beforeclose": true,
30233         /**
30234          * @event close
30235          * Fires when this tab is closed.
30236          * @param {Roo.TabPanelItem} this
30237          */
30238          "close": true,
30239         /**
30240          * @event deactivate
30241          * Fires when this tab is no longer the active tab.
30242          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30243          * @param {Roo.TabPanelItem} this
30244          */
30245          "deactivate" : true
30246     });
30247     this.hidden = false;
30248
30249     Roo.TabPanelItem.superclass.constructor.call(this);
30250 };
30251
30252 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30253     purgeListeners : function(){
30254        Roo.util.Observable.prototype.purgeListeners.call(this);
30255        this.el.removeAllListeners();
30256     },
30257     /**
30258      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30259      */
30260     show : function(){
30261         this.pnode.addClass("on");
30262         this.showAction();
30263         if(Roo.isOpera){
30264             this.tabPanel.stripWrap.repaint();
30265         }
30266         this.fireEvent("activate", this.tabPanel, this);
30267     },
30268
30269     /**
30270      * Returns true if this tab is the active tab.
30271      * @return {Boolean}
30272      */
30273     isActive : function(){
30274         return this.tabPanel.getActiveTab() == this;
30275     },
30276
30277     /**
30278      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30279      */
30280     hide : function(){
30281         this.pnode.removeClass("on");
30282         this.hideAction();
30283         this.fireEvent("deactivate", this.tabPanel, this);
30284     },
30285
30286     hideAction : function(){
30287         this.bodyEl.hide();
30288         this.bodyEl.setStyle("position", "absolute");
30289         this.bodyEl.setLeft("-20000px");
30290         this.bodyEl.setTop("-20000px");
30291     },
30292
30293     showAction : function(){
30294         this.bodyEl.setStyle("position", "relative");
30295         this.bodyEl.setTop("");
30296         this.bodyEl.setLeft("");
30297         this.bodyEl.show();
30298     },
30299
30300     /**
30301      * Set the tooltip for the tab.
30302      * @param {String} tooltip The tab's tooltip
30303      */
30304     setTooltip : function(text){
30305         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30306             this.textEl.dom.qtip = text;
30307             this.textEl.dom.removeAttribute('title');
30308         }else{
30309             this.textEl.dom.title = text;
30310         }
30311     },
30312
30313     onTabClick : function(e){
30314         e.preventDefault();
30315         this.tabPanel.activate(this.id);
30316     },
30317
30318     onTabMouseDown : function(e){
30319         e.preventDefault();
30320         this.tabPanel.activate(this.id);
30321     },
30322
30323     getWidth : function(){
30324         return this.inner.getWidth();
30325     },
30326
30327     setWidth : function(width){
30328         var iwidth = width - this.pnode.getPadding("lr");
30329         this.inner.setWidth(iwidth);
30330         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30331         this.pnode.setWidth(width);
30332     },
30333
30334     /**
30335      * Show or hide the tab
30336      * @param {Boolean} hidden True to hide or false to show.
30337      */
30338     setHidden : function(hidden){
30339         this.hidden = hidden;
30340         this.pnode.setStyle("display", hidden ? "none" : "");
30341     },
30342
30343     /**
30344      * Returns true if this tab is "hidden"
30345      * @return {Boolean}
30346      */
30347     isHidden : function(){
30348         return this.hidden;
30349     },
30350
30351     /**
30352      * Returns the text for this tab
30353      * @return {String}
30354      */
30355     getText : function(){
30356         return this.text;
30357     },
30358
30359     autoSize : function(){
30360         //this.el.beginMeasure();
30361         this.textEl.setWidth(1);
30362         /*
30363          *  #2804 [new] Tabs in Roojs
30364          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30365          */
30366         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30367         //this.el.endMeasure();
30368     },
30369
30370     /**
30371      * Sets the text for the tab (Note: this also sets the tooltip text)
30372      * @param {String} text The tab's text and tooltip
30373      */
30374     setText : function(text){
30375         this.text = text;
30376         this.textEl.update(text);
30377         this.setTooltip(text);
30378         if(!this.tabPanel.resizeTabs){
30379             this.autoSize();
30380         }
30381     },
30382     /**
30383      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30384      */
30385     activate : function(){
30386         this.tabPanel.activate(this.id);
30387     },
30388
30389     /**
30390      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30391      */
30392     disable : function(){
30393         if(this.tabPanel.active != this){
30394             this.disabled = true;
30395             this.pnode.addClass("disabled");
30396         }
30397     },
30398
30399     /**
30400      * Enables this TabPanelItem if it was previously disabled.
30401      */
30402     enable : function(){
30403         this.disabled = false;
30404         this.pnode.removeClass("disabled");
30405     },
30406
30407     /**
30408      * Sets the content for this TabPanelItem.
30409      * @param {String} content The content
30410      * @param {Boolean} loadScripts true to look for and load scripts
30411      */
30412     setContent : function(content, loadScripts){
30413         this.bodyEl.update(content, loadScripts);
30414     },
30415
30416     /**
30417      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30418      * @return {Roo.UpdateManager} The UpdateManager
30419      */
30420     getUpdateManager : function(){
30421         return this.bodyEl.getUpdateManager();
30422     },
30423
30424     /**
30425      * Set a URL to be used to load the content for this TabPanelItem.
30426      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30427      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
30428      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
30429      * @return {Roo.UpdateManager} The UpdateManager
30430      */
30431     setUrl : function(url, params, loadOnce){
30432         if(this.refreshDelegate){
30433             this.un('activate', this.refreshDelegate);
30434         }
30435         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30436         this.on("activate", this.refreshDelegate);
30437         return this.bodyEl.getUpdateManager();
30438     },
30439
30440     /** @private */
30441     _handleRefresh : function(url, params, loadOnce){
30442         if(!loadOnce || !this.loaded){
30443             var updater = this.bodyEl.getUpdateManager();
30444             updater.update(url, params, this._setLoaded.createDelegate(this));
30445         }
30446     },
30447
30448     /**
30449      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30450      *   Will fail silently if the setUrl method has not been called.
30451      *   This does not activate the panel, just updates its content.
30452      */
30453     refresh : function(){
30454         if(this.refreshDelegate){
30455            this.loaded = false;
30456            this.refreshDelegate();
30457         }
30458     },
30459
30460     /** @private */
30461     _setLoaded : function(){
30462         this.loaded = true;
30463     },
30464
30465     /** @private */
30466     closeClick : function(e){
30467         var o = {};
30468         e.stopEvent();
30469         this.fireEvent("beforeclose", this, o);
30470         if(o.cancel !== true){
30471             this.tabPanel.removeTab(this.id);
30472         }
30473     },
30474     /**
30475      * The text displayed in the tooltip for the close icon.
30476      * @type String
30477      */
30478     closeText : "Close this tab"
30479 });
30480
30481 /** @private */
30482 Roo.TabPanel.prototype.createStrip = function(container){
30483     var strip = document.createElement("div");
30484     strip.className = "x-tabs-wrap";
30485     container.appendChild(strip);
30486     return strip;
30487 };
30488 /** @private */
30489 Roo.TabPanel.prototype.createStripList = function(strip){
30490     // div wrapper for retard IE
30491     // returns the "tr" element.
30492     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30493         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30494         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30495     return strip.firstChild.firstChild.firstChild.firstChild;
30496 };
30497 /** @private */
30498 Roo.TabPanel.prototype.createBody = function(container){
30499     var body = document.createElement("div");
30500     Roo.id(body, "tab-body");
30501     Roo.fly(body).addClass("x-tabs-body");
30502     container.appendChild(body);
30503     return body;
30504 };
30505 /** @private */
30506 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30507     var body = Roo.getDom(id);
30508     if(!body){
30509         body = document.createElement("div");
30510         body.id = id;
30511     }
30512     Roo.fly(body).addClass("x-tabs-item-body");
30513     bodyEl.insertBefore(body, bodyEl.firstChild);
30514     return body;
30515 };
30516 /** @private */
30517 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30518     var td = document.createElement("td");
30519     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30520     //stripEl.appendChild(td);
30521     if(closable){
30522         td.className = "x-tabs-closable";
30523         if(!this.closeTpl){
30524             this.closeTpl = new Roo.Template(
30525                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30526                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30527                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30528             );
30529         }
30530         var el = this.closeTpl.overwrite(td, {"text": text});
30531         var close = el.getElementsByTagName("div")[0];
30532         var inner = el.getElementsByTagName("em")[0];
30533         return {"el": el, "close": close, "inner": inner};
30534     } else {
30535         if(!this.tabTpl){
30536             this.tabTpl = new Roo.Template(
30537                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30538                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30539             );
30540         }
30541         var el = this.tabTpl.overwrite(td, {"text": text});
30542         var inner = el.getElementsByTagName("em")[0];
30543         return {"el": el, "inner": inner};
30544     }
30545 };/*
30546  * Based on:
30547  * Ext JS Library 1.1.1
30548  * Copyright(c) 2006-2007, Ext JS, LLC.
30549  *
30550  * Originally Released Under LGPL - original licence link has changed is not relivant.
30551  *
30552  * Fork - LGPL
30553  * <script type="text/javascript">
30554  */
30555
30556 /**
30557  * @class Roo.Button
30558  * @extends Roo.util.Observable
30559  * Simple Button class
30560  * @cfg {String} text The button text
30561  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30562  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30563  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30564  * @cfg {Object} scope The scope of the handler
30565  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30566  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30567  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30568  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30569  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30570  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30571    applies if enableToggle = true)
30572  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30573  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30574   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30575  * @constructor
30576  * Create a new button
30577  * @param {Object} config The config object
30578  */
30579 Roo.Button = function(renderTo, config)
30580 {
30581     if (!config) {
30582         config = renderTo;
30583         renderTo = config.renderTo || false;
30584     }
30585     
30586     Roo.apply(this, config);
30587     this.addEvents({
30588         /**
30589              * @event click
30590              * Fires when this button is clicked
30591              * @param {Button} this
30592              * @param {EventObject} e The click event
30593              */
30594             "click" : true,
30595         /**
30596              * @event toggle
30597              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30598              * @param {Button} this
30599              * @param {Boolean} pressed
30600              */
30601             "toggle" : true,
30602         /**
30603              * @event mouseover
30604              * Fires when the mouse hovers over the button
30605              * @param {Button} this
30606              * @param {Event} e The event object
30607              */
30608         'mouseover' : true,
30609         /**
30610              * @event mouseout
30611              * Fires when the mouse exits the button
30612              * @param {Button} this
30613              * @param {Event} e The event object
30614              */
30615         'mouseout': true,
30616          /**
30617              * @event render
30618              * Fires when the button is rendered
30619              * @param {Button} this
30620              */
30621         'render': true
30622     });
30623     if(this.menu){
30624         this.menu = Roo.menu.MenuMgr.get(this.menu);
30625     }
30626     // register listeners first!!  - so render can be captured..
30627     Roo.util.Observable.call(this);
30628     if(renderTo){
30629         this.render(renderTo);
30630     }
30631     
30632   
30633 };
30634
30635 Roo.extend(Roo.Button, Roo.util.Observable, {
30636     /**
30637      * 
30638      */
30639     
30640     /**
30641      * Read-only. True if this button is hidden
30642      * @type Boolean
30643      */
30644     hidden : false,
30645     /**
30646      * Read-only. True if this button is disabled
30647      * @type Boolean
30648      */
30649     disabled : false,
30650     /**
30651      * Read-only. True if this button is pressed (only if enableToggle = true)
30652      * @type Boolean
30653      */
30654     pressed : false,
30655
30656     /**
30657      * @cfg {Number} tabIndex 
30658      * The DOM tabIndex for this button (defaults to undefined)
30659      */
30660     tabIndex : undefined,
30661
30662     /**
30663      * @cfg {Boolean} enableToggle
30664      * True to enable pressed/not pressed toggling (defaults to false)
30665      */
30666     enableToggle: false,
30667     /**
30668      * @cfg {Roo.menu.Menu} menu
30669      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30670      */
30671     menu : undefined,
30672     /**
30673      * @cfg {String} menuAlign
30674      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30675      */
30676     menuAlign : "tl-bl?",
30677
30678     /**
30679      * @cfg {String} iconCls
30680      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30681      */
30682     iconCls : undefined,
30683     /**
30684      * @cfg {String} type
30685      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30686      */
30687     type : 'button',
30688
30689     // private
30690     menuClassTarget: 'tr',
30691
30692     /**
30693      * @cfg {String} clickEvent
30694      * The type of event to map to the button's event handler (defaults to 'click')
30695      */
30696     clickEvent : 'click',
30697
30698     /**
30699      * @cfg {Boolean} handleMouseEvents
30700      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30701      */
30702     handleMouseEvents : true,
30703
30704     /**
30705      * @cfg {String} tooltipType
30706      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30707      */
30708     tooltipType : 'qtip',
30709
30710     /**
30711      * @cfg {String} cls
30712      * A CSS class to apply to the button's main element.
30713      */
30714     
30715     /**
30716      * @cfg {Roo.Template} template (Optional)
30717      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30718      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30719      * require code modifications if required elements (e.g. a button) aren't present.
30720      */
30721
30722     // private
30723     render : function(renderTo){
30724         var btn;
30725         if(this.hideParent){
30726             this.parentEl = Roo.get(renderTo);
30727         }
30728         if(!this.dhconfig){
30729             if(!this.template){
30730                 if(!Roo.Button.buttonTemplate){
30731                     // hideous table template
30732                     Roo.Button.buttonTemplate = new Roo.Template(
30733                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30734                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
30735                         "</tr></tbody></table>");
30736                 }
30737                 this.template = Roo.Button.buttonTemplate;
30738             }
30739             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30740             var btnEl = btn.child("button:first");
30741             btnEl.on('focus', this.onFocus, this);
30742             btnEl.on('blur', this.onBlur, this);
30743             if(this.cls){
30744                 btn.addClass(this.cls);
30745             }
30746             if(this.icon){
30747                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30748             }
30749             if(this.iconCls){
30750                 btnEl.addClass(this.iconCls);
30751                 if(!this.cls){
30752                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30753                 }
30754             }
30755             if(this.tabIndex !== undefined){
30756                 btnEl.dom.tabIndex = this.tabIndex;
30757             }
30758             if(this.tooltip){
30759                 if(typeof this.tooltip == 'object'){
30760                     Roo.QuickTips.tips(Roo.apply({
30761                           target: btnEl.id
30762                     }, this.tooltip));
30763                 } else {
30764                     btnEl.dom[this.tooltipType] = this.tooltip;
30765                 }
30766             }
30767         }else{
30768             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30769         }
30770         this.el = btn;
30771         if(this.id){
30772             this.el.dom.id = this.el.id = this.id;
30773         }
30774         if(this.menu){
30775             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30776             this.menu.on("show", this.onMenuShow, this);
30777             this.menu.on("hide", this.onMenuHide, this);
30778         }
30779         btn.addClass("x-btn");
30780         if(Roo.isIE && !Roo.isIE7){
30781             this.autoWidth.defer(1, this);
30782         }else{
30783             this.autoWidth();
30784         }
30785         if(this.handleMouseEvents){
30786             btn.on("mouseover", this.onMouseOver, this);
30787             btn.on("mouseout", this.onMouseOut, this);
30788             btn.on("mousedown", this.onMouseDown, this);
30789         }
30790         btn.on(this.clickEvent, this.onClick, this);
30791         //btn.on("mouseup", this.onMouseUp, this);
30792         if(this.hidden){
30793             this.hide();
30794         }
30795         if(this.disabled){
30796             this.disable();
30797         }
30798         Roo.ButtonToggleMgr.register(this);
30799         if(this.pressed){
30800             this.el.addClass("x-btn-pressed");
30801         }
30802         if(this.repeat){
30803             var repeater = new Roo.util.ClickRepeater(btn,
30804                 typeof this.repeat == "object" ? this.repeat : {}
30805             );
30806             repeater.on("click", this.onClick,  this);
30807         }
30808         
30809         this.fireEvent('render', this);
30810         
30811     },
30812     /**
30813      * Returns the button's underlying element
30814      * @return {Roo.Element} The element
30815      */
30816     getEl : function(){
30817         return this.el;  
30818     },
30819     
30820     /**
30821      * Destroys this Button and removes any listeners.
30822      */
30823     destroy : function(){
30824         Roo.ButtonToggleMgr.unregister(this);
30825         this.el.removeAllListeners();
30826         this.purgeListeners();
30827         this.el.remove();
30828     },
30829
30830     // private
30831     autoWidth : function(){
30832         if(this.el){
30833             this.el.setWidth("auto");
30834             if(Roo.isIE7 && Roo.isStrict){
30835                 var ib = this.el.child('button');
30836                 if(ib && ib.getWidth() > 20){
30837                     ib.clip();
30838                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30839                 }
30840             }
30841             if(this.minWidth){
30842                 if(this.hidden){
30843                     this.el.beginMeasure();
30844                 }
30845                 if(this.el.getWidth() < this.minWidth){
30846                     this.el.setWidth(this.minWidth);
30847                 }
30848                 if(this.hidden){
30849                     this.el.endMeasure();
30850                 }
30851             }
30852         }
30853     },
30854
30855     /**
30856      * Assigns this button's click handler
30857      * @param {Function} handler The function to call when the button is clicked
30858      * @param {Object} scope (optional) Scope for the function passed in
30859      */
30860     setHandler : function(handler, scope){
30861         this.handler = handler;
30862         this.scope = scope;  
30863     },
30864     
30865     /**
30866      * Sets this button's text
30867      * @param {String} text The button text
30868      */
30869     setText : function(text){
30870         this.text = text;
30871         if(this.el){
30872             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30873         }
30874         this.autoWidth();
30875     },
30876     
30877     /**
30878      * Gets the text for this button
30879      * @return {String} The button text
30880      */
30881     getText : function(){
30882         return this.text;  
30883     },
30884     
30885     /**
30886      * Show this button
30887      */
30888     show: function(){
30889         this.hidden = false;
30890         if(this.el){
30891             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30892         }
30893     },
30894     
30895     /**
30896      * Hide this button
30897      */
30898     hide: function(){
30899         this.hidden = true;
30900         if(this.el){
30901             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30902         }
30903     },
30904     
30905     /**
30906      * Convenience function for boolean show/hide
30907      * @param {Boolean} visible True to show, false to hide
30908      */
30909     setVisible: function(visible){
30910         if(visible) {
30911             this.show();
30912         }else{
30913             this.hide();
30914         }
30915     },
30916     
30917     /**
30918      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30919      * @param {Boolean} state (optional) Force a particular state
30920      */
30921     toggle : function(state){
30922         state = state === undefined ? !this.pressed : state;
30923         if(state != this.pressed){
30924             if(state){
30925                 this.el.addClass("x-btn-pressed");
30926                 this.pressed = true;
30927                 this.fireEvent("toggle", this, true);
30928             }else{
30929                 this.el.removeClass("x-btn-pressed");
30930                 this.pressed = false;
30931                 this.fireEvent("toggle", this, false);
30932             }
30933             if(this.toggleHandler){
30934                 this.toggleHandler.call(this.scope || this, this, state);
30935             }
30936         }
30937     },
30938     
30939     /**
30940      * Focus the button
30941      */
30942     focus : function(){
30943         this.el.child('button:first').focus();
30944     },
30945     
30946     /**
30947      * Disable this button
30948      */
30949     disable : function(){
30950         if(this.el){
30951             this.el.addClass("x-btn-disabled");
30952         }
30953         this.disabled = true;
30954     },
30955     
30956     /**
30957      * Enable this button
30958      */
30959     enable : function(){
30960         if(this.el){
30961             this.el.removeClass("x-btn-disabled");
30962         }
30963         this.disabled = false;
30964     },
30965
30966     /**
30967      * Convenience function for boolean enable/disable
30968      * @param {Boolean} enabled True to enable, false to disable
30969      */
30970     setDisabled : function(v){
30971         this[v !== true ? "enable" : "disable"]();
30972     },
30973
30974     // private
30975     onClick : function(e)
30976     {
30977         if(e){
30978             e.preventDefault();
30979         }
30980         if(e.button != 0){
30981             return;
30982         }
30983         if(!this.disabled){
30984             if(this.enableToggle){
30985                 this.toggle();
30986             }
30987             if(this.menu && !this.menu.isVisible()){
30988                 this.menu.show(this.el, this.menuAlign);
30989             }
30990             this.fireEvent("click", this, e);
30991             if(this.handler){
30992                 this.el.removeClass("x-btn-over");
30993                 this.handler.call(this.scope || this, this, e);
30994             }
30995         }
30996     },
30997     // private
30998     onMouseOver : function(e){
30999         if(!this.disabled){
31000             this.el.addClass("x-btn-over");
31001             this.fireEvent('mouseover', this, e);
31002         }
31003     },
31004     // private
31005     onMouseOut : function(e){
31006         if(!e.within(this.el,  true)){
31007             this.el.removeClass("x-btn-over");
31008             this.fireEvent('mouseout', this, e);
31009         }
31010     },
31011     // private
31012     onFocus : function(e){
31013         if(!this.disabled){
31014             this.el.addClass("x-btn-focus");
31015         }
31016     },
31017     // private
31018     onBlur : function(e){
31019         this.el.removeClass("x-btn-focus");
31020     },
31021     // private
31022     onMouseDown : function(e){
31023         if(!this.disabled && e.button == 0){
31024             this.el.addClass("x-btn-click");
31025             Roo.get(document).on('mouseup', this.onMouseUp, this);
31026         }
31027     },
31028     // private
31029     onMouseUp : function(e){
31030         if(e.button == 0){
31031             this.el.removeClass("x-btn-click");
31032             Roo.get(document).un('mouseup', this.onMouseUp, this);
31033         }
31034     },
31035     // private
31036     onMenuShow : function(e){
31037         this.el.addClass("x-btn-menu-active");
31038     },
31039     // private
31040     onMenuHide : function(e){
31041         this.el.removeClass("x-btn-menu-active");
31042     }   
31043 });
31044
31045 // Private utility class used by Button
31046 Roo.ButtonToggleMgr = function(){
31047    var groups = {};
31048    
31049    function toggleGroup(btn, state){
31050        if(state){
31051            var g = groups[btn.toggleGroup];
31052            for(var i = 0, l = g.length; i < l; i++){
31053                if(g[i] != btn){
31054                    g[i].toggle(false);
31055                }
31056            }
31057        }
31058    }
31059    
31060    return {
31061        register : function(btn){
31062            if(!btn.toggleGroup){
31063                return;
31064            }
31065            var g = groups[btn.toggleGroup];
31066            if(!g){
31067                g = groups[btn.toggleGroup] = [];
31068            }
31069            g.push(btn);
31070            btn.on("toggle", toggleGroup);
31071        },
31072        
31073        unregister : function(btn){
31074            if(!btn.toggleGroup){
31075                return;
31076            }
31077            var g = groups[btn.toggleGroup];
31078            if(g){
31079                g.remove(btn);
31080                btn.un("toggle", toggleGroup);
31081            }
31082        }
31083    };
31084 }();/*
31085  * Based on:
31086  * Ext JS Library 1.1.1
31087  * Copyright(c) 2006-2007, Ext JS, LLC.
31088  *
31089  * Originally Released Under LGPL - original licence link has changed is not relivant.
31090  *
31091  * Fork - LGPL
31092  * <script type="text/javascript">
31093  */
31094  
31095 /**
31096  * @class Roo.SplitButton
31097  * @extends Roo.Button
31098  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31099  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31100  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31101  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31102  * @cfg {String} arrowTooltip The title attribute of the arrow
31103  * @constructor
31104  * Create a new menu button
31105  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31106  * @param {Object} config The config object
31107  */
31108 Roo.SplitButton = function(renderTo, config){
31109     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31110     /**
31111      * @event arrowclick
31112      * Fires when this button's arrow is clicked
31113      * @param {SplitButton} this
31114      * @param {EventObject} e The click event
31115      */
31116     this.addEvents({"arrowclick":true});
31117 };
31118
31119 Roo.extend(Roo.SplitButton, Roo.Button, {
31120     render : function(renderTo){
31121         // this is one sweet looking template!
31122         var tpl = new Roo.Template(
31123             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31124             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31125             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
31126             "</tbody></table></td><td>",
31127             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31128             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
31129             "</tbody></table></td></tr></table>"
31130         );
31131         var btn = tpl.append(renderTo, [this.text, this.type], true);
31132         var btnEl = btn.child("button");
31133         if(this.cls){
31134             btn.addClass(this.cls);
31135         }
31136         if(this.icon){
31137             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31138         }
31139         if(this.iconCls){
31140             btnEl.addClass(this.iconCls);
31141             if(!this.cls){
31142                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31143             }
31144         }
31145         this.el = btn;
31146         if(this.handleMouseEvents){
31147             btn.on("mouseover", this.onMouseOver, this);
31148             btn.on("mouseout", this.onMouseOut, this);
31149             btn.on("mousedown", this.onMouseDown, this);
31150             btn.on("mouseup", this.onMouseUp, this);
31151         }
31152         btn.on(this.clickEvent, this.onClick, this);
31153         if(this.tooltip){
31154             if(typeof this.tooltip == 'object'){
31155                 Roo.QuickTips.tips(Roo.apply({
31156                       target: btnEl.id
31157                 }, this.tooltip));
31158             } else {
31159                 btnEl.dom[this.tooltipType] = this.tooltip;
31160             }
31161         }
31162         if(this.arrowTooltip){
31163             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31164         }
31165         if(this.hidden){
31166             this.hide();
31167         }
31168         if(this.disabled){
31169             this.disable();
31170         }
31171         if(this.pressed){
31172             this.el.addClass("x-btn-pressed");
31173         }
31174         if(Roo.isIE && !Roo.isIE7){
31175             this.autoWidth.defer(1, this);
31176         }else{
31177             this.autoWidth();
31178         }
31179         if(this.menu){
31180             this.menu.on("show", this.onMenuShow, this);
31181             this.menu.on("hide", this.onMenuHide, this);
31182         }
31183         this.fireEvent('render', this);
31184     },
31185
31186     // private
31187     autoWidth : function(){
31188         if(this.el){
31189             var tbl = this.el.child("table:first");
31190             var tbl2 = this.el.child("table:last");
31191             this.el.setWidth("auto");
31192             tbl.setWidth("auto");
31193             if(Roo.isIE7 && Roo.isStrict){
31194                 var ib = this.el.child('button:first');
31195                 if(ib && ib.getWidth() > 20){
31196                     ib.clip();
31197                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31198                 }
31199             }
31200             if(this.minWidth){
31201                 if(this.hidden){
31202                     this.el.beginMeasure();
31203                 }
31204                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31205                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31206                 }
31207                 if(this.hidden){
31208                     this.el.endMeasure();
31209                 }
31210             }
31211             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31212         } 
31213     },
31214     /**
31215      * Sets this button's click handler
31216      * @param {Function} handler The function to call when the button is clicked
31217      * @param {Object} scope (optional) Scope for the function passed above
31218      */
31219     setHandler : function(handler, scope){
31220         this.handler = handler;
31221         this.scope = scope;  
31222     },
31223     
31224     /**
31225      * Sets this button's arrow click handler
31226      * @param {Function} handler The function to call when the arrow is clicked
31227      * @param {Object} scope (optional) Scope for the function passed above
31228      */
31229     setArrowHandler : function(handler, scope){
31230         this.arrowHandler = handler;
31231         this.scope = scope;  
31232     },
31233     
31234     /**
31235      * Focus the button
31236      */
31237     focus : function(){
31238         if(this.el){
31239             this.el.child("button:first").focus();
31240         }
31241     },
31242
31243     // private
31244     onClick : function(e){
31245         e.preventDefault();
31246         if(!this.disabled){
31247             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31248                 if(this.menu && !this.menu.isVisible()){
31249                     this.menu.show(this.el, this.menuAlign);
31250                 }
31251                 this.fireEvent("arrowclick", this, e);
31252                 if(this.arrowHandler){
31253                     this.arrowHandler.call(this.scope || this, this, e);
31254                 }
31255             }else{
31256                 this.fireEvent("click", this, e);
31257                 if(this.handler){
31258                     this.handler.call(this.scope || this, this, e);
31259                 }
31260             }
31261         }
31262     },
31263     // private
31264     onMouseDown : function(e){
31265         if(!this.disabled){
31266             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31267         }
31268     },
31269     // private
31270     onMouseUp : function(e){
31271         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31272     }   
31273 });
31274
31275
31276 // backwards compat
31277 Roo.MenuButton = Roo.SplitButton;/*
31278  * Based on:
31279  * Ext JS Library 1.1.1
31280  * Copyright(c) 2006-2007, Ext JS, LLC.
31281  *
31282  * Originally Released Under LGPL - original licence link has changed is not relivant.
31283  *
31284  * Fork - LGPL
31285  * <script type="text/javascript">
31286  */
31287
31288 /**
31289  * @class Roo.Toolbar
31290  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31291  * Basic Toolbar class.
31292  * @constructor
31293  * Creates a new Toolbar
31294  * @param {Object} container The config object
31295  */ 
31296 Roo.Toolbar = function(container, buttons, config)
31297 {
31298     /// old consturctor format still supported..
31299     if(container instanceof Array){ // omit the container for later rendering
31300         buttons = container;
31301         config = buttons;
31302         container = null;
31303     }
31304     if (typeof(container) == 'object' && container.xtype) {
31305         config = container;
31306         container = config.container;
31307         buttons = config.buttons || []; // not really - use items!!
31308     }
31309     var xitems = [];
31310     if (config && config.items) {
31311         xitems = config.items;
31312         delete config.items;
31313     }
31314     Roo.apply(this, config);
31315     this.buttons = buttons;
31316     
31317     if(container){
31318         this.render(container);
31319     }
31320     this.xitems = xitems;
31321     Roo.each(xitems, function(b) {
31322         this.add(b);
31323     }, this);
31324     
31325 };
31326
31327 Roo.Toolbar.prototype = {
31328     /**
31329      * @cfg {Array} items
31330      * array of button configs or elements to add (will be converted to a MixedCollection)
31331      */
31332     items: false,
31333     /**
31334      * @cfg {String/HTMLElement/Element} container
31335      * The id or element that will contain the toolbar
31336      */
31337     // private
31338     render : function(ct){
31339         this.el = Roo.get(ct);
31340         if(this.cls){
31341             this.el.addClass(this.cls);
31342         }
31343         // using a table allows for vertical alignment
31344         // 100% width is needed by Safari...
31345         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31346         this.tr = this.el.child("tr", true);
31347         var autoId = 0;
31348         this.items = new Roo.util.MixedCollection(false, function(o){
31349             return o.id || ("item" + (++autoId));
31350         });
31351         if(this.buttons){
31352             this.add.apply(this, this.buttons);
31353             delete this.buttons;
31354         }
31355     },
31356
31357     /**
31358      * Adds element(s) to the toolbar -- this function takes a variable number of 
31359      * arguments of mixed type and adds them to the toolbar.
31360      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31361      * <ul>
31362      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31363      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31364      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31365      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31366      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31367      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31368      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31369      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31370      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31371      * </ul>
31372      * @param {Mixed} arg2
31373      * @param {Mixed} etc.
31374      */
31375     add : function(){
31376         var a = arguments, l = a.length;
31377         for(var i = 0; i < l; i++){
31378             this._add(a[i]);
31379         }
31380     },
31381     // private..
31382     _add : function(el) {
31383         
31384         if (el.xtype) {
31385             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31386         }
31387         
31388         if (el.applyTo){ // some kind of form field
31389             return this.addField(el);
31390         } 
31391         if (el.render){ // some kind of Toolbar.Item
31392             return this.addItem(el);
31393         }
31394         if (typeof el == "string"){ // string
31395             if(el == "separator" || el == "-"){
31396                 return this.addSeparator();
31397             }
31398             if (el == " "){
31399                 return this.addSpacer();
31400             }
31401             if(el == "->"){
31402                 return this.addFill();
31403             }
31404             return this.addText(el);
31405             
31406         }
31407         if(el.tagName){ // element
31408             return this.addElement(el);
31409         }
31410         if(typeof el == "object"){ // must be button config?
31411             return this.addButton(el);
31412         }
31413         // and now what?!?!
31414         return false;
31415         
31416     },
31417     
31418     /**
31419      * Add an Xtype element
31420      * @param {Object} xtype Xtype Object
31421      * @return {Object} created Object
31422      */
31423     addxtype : function(e){
31424         return this.add(e);  
31425     },
31426     
31427     /**
31428      * Returns the Element for this toolbar.
31429      * @return {Roo.Element}
31430      */
31431     getEl : function(){
31432         return this.el;  
31433     },
31434     
31435     /**
31436      * Adds a separator
31437      * @return {Roo.Toolbar.Item} The separator item
31438      */
31439     addSeparator : function(){
31440         return this.addItem(new Roo.Toolbar.Separator());
31441     },
31442
31443     /**
31444      * Adds a spacer element
31445      * @return {Roo.Toolbar.Spacer} The spacer item
31446      */
31447     addSpacer : function(){
31448         return this.addItem(new Roo.Toolbar.Spacer());
31449     },
31450
31451     /**
31452      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31453      * @return {Roo.Toolbar.Fill} The fill item
31454      */
31455     addFill : function(){
31456         return this.addItem(new Roo.Toolbar.Fill());
31457     },
31458
31459     /**
31460      * Adds any standard HTML element to the toolbar
31461      * @param {String/HTMLElement/Element} el The element or id of the element to add
31462      * @return {Roo.Toolbar.Item} The element's item
31463      */
31464     addElement : function(el){
31465         return this.addItem(new Roo.Toolbar.Item(el));
31466     },
31467     /**
31468      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31469      * @type Roo.util.MixedCollection  
31470      */
31471     items : false,
31472      
31473     /**
31474      * Adds any Toolbar.Item or subclass
31475      * @param {Roo.Toolbar.Item} item
31476      * @return {Roo.Toolbar.Item} The item
31477      */
31478     addItem : function(item){
31479         var td = this.nextBlock();
31480         item.render(td);
31481         this.items.add(item);
31482         return item;
31483     },
31484     
31485     /**
31486      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31487      * @param {Object/Array} config A button config or array of configs
31488      * @return {Roo.Toolbar.Button/Array}
31489      */
31490     addButton : function(config){
31491         if(config instanceof Array){
31492             var buttons = [];
31493             for(var i = 0, len = config.length; i < len; i++) {
31494                 buttons.push(this.addButton(config[i]));
31495             }
31496             return buttons;
31497         }
31498         var b = config;
31499         if(!(config instanceof Roo.Toolbar.Button)){
31500             b = config.split ?
31501                 new Roo.Toolbar.SplitButton(config) :
31502                 new Roo.Toolbar.Button(config);
31503         }
31504         var td = this.nextBlock();
31505         b.render(td);
31506         this.items.add(b);
31507         return b;
31508     },
31509     
31510     /**
31511      * Adds text to the toolbar
31512      * @param {String} text The text to add
31513      * @return {Roo.Toolbar.Item} The element's item
31514      */
31515     addText : function(text){
31516         return this.addItem(new Roo.Toolbar.TextItem(text));
31517     },
31518     
31519     /**
31520      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31521      * @param {Number} index The index where the item is to be inserted
31522      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31523      * @return {Roo.Toolbar.Button/Item}
31524      */
31525     insertButton : function(index, item){
31526         if(item instanceof Array){
31527             var buttons = [];
31528             for(var i = 0, len = item.length; i < len; i++) {
31529                buttons.push(this.insertButton(index + i, item[i]));
31530             }
31531             return buttons;
31532         }
31533         if (!(item instanceof Roo.Toolbar.Button)){
31534            item = new Roo.Toolbar.Button(item);
31535         }
31536         var td = document.createElement("td");
31537         this.tr.insertBefore(td, this.tr.childNodes[index]);
31538         item.render(td);
31539         this.items.insert(index, item);
31540         return item;
31541     },
31542     
31543     /**
31544      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31545      * @param {Object} config
31546      * @return {Roo.Toolbar.Item} The element's item
31547      */
31548     addDom : function(config, returnEl){
31549         var td = this.nextBlock();
31550         Roo.DomHelper.overwrite(td, config);
31551         var ti = new Roo.Toolbar.Item(td.firstChild);
31552         ti.render(td);
31553         this.items.add(ti);
31554         return ti;
31555     },
31556
31557     /**
31558      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31559      * @type Roo.util.MixedCollection  
31560      */
31561     fields : false,
31562     
31563     /**
31564      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31565      * Note: the field should not have been rendered yet. For a field that has already been
31566      * rendered, use {@link #addElement}.
31567      * @param {Roo.form.Field} field
31568      * @return {Roo.ToolbarItem}
31569      */
31570      
31571       
31572     addField : function(field) {
31573         if (!this.fields) {
31574             var autoId = 0;
31575             this.fields = new Roo.util.MixedCollection(false, function(o){
31576                 return o.id || ("item" + (++autoId));
31577             });
31578
31579         }
31580         
31581         var td = this.nextBlock();
31582         field.render(td);
31583         var ti = new Roo.Toolbar.Item(td.firstChild);
31584         ti.render(td);
31585         this.items.add(ti);
31586         this.fields.add(field);
31587         return ti;
31588     },
31589     /**
31590      * Hide the toolbar
31591      * @method hide
31592      */
31593      
31594       
31595     hide : function()
31596     {
31597         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31598         this.el.child('div').hide();
31599     },
31600     /**
31601      * Show the toolbar
31602      * @method show
31603      */
31604     show : function()
31605     {
31606         this.el.child('div').show();
31607     },
31608       
31609     // private
31610     nextBlock : function(){
31611         var td = document.createElement("td");
31612         this.tr.appendChild(td);
31613         return td;
31614     },
31615
31616     // private
31617     destroy : function(){
31618         if(this.items){ // rendered?
31619             Roo.destroy.apply(Roo, this.items.items);
31620         }
31621         if(this.fields){ // rendered?
31622             Roo.destroy.apply(Roo, this.fields.items);
31623         }
31624         Roo.Element.uncache(this.el, this.tr);
31625     }
31626 };
31627
31628 /**
31629  * @class Roo.Toolbar.Item
31630  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31631  * @constructor
31632  * Creates a new Item
31633  * @param {HTMLElement} el 
31634  */
31635 Roo.Toolbar.Item = function(el){
31636     var cfg = {};
31637     if (typeof (el.xtype) != 'undefined') {
31638         cfg = el;
31639         el = cfg.el;
31640     }
31641     
31642     this.el = Roo.getDom(el);
31643     this.id = Roo.id(this.el);
31644     this.hidden = false;
31645     
31646     this.addEvents({
31647          /**
31648              * @event render
31649              * Fires when the button is rendered
31650              * @param {Button} this
31651              */
31652         'render': true
31653     });
31654     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31655 };
31656 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31657 //Roo.Toolbar.Item.prototype = {
31658     
31659     /**
31660      * Get this item's HTML Element
31661      * @return {HTMLElement}
31662      */
31663     getEl : function(){
31664        return this.el;  
31665     },
31666
31667     // private
31668     render : function(td){
31669         
31670          this.td = td;
31671         td.appendChild(this.el);
31672         
31673         this.fireEvent('render', this);
31674     },
31675     
31676     /**
31677      * Removes and destroys this item.
31678      */
31679     destroy : function(){
31680         this.td.parentNode.removeChild(this.td);
31681     },
31682     
31683     /**
31684      * Shows this item.
31685      */
31686     show: function(){
31687         this.hidden = false;
31688         this.td.style.display = "";
31689     },
31690     
31691     /**
31692      * Hides this item.
31693      */
31694     hide: function(){
31695         this.hidden = true;
31696         this.td.style.display = "none";
31697     },
31698     
31699     /**
31700      * Convenience function for boolean show/hide.
31701      * @param {Boolean} visible true to show/false to hide
31702      */
31703     setVisible: function(visible){
31704         if(visible) {
31705             this.show();
31706         }else{
31707             this.hide();
31708         }
31709     },
31710     
31711     /**
31712      * Try to focus this item.
31713      */
31714     focus : function(){
31715         Roo.fly(this.el).focus();
31716     },
31717     
31718     /**
31719      * Disables this item.
31720      */
31721     disable : function(){
31722         Roo.fly(this.td).addClass("x-item-disabled");
31723         this.disabled = true;
31724         this.el.disabled = true;
31725     },
31726     
31727     /**
31728      * Enables this item.
31729      */
31730     enable : function(){
31731         Roo.fly(this.td).removeClass("x-item-disabled");
31732         this.disabled = false;
31733         this.el.disabled = false;
31734     }
31735 });
31736
31737
31738 /**
31739  * @class Roo.Toolbar.Separator
31740  * @extends Roo.Toolbar.Item
31741  * A simple toolbar separator class
31742  * @constructor
31743  * Creates a new Separator
31744  */
31745 Roo.Toolbar.Separator = function(cfg){
31746     
31747     var s = document.createElement("span");
31748     s.className = "ytb-sep";
31749     if (cfg) {
31750         cfg.el = s;
31751     }
31752     
31753     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31754 };
31755 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31756     enable:Roo.emptyFn,
31757     disable:Roo.emptyFn,
31758     focus:Roo.emptyFn
31759 });
31760
31761 /**
31762  * @class Roo.Toolbar.Spacer
31763  * @extends Roo.Toolbar.Item
31764  * A simple element that adds extra horizontal space to a toolbar.
31765  * @constructor
31766  * Creates a new Spacer
31767  */
31768 Roo.Toolbar.Spacer = function(cfg){
31769     var s = document.createElement("div");
31770     s.className = "ytb-spacer";
31771     if (cfg) {
31772         cfg.el = s;
31773     }
31774     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31775 };
31776 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31777     enable:Roo.emptyFn,
31778     disable:Roo.emptyFn,
31779     focus:Roo.emptyFn
31780 });
31781
31782 /**
31783  * @class Roo.Toolbar.Fill
31784  * @extends Roo.Toolbar.Spacer
31785  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31786  * @constructor
31787  * Creates a new Spacer
31788  */
31789 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31790     // private
31791     render : function(td){
31792         td.style.width = '100%';
31793         Roo.Toolbar.Fill.superclass.render.call(this, td);
31794     }
31795 });
31796
31797 /**
31798  * @class Roo.Toolbar.TextItem
31799  * @extends Roo.Toolbar.Item
31800  * A simple class that renders text directly into a toolbar.
31801  * @constructor
31802  * Creates a new TextItem
31803  * @cfg {string} text 
31804  */
31805 Roo.Toolbar.TextItem = function(cfg){
31806     var  text = cfg || "";
31807     if (typeof(cfg) == 'object') {
31808         text = cfg.text || "";
31809     }  else {
31810         cfg = null;
31811     }
31812     var s = document.createElement("span");
31813     s.className = "ytb-text";
31814     s.innerHTML = text;
31815     if (cfg) {
31816         cfg.el  = s;
31817     }
31818     
31819     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31820 };
31821 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31822     
31823      
31824     enable:Roo.emptyFn,
31825     disable:Roo.emptyFn,
31826     focus:Roo.emptyFn,
31827      /**
31828      * Shows this button
31829      */
31830     show: function(){
31831         this.hidden = false;
31832         this.el.style.display = "";
31833     },
31834     
31835     /**
31836      * Hides this button
31837      */
31838     hide: function(){
31839         this.hidden = true;
31840         this.el.style.display = "none";
31841     }
31842     
31843 });
31844
31845 /**
31846  * @class Roo.Toolbar.Button
31847  * @extends Roo.Button
31848  * A button that renders into a toolbar.
31849  * @constructor
31850  * Creates a new Button
31851  * @param {Object} config A standard {@link Roo.Button} config object
31852  */
31853 Roo.Toolbar.Button = function(config){
31854     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31855 };
31856 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31857 {
31858     
31859     
31860     render : function(td){
31861         this.td = td;
31862         Roo.Toolbar.Button.superclass.render.call(this, td);
31863     },
31864     
31865     /**
31866      * Removes and destroys this button
31867      */
31868     destroy : function(){
31869         Roo.Toolbar.Button.superclass.destroy.call(this);
31870         this.td.parentNode.removeChild(this.td);
31871     },
31872     
31873     /**
31874      * Shows this button
31875      */
31876     show: function(){
31877         this.hidden = false;
31878         this.td.style.display = "";
31879     },
31880     
31881     /**
31882      * Hides this button
31883      */
31884     hide: function(){
31885         this.hidden = true;
31886         this.td.style.display = "none";
31887     },
31888
31889     /**
31890      * Disables this item
31891      */
31892     disable : function(){
31893         Roo.fly(this.td).addClass("x-item-disabled");
31894         this.disabled = true;
31895     },
31896
31897     /**
31898      * Enables this item
31899      */
31900     enable : function(){
31901         Roo.fly(this.td).removeClass("x-item-disabled");
31902         this.disabled = false;
31903     }
31904 });
31905 // backwards compat
31906 Roo.ToolbarButton = Roo.Toolbar.Button;
31907
31908 /**
31909  * @class Roo.Toolbar.SplitButton
31910  * @extends Roo.SplitButton
31911  * A menu button that renders into a toolbar.
31912  * @constructor
31913  * Creates a new SplitButton
31914  * @param {Object} config A standard {@link Roo.SplitButton} config object
31915  */
31916 Roo.Toolbar.SplitButton = function(config){
31917     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31918 };
31919 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31920     render : function(td){
31921         this.td = td;
31922         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31923     },
31924     
31925     /**
31926      * Removes and destroys this button
31927      */
31928     destroy : function(){
31929         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31930         this.td.parentNode.removeChild(this.td);
31931     },
31932     
31933     /**
31934      * Shows this button
31935      */
31936     show: function(){
31937         this.hidden = false;
31938         this.td.style.display = "";
31939     },
31940     
31941     /**
31942      * Hides this button
31943      */
31944     hide: function(){
31945         this.hidden = true;
31946         this.td.style.display = "none";
31947     }
31948 });
31949
31950 // backwards compat
31951 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31952  * Based on:
31953  * Ext JS Library 1.1.1
31954  * Copyright(c) 2006-2007, Ext JS, LLC.
31955  *
31956  * Originally Released Under LGPL - original licence link has changed is not relivant.
31957  *
31958  * Fork - LGPL
31959  * <script type="text/javascript">
31960  */
31961  
31962 /**
31963  * @class Roo.PagingToolbar
31964  * @extends Roo.Toolbar
31965  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31966  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31967  * @constructor
31968  * Create a new PagingToolbar
31969  * @param {Object} config The config object
31970  */
31971 Roo.PagingToolbar = function(el, ds, config)
31972 {
31973     // old args format still supported... - xtype is prefered..
31974     if (typeof(el) == 'object' && el.xtype) {
31975         // created from xtype...
31976         config = el;
31977         ds = el.dataSource;
31978         el = config.container;
31979     }
31980     var items = [];
31981     if (config.items) {
31982         items = config.items;
31983         config.items = [];
31984     }
31985     
31986     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31987     this.ds = ds;
31988     this.cursor = 0;
31989     this.renderButtons(this.el);
31990     this.bind(ds);
31991     
31992     // supprot items array.
31993    
31994     Roo.each(items, function(e) {
31995         this.add(Roo.factory(e));
31996     },this);
31997     
31998 };
31999
32000 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32001    
32002     /**
32003      * @cfg {String/HTMLElement/Element} container
32004      * container The id or element that will contain the toolbar
32005      */
32006     /**
32007      * @cfg {Boolean} displayInfo
32008      * True to display the displayMsg (defaults to false)
32009      */
32010     
32011     
32012     /**
32013      * @cfg {Number} pageSize
32014      * The number of records to display per page (defaults to 20)
32015      */
32016     pageSize: 20,
32017     /**
32018      * @cfg {String} displayMsg
32019      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32020      */
32021     displayMsg : 'Displaying {0} - {1} of {2}',
32022     /**
32023      * @cfg {String} emptyMsg
32024      * The message to display when no records are found (defaults to "No data to display")
32025      */
32026     emptyMsg : 'No data to display',
32027     /**
32028      * Customizable piece of the default paging text (defaults to "Page")
32029      * @type String
32030      */
32031     beforePageText : "Page",
32032     /**
32033      * Customizable piece of the default paging text (defaults to "of %0")
32034      * @type String
32035      */
32036     afterPageText : "of {0}",
32037     /**
32038      * Customizable piece of the default paging text (defaults to "First Page")
32039      * @type String
32040      */
32041     firstText : "First Page",
32042     /**
32043      * Customizable piece of the default paging text (defaults to "Previous Page")
32044      * @type String
32045      */
32046     prevText : "Previous Page",
32047     /**
32048      * Customizable piece of the default paging text (defaults to "Next Page")
32049      * @type String
32050      */
32051     nextText : "Next Page",
32052     /**
32053      * Customizable piece of the default paging text (defaults to "Last Page")
32054      * @type String
32055      */
32056     lastText : "Last Page",
32057     /**
32058      * Customizable piece of the default paging text (defaults to "Refresh")
32059      * @type String
32060      */
32061     refreshText : "Refresh",
32062
32063     // private
32064     renderButtons : function(el){
32065         Roo.PagingToolbar.superclass.render.call(this, el);
32066         this.first = this.addButton({
32067             tooltip: this.firstText,
32068             cls: "x-btn-icon x-grid-page-first",
32069             disabled: true,
32070             handler: this.onClick.createDelegate(this, ["first"])
32071         });
32072         this.prev = this.addButton({
32073             tooltip: this.prevText,
32074             cls: "x-btn-icon x-grid-page-prev",
32075             disabled: true,
32076             handler: this.onClick.createDelegate(this, ["prev"])
32077         });
32078         //this.addSeparator();
32079         this.add(this.beforePageText);
32080         this.field = Roo.get(this.addDom({
32081            tag: "input",
32082            type: "text",
32083            size: "3",
32084            value: "1",
32085            cls: "x-grid-page-number"
32086         }).el);
32087         this.field.on("keydown", this.onPagingKeydown, this);
32088         this.field.on("focus", function(){this.dom.select();});
32089         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32090         this.field.setHeight(18);
32091         //this.addSeparator();
32092         this.next = this.addButton({
32093             tooltip: this.nextText,
32094             cls: "x-btn-icon x-grid-page-next",
32095             disabled: true,
32096             handler: this.onClick.createDelegate(this, ["next"])
32097         });
32098         this.last = this.addButton({
32099             tooltip: this.lastText,
32100             cls: "x-btn-icon x-grid-page-last",
32101             disabled: true,
32102             handler: this.onClick.createDelegate(this, ["last"])
32103         });
32104         //this.addSeparator();
32105         this.loading = this.addButton({
32106             tooltip: this.refreshText,
32107             cls: "x-btn-icon x-grid-loading",
32108             handler: this.onClick.createDelegate(this, ["refresh"])
32109         });
32110
32111         if(this.displayInfo){
32112             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32113         }
32114     },
32115
32116     // private
32117     updateInfo : function(){
32118         if(this.displayEl){
32119             var count = this.ds.getCount();
32120             var msg = count == 0 ?
32121                 this.emptyMsg :
32122                 String.format(
32123                     this.displayMsg,
32124                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32125                 );
32126             this.displayEl.update(msg);
32127         }
32128     },
32129
32130     // private
32131     onLoad : function(ds, r, o){
32132        this.cursor = o.params ? o.params.start : 0;
32133        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32134
32135        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32136        this.field.dom.value = ap;
32137        this.first.setDisabled(ap == 1);
32138        this.prev.setDisabled(ap == 1);
32139        this.next.setDisabled(ap == ps);
32140        this.last.setDisabled(ap == ps);
32141        this.loading.enable();
32142        this.updateInfo();
32143     },
32144
32145     // private
32146     getPageData : function(){
32147         var total = this.ds.getTotalCount();
32148         return {
32149             total : total,
32150             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32151             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32152         };
32153     },
32154
32155     // private
32156     onLoadError : function(){
32157         this.loading.enable();
32158     },
32159
32160     // private
32161     onPagingKeydown : function(e){
32162         var k = e.getKey();
32163         var d = this.getPageData();
32164         if(k == e.RETURN){
32165             var v = this.field.dom.value, pageNum;
32166             if(!v || isNaN(pageNum = parseInt(v, 10))){
32167                 this.field.dom.value = d.activePage;
32168                 return;
32169             }
32170             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32171             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32172             e.stopEvent();
32173         }
32174         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
32175         {
32176           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32177           this.field.dom.value = pageNum;
32178           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32179           e.stopEvent();
32180         }
32181         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32182         {
32183           var v = this.field.dom.value, pageNum; 
32184           var increment = (e.shiftKey) ? 10 : 1;
32185           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32186             increment *= -1;
32187           }
32188           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32189             this.field.dom.value = d.activePage;
32190             return;
32191           }
32192           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32193           {
32194             this.field.dom.value = parseInt(v, 10) + increment;
32195             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32196             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32197           }
32198           e.stopEvent();
32199         }
32200     },
32201
32202     // private
32203     beforeLoad : function(){
32204         if(this.loading){
32205             this.loading.disable();
32206         }
32207     },
32208
32209     // private
32210     onClick : function(which){
32211         var ds = this.ds;
32212         switch(which){
32213             case "first":
32214                 ds.load({params:{start: 0, limit: this.pageSize}});
32215             break;
32216             case "prev":
32217                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32218             break;
32219             case "next":
32220                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32221             break;
32222             case "last":
32223                 var total = ds.getTotalCount();
32224                 var extra = total % this.pageSize;
32225                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32226                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32227             break;
32228             case "refresh":
32229                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32230             break;
32231         }
32232     },
32233
32234     /**
32235      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32236      * @param {Roo.data.Store} store The data store to unbind
32237      */
32238     unbind : function(ds){
32239         ds.un("beforeload", this.beforeLoad, this);
32240         ds.un("load", this.onLoad, this);
32241         ds.un("loadexception", this.onLoadError, this);
32242         ds.un("remove", this.updateInfo, this);
32243         ds.un("add", this.updateInfo, this);
32244         this.ds = undefined;
32245     },
32246
32247     /**
32248      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32249      * @param {Roo.data.Store} store The data store to bind
32250      */
32251     bind : function(ds){
32252         ds.on("beforeload", this.beforeLoad, this);
32253         ds.on("load", this.onLoad, this);
32254         ds.on("loadexception", this.onLoadError, this);
32255         ds.on("remove", this.updateInfo, this);
32256         ds.on("add", this.updateInfo, this);
32257         this.ds = ds;
32258     }
32259 });/*
32260  * Based on:
32261  * Ext JS Library 1.1.1
32262  * Copyright(c) 2006-2007, Ext JS, LLC.
32263  *
32264  * Originally Released Under LGPL - original licence link has changed is not relivant.
32265  *
32266  * Fork - LGPL
32267  * <script type="text/javascript">
32268  */
32269
32270 /**
32271  * @class Roo.Resizable
32272  * @extends Roo.util.Observable
32273  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32274  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32275  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
32276  * the element will be wrapped for you automatically.</p>
32277  * <p>Here is the list of valid resize handles:</p>
32278  * <pre>
32279 Value   Description
32280 ------  -------------------
32281  'n'     north
32282  's'     south
32283  'e'     east
32284  'w'     west
32285  'nw'    northwest
32286  'sw'    southwest
32287  'se'    southeast
32288  'ne'    northeast
32289  'hd'    horizontal drag
32290  'all'   all
32291 </pre>
32292  * <p>Here's an example showing the creation of a typical Resizable:</p>
32293  * <pre><code>
32294 var resizer = new Roo.Resizable("element-id", {
32295     handles: 'all',
32296     minWidth: 200,
32297     minHeight: 100,
32298     maxWidth: 500,
32299     maxHeight: 400,
32300     pinned: true
32301 });
32302 resizer.on("resize", myHandler);
32303 </code></pre>
32304  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32305  * resizer.east.setDisplayed(false);</p>
32306  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32307  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32308  * resize operation's new size (defaults to [0, 0])
32309  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32310  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32311  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32312  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32313  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32314  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32315  * @cfg {Number} width The width of the element in pixels (defaults to null)
32316  * @cfg {Number} height The height of the element in pixels (defaults to null)
32317  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32318  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32319  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32320  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32321  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32322  * in favor of the handles config option (defaults to false)
32323  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32324  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32325  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32326  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32327  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32328  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32329  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32330  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32331  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32332  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32333  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32334  * @constructor
32335  * Create a new resizable component
32336  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32337  * @param {Object} config configuration options
32338   */
32339 Roo.Resizable = function(el, config)
32340 {
32341     this.el = Roo.get(el);
32342
32343     if(config && config.wrap){
32344         config.resizeChild = this.el;
32345         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32346         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32347         this.el.setStyle("overflow", "hidden");
32348         this.el.setPositioning(config.resizeChild.getPositioning());
32349         config.resizeChild.clearPositioning();
32350         if(!config.width || !config.height){
32351             var csize = config.resizeChild.getSize();
32352             this.el.setSize(csize.width, csize.height);
32353         }
32354         if(config.pinned && !config.adjustments){
32355             config.adjustments = "auto";
32356         }
32357     }
32358
32359     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32360     this.proxy.unselectable();
32361     this.proxy.enableDisplayMode('block');
32362
32363     Roo.apply(this, config);
32364
32365     if(this.pinned){
32366         this.disableTrackOver = true;
32367         this.el.addClass("x-resizable-pinned");
32368     }
32369     // if the element isn't positioned, make it relative
32370     var position = this.el.getStyle("position");
32371     if(position != "absolute" && position != "fixed"){
32372         this.el.setStyle("position", "relative");
32373     }
32374     if(!this.handles){ // no handles passed, must be legacy style
32375         this.handles = 's,e,se';
32376         if(this.multiDirectional){
32377             this.handles += ',n,w';
32378         }
32379     }
32380     if(this.handles == "all"){
32381         this.handles = "n s e w ne nw se sw";
32382     }
32383     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32384     var ps = Roo.Resizable.positions;
32385     for(var i = 0, len = hs.length; i < len; i++){
32386         if(hs[i] && ps[hs[i]]){
32387             var pos = ps[hs[i]];
32388             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32389         }
32390     }
32391     // legacy
32392     this.corner = this.southeast;
32393     
32394     // updateBox = the box can move..
32395     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32396         this.updateBox = true;
32397     }
32398
32399     this.activeHandle = null;
32400
32401     if(this.resizeChild){
32402         if(typeof this.resizeChild == "boolean"){
32403             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32404         }else{
32405             this.resizeChild = Roo.get(this.resizeChild, true);
32406         }
32407     }
32408     
32409     if(this.adjustments == "auto"){
32410         var rc = this.resizeChild;
32411         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32412         if(rc && (hw || hn)){
32413             rc.position("relative");
32414             rc.setLeft(hw ? hw.el.getWidth() : 0);
32415             rc.setTop(hn ? hn.el.getHeight() : 0);
32416         }
32417         this.adjustments = [
32418             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32419             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32420         ];
32421     }
32422
32423     if(this.draggable){
32424         this.dd = this.dynamic ?
32425             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32426         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32427     }
32428
32429     // public events
32430     this.addEvents({
32431         /**
32432          * @event beforeresize
32433          * Fired before resize is allowed. Set enabled to false to cancel resize.
32434          * @param {Roo.Resizable} this
32435          * @param {Roo.EventObject} e The mousedown event
32436          */
32437         "beforeresize" : true,
32438         /**
32439          * @event resizing
32440          * Fired a resizing.
32441          * @param {Roo.Resizable} this
32442          * @param {Number} x The new x position
32443          * @param {Number} y The new y position
32444          * @param {Number} w The new w width
32445          * @param {Number} h The new h hight
32446          * @param {Roo.EventObject} e The mouseup event
32447          */
32448         "resizing" : true,
32449         /**
32450          * @event resize
32451          * Fired after a resize.
32452          * @param {Roo.Resizable} this
32453          * @param {Number} width The new width
32454          * @param {Number} height The new height
32455          * @param {Roo.EventObject} e The mouseup event
32456          */
32457         "resize" : true
32458     });
32459
32460     if(this.width !== null && this.height !== null){
32461         this.resizeTo(this.width, this.height);
32462     }else{
32463         this.updateChildSize();
32464     }
32465     if(Roo.isIE){
32466         this.el.dom.style.zoom = 1;
32467     }
32468     Roo.Resizable.superclass.constructor.call(this);
32469 };
32470
32471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32472         resizeChild : false,
32473         adjustments : [0, 0],
32474         minWidth : 5,
32475         minHeight : 5,
32476         maxWidth : 10000,
32477         maxHeight : 10000,
32478         enabled : true,
32479         animate : false,
32480         duration : .35,
32481         dynamic : false,
32482         handles : false,
32483         multiDirectional : false,
32484         disableTrackOver : false,
32485         easing : 'easeOutStrong',
32486         widthIncrement : 0,
32487         heightIncrement : 0,
32488         pinned : false,
32489         width : null,
32490         height : null,
32491         preserveRatio : false,
32492         transparent: false,
32493         minX: 0,
32494         minY: 0,
32495         draggable: false,
32496
32497         /**
32498          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32499          */
32500         constrainTo: undefined,
32501         /**
32502          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32503          */
32504         resizeRegion: undefined,
32505
32506
32507     /**
32508      * Perform a manual resize
32509      * @param {Number} width
32510      * @param {Number} height
32511      */
32512     resizeTo : function(width, height){
32513         this.el.setSize(width, height);
32514         this.updateChildSize();
32515         this.fireEvent("resize", this, width, height, null);
32516     },
32517
32518     // private
32519     startSizing : function(e, handle){
32520         this.fireEvent("beforeresize", this, e);
32521         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32522
32523             if(!this.overlay){
32524                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32525                 this.overlay.unselectable();
32526                 this.overlay.enableDisplayMode("block");
32527                 this.overlay.on("mousemove", this.onMouseMove, this);
32528                 this.overlay.on("mouseup", this.onMouseUp, this);
32529             }
32530             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32531
32532             this.resizing = true;
32533             this.startBox = this.el.getBox();
32534             this.startPoint = e.getXY();
32535             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32536                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32537
32538             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32539             this.overlay.show();
32540
32541             if(this.constrainTo) {
32542                 var ct = Roo.get(this.constrainTo);
32543                 this.resizeRegion = ct.getRegion().adjust(
32544                     ct.getFrameWidth('t'),
32545                     ct.getFrameWidth('l'),
32546                     -ct.getFrameWidth('b'),
32547                     -ct.getFrameWidth('r')
32548                 );
32549             }
32550
32551             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32552             this.proxy.show();
32553             this.proxy.setBox(this.startBox);
32554             if(!this.dynamic){
32555                 this.proxy.setStyle('visibility', 'visible');
32556             }
32557         }
32558     },
32559
32560     // private
32561     onMouseDown : function(handle, e){
32562         if(this.enabled){
32563             e.stopEvent();
32564             this.activeHandle = handle;
32565             this.startSizing(e, handle);
32566         }
32567     },
32568
32569     // private
32570     onMouseUp : function(e){
32571         var size = this.resizeElement();
32572         this.resizing = false;
32573         this.handleOut();
32574         this.overlay.hide();
32575         this.proxy.hide();
32576         this.fireEvent("resize", this, size.width, size.height, e);
32577     },
32578
32579     // private
32580     updateChildSize : function(){
32581         
32582         if(this.resizeChild){
32583             var el = this.el;
32584             var child = this.resizeChild;
32585             var adj = this.adjustments;
32586             if(el.dom.offsetWidth){
32587                 var b = el.getSize(true);
32588                 child.setSize(b.width+adj[0], b.height+adj[1]);
32589             }
32590             // Second call here for IE
32591             // The first call enables instant resizing and
32592             // the second call corrects scroll bars if they
32593             // exist
32594             if(Roo.isIE){
32595                 setTimeout(function(){
32596                     if(el.dom.offsetWidth){
32597                         var b = el.getSize(true);
32598                         child.setSize(b.width+adj[0], b.height+adj[1]);
32599                     }
32600                 }, 10);
32601             }
32602         }
32603     },
32604
32605     // private
32606     snap : function(value, inc, min){
32607         if(!inc || !value) {
32608             return value;
32609         }
32610         var newValue = value;
32611         var m = value % inc;
32612         if(m > 0){
32613             if(m > (inc/2)){
32614                 newValue = value + (inc-m);
32615             }else{
32616                 newValue = value - m;
32617             }
32618         }
32619         return Math.max(min, newValue);
32620     },
32621
32622     // private
32623     resizeElement : function(){
32624         var box = this.proxy.getBox();
32625         if(this.updateBox){
32626             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32627         }else{
32628             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32629         }
32630         this.updateChildSize();
32631         if(!this.dynamic){
32632             this.proxy.hide();
32633         }
32634         return box;
32635     },
32636
32637     // private
32638     constrain : function(v, diff, m, mx){
32639         if(v - diff < m){
32640             diff = v - m;
32641         }else if(v - diff > mx){
32642             diff = mx - v;
32643         }
32644         return diff;
32645     },
32646
32647     // private
32648     onMouseMove : function(e){
32649         
32650         if(this.enabled){
32651             try{// try catch so if something goes wrong the user doesn't get hung
32652
32653             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32654                 return;
32655             }
32656
32657             //var curXY = this.startPoint;
32658             var curSize = this.curSize || this.startBox;
32659             var x = this.startBox.x, y = this.startBox.y;
32660             var ox = x, oy = y;
32661             var w = curSize.width, h = curSize.height;
32662             var ow = w, oh = h;
32663             var mw = this.minWidth, mh = this.minHeight;
32664             var mxw = this.maxWidth, mxh = this.maxHeight;
32665             var wi = this.widthIncrement;
32666             var hi = this.heightIncrement;
32667
32668             var eventXY = e.getXY();
32669             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32670             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32671
32672             var pos = this.activeHandle.position;
32673
32674             switch(pos){
32675                 case "east":
32676                     w += diffX;
32677                     w = Math.min(Math.max(mw, w), mxw);
32678                     break;
32679              
32680                 case "south":
32681                     h += diffY;
32682                     h = Math.min(Math.max(mh, h), mxh);
32683                     break;
32684                 case "southeast":
32685                     w += diffX;
32686                     h += diffY;
32687                     w = Math.min(Math.max(mw, w), mxw);
32688                     h = Math.min(Math.max(mh, h), mxh);
32689                     break;
32690                 case "north":
32691                     diffY = this.constrain(h, diffY, mh, mxh);
32692                     y += diffY;
32693                     h -= diffY;
32694                     break;
32695                 case "hdrag":
32696                     
32697                     if (wi) {
32698                         var adiffX = Math.abs(diffX);
32699                         var sub = (adiffX % wi); // how much 
32700                         if (sub > (wi/2)) { // far enough to snap
32701                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32702                         } else {
32703                             // remove difference.. 
32704                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32705                         }
32706                     }
32707                     x += diffX;
32708                     x = Math.max(this.minX, x);
32709                     break;
32710                 case "west":
32711                     diffX = this.constrain(w, diffX, mw, mxw);
32712                     x += diffX;
32713                     w -= diffX;
32714                     break;
32715                 case "northeast":
32716                     w += diffX;
32717                     w = Math.min(Math.max(mw, w), mxw);
32718                     diffY = this.constrain(h, diffY, mh, mxh);
32719                     y += diffY;
32720                     h -= diffY;
32721                     break;
32722                 case "northwest":
32723                     diffX = this.constrain(w, diffX, mw, mxw);
32724                     diffY = this.constrain(h, diffY, mh, mxh);
32725                     y += diffY;
32726                     h -= diffY;
32727                     x += diffX;
32728                     w -= diffX;
32729                     break;
32730                case "southwest":
32731                     diffX = this.constrain(w, diffX, mw, mxw);
32732                     h += diffY;
32733                     h = Math.min(Math.max(mh, h), mxh);
32734                     x += diffX;
32735                     w -= diffX;
32736                     break;
32737             }
32738
32739             var sw = this.snap(w, wi, mw);
32740             var sh = this.snap(h, hi, mh);
32741             if(sw != w || sh != h){
32742                 switch(pos){
32743                     case "northeast":
32744                         y -= sh - h;
32745                     break;
32746                     case "north":
32747                         y -= sh - h;
32748                         break;
32749                     case "southwest":
32750                         x -= sw - w;
32751                     break;
32752                     case "west":
32753                         x -= sw - w;
32754                         break;
32755                     case "northwest":
32756                         x -= sw - w;
32757                         y -= sh - h;
32758                     break;
32759                 }
32760                 w = sw;
32761                 h = sh;
32762             }
32763
32764             if(this.preserveRatio){
32765                 switch(pos){
32766                     case "southeast":
32767                     case "east":
32768                         h = oh * (w/ow);
32769                         h = Math.min(Math.max(mh, h), mxh);
32770                         w = ow * (h/oh);
32771                        break;
32772                     case "south":
32773                         w = ow * (h/oh);
32774                         w = Math.min(Math.max(mw, w), mxw);
32775                         h = oh * (w/ow);
32776                         break;
32777                     case "northeast":
32778                         w = ow * (h/oh);
32779                         w = Math.min(Math.max(mw, w), mxw);
32780                         h = oh * (w/ow);
32781                     break;
32782                     case "north":
32783                         var tw = w;
32784                         w = ow * (h/oh);
32785                         w = Math.min(Math.max(mw, w), mxw);
32786                         h = oh * (w/ow);
32787                         x += (tw - w) / 2;
32788                         break;
32789                     case "southwest":
32790                         h = oh * (w/ow);
32791                         h = Math.min(Math.max(mh, h), mxh);
32792                         var tw = w;
32793                         w = ow * (h/oh);
32794                         x += tw - w;
32795                         break;
32796                     case "west":
32797                         var th = h;
32798                         h = oh * (w/ow);
32799                         h = Math.min(Math.max(mh, h), mxh);
32800                         y += (th - h) / 2;
32801                         var tw = w;
32802                         w = ow * (h/oh);
32803                         x += tw - w;
32804                        break;
32805                     case "northwest":
32806                         var tw = w;
32807                         var th = h;
32808                         h = oh * (w/ow);
32809                         h = Math.min(Math.max(mh, h), mxh);
32810                         w = ow * (h/oh);
32811                         y += th - h;
32812                         x += tw - w;
32813                        break;
32814
32815                 }
32816             }
32817             if (pos == 'hdrag') {
32818                 w = ow;
32819             }
32820             this.proxy.setBounds(x, y, w, h);
32821             if(this.dynamic){
32822                 this.resizeElement();
32823             }
32824             }catch(e){}
32825         }
32826         this.fireEvent("resizing", this, x, y, w, h, e);
32827     },
32828
32829     // private
32830     handleOver : function(){
32831         if(this.enabled){
32832             this.el.addClass("x-resizable-over");
32833         }
32834     },
32835
32836     // private
32837     handleOut : function(){
32838         if(!this.resizing){
32839             this.el.removeClass("x-resizable-over");
32840         }
32841     },
32842
32843     /**
32844      * Returns the element this component is bound to.
32845      * @return {Roo.Element}
32846      */
32847     getEl : function(){
32848         return this.el;
32849     },
32850
32851     /**
32852      * Returns the resizeChild element (or null).
32853      * @return {Roo.Element}
32854      */
32855     getResizeChild : function(){
32856         return this.resizeChild;
32857     },
32858     groupHandler : function()
32859     {
32860         
32861     },
32862     /**
32863      * Destroys this resizable. If the element was wrapped and
32864      * removeEl is not true then the element remains.
32865      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32866      */
32867     destroy : function(removeEl){
32868         this.proxy.remove();
32869         if(this.overlay){
32870             this.overlay.removeAllListeners();
32871             this.overlay.remove();
32872         }
32873         var ps = Roo.Resizable.positions;
32874         for(var k in ps){
32875             if(typeof ps[k] != "function" && this[ps[k]]){
32876                 var h = this[ps[k]];
32877                 h.el.removeAllListeners();
32878                 h.el.remove();
32879             }
32880         }
32881         if(removeEl){
32882             this.el.update("");
32883             this.el.remove();
32884         }
32885     }
32886 });
32887
32888 // private
32889 // hash to map config positions to true positions
32890 Roo.Resizable.positions = {
32891     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32892     hd: "hdrag"
32893 };
32894
32895 // private
32896 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32897     if(!this.tpl){
32898         // only initialize the template if resizable is used
32899         var tpl = Roo.DomHelper.createTemplate(
32900             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32901         );
32902         tpl.compile();
32903         Roo.Resizable.Handle.prototype.tpl = tpl;
32904     }
32905     this.position = pos;
32906     this.rz = rz;
32907     // show north drag fro topdra
32908     var handlepos = pos == 'hdrag' ? 'north' : pos;
32909     
32910     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32911     if (pos == 'hdrag') {
32912         this.el.setStyle('cursor', 'pointer');
32913     }
32914     this.el.unselectable();
32915     if(transparent){
32916         this.el.setOpacity(0);
32917     }
32918     this.el.on("mousedown", this.onMouseDown, this);
32919     if(!disableTrackOver){
32920         this.el.on("mouseover", this.onMouseOver, this);
32921         this.el.on("mouseout", this.onMouseOut, this);
32922     }
32923 };
32924
32925 // private
32926 Roo.Resizable.Handle.prototype = {
32927     afterResize : function(rz){
32928         Roo.log('after?');
32929         // do nothing
32930     },
32931     // private
32932     onMouseDown : function(e){
32933         this.rz.onMouseDown(this, e);
32934     },
32935     // private
32936     onMouseOver : function(e){
32937         this.rz.handleOver(this, e);
32938     },
32939     // private
32940     onMouseOut : function(e){
32941         this.rz.handleOut(this, e);
32942     }
32943 };/*
32944  * Based on:
32945  * Ext JS Library 1.1.1
32946  * Copyright(c) 2006-2007, Ext JS, LLC.
32947  *
32948  * Originally Released Under LGPL - original licence link has changed is not relivant.
32949  *
32950  * Fork - LGPL
32951  * <script type="text/javascript">
32952  */
32953
32954 /**
32955  * @class Roo.Editor
32956  * @extends Roo.Component
32957  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32958  * @constructor
32959  * Create a new Editor
32960  * @param {Roo.form.Field} field The Field object (or descendant)
32961  * @param {Object} config The config object
32962  */
32963 Roo.Editor = function(field, config){
32964     Roo.Editor.superclass.constructor.call(this, config);
32965     this.field = field;
32966     this.addEvents({
32967         /**
32968              * @event beforestartedit
32969              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32970              * false from the handler of this event.
32971              * @param {Editor} this
32972              * @param {Roo.Element} boundEl The underlying element bound to this editor
32973              * @param {Mixed} value The field value being set
32974              */
32975         "beforestartedit" : true,
32976         /**
32977              * @event startedit
32978              * Fires when this editor is displayed
32979              * @param {Roo.Element} boundEl The underlying element bound to this editor
32980              * @param {Mixed} value The starting field value
32981              */
32982         "startedit" : true,
32983         /**
32984              * @event beforecomplete
32985              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32986              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32987              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32988              * event will not fire since no edit actually occurred.
32989              * @param {Editor} this
32990              * @param {Mixed} value The current field value
32991              * @param {Mixed} startValue The original field value
32992              */
32993         "beforecomplete" : true,
32994         /**
32995              * @event complete
32996              * Fires after editing is complete and any changed value has been written to the underlying field.
32997              * @param {Editor} this
32998              * @param {Mixed} value The current field value
32999              * @param {Mixed} startValue The original field value
33000              */
33001         "complete" : true,
33002         /**
33003          * @event specialkey
33004          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33005          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33006          * @param {Roo.form.Field} this
33007          * @param {Roo.EventObject} e The event object
33008          */
33009         "specialkey" : true
33010     });
33011 };
33012
33013 Roo.extend(Roo.Editor, Roo.Component, {
33014     /**
33015      * @cfg {Boolean/String} autosize
33016      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33017      * or "height" to adopt the height only (defaults to false)
33018      */
33019     /**
33020      * @cfg {Boolean} revertInvalid
33021      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33022      * validation fails (defaults to true)
33023      */
33024     /**
33025      * @cfg {Boolean} ignoreNoChange
33026      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33027      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33028      * will never be ignored.
33029      */
33030     /**
33031      * @cfg {Boolean} hideEl
33032      * False to keep the bound element visible while the editor is displayed (defaults to true)
33033      */
33034     /**
33035      * @cfg {Mixed} value
33036      * The data value of the underlying field (defaults to "")
33037      */
33038     value : "",
33039     /**
33040      * @cfg {String} alignment
33041      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33042      */
33043     alignment: "c-c?",
33044     /**
33045      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33046      * for bottom-right shadow (defaults to "frame")
33047      */
33048     shadow : "frame",
33049     /**
33050      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33051      */
33052     constrain : false,
33053     /**
33054      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33055      */
33056     completeOnEnter : false,
33057     /**
33058      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33059      */
33060     cancelOnEsc : false,
33061     /**
33062      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33063      */
33064     updateEl : false,
33065
33066     // private
33067     onRender : function(ct, position){
33068         this.el = new Roo.Layer({
33069             shadow: this.shadow,
33070             cls: "x-editor",
33071             parentEl : ct,
33072             shim : this.shim,
33073             shadowOffset:4,
33074             id: this.id,
33075             constrain: this.constrain
33076         });
33077         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33078         if(this.field.msgTarget != 'title'){
33079             this.field.msgTarget = 'qtip';
33080         }
33081         this.field.render(this.el);
33082         if(Roo.isGecko){
33083             this.field.el.dom.setAttribute('autocomplete', 'off');
33084         }
33085         this.field.on("specialkey", this.onSpecialKey, this);
33086         if(this.swallowKeys){
33087             this.field.el.swallowEvent(['keydown','keypress']);
33088         }
33089         this.field.show();
33090         this.field.on("blur", this.onBlur, this);
33091         if(this.field.grow){
33092             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33093         }
33094     },
33095
33096     onSpecialKey : function(field, e)
33097     {
33098         //Roo.log('editor onSpecialKey');
33099         if(this.completeOnEnter && e.getKey() == e.ENTER){
33100             e.stopEvent();
33101             this.completeEdit();
33102             return;
33103         }
33104         // do not fire special key otherwise it might hide close the editor...
33105         if(e.getKey() == e.ENTER){    
33106             return;
33107         }
33108         if(this.cancelOnEsc && e.getKey() == e.ESC){
33109             this.cancelEdit();
33110             return;
33111         } 
33112         this.fireEvent('specialkey', field, e);
33113     
33114     },
33115
33116     /**
33117      * Starts the editing process and shows the editor.
33118      * @param {String/HTMLElement/Element} el The element to edit
33119      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33120       * to the innerHTML of el.
33121      */
33122     startEdit : function(el, value){
33123         if(this.editing){
33124             this.completeEdit();
33125         }
33126         this.boundEl = Roo.get(el);
33127         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33128         if(!this.rendered){
33129             this.render(this.parentEl || document.body);
33130         }
33131         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33132             return;
33133         }
33134         this.startValue = v;
33135         this.field.setValue(v);
33136         if(this.autoSize){
33137             var sz = this.boundEl.getSize();
33138             switch(this.autoSize){
33139                 case "width":
33140                 this.setSize(sz.width,  "");
33141                 break;
33142                 case "height":
33143                 this.setSize("",  sz.height);
33144                 break;
33145                 default:
33146                 this.setSize(sz.width,  sz.height);
33147             }
33148         }
33149         this.el.alignTo(this.boundEl, this.alignment);
33150         this.editing = true;
33151         if(Roo.QuickTips){
33152             Roo.QuickTips.disable();
33153         }
33154         this.show();
33155     },
33156
33157     /**
33158      * Sets the height and width of this editor.
33159      * @param {Number} width The new width
33160      * @param {Number} height The new height
33161      */
33162     setSize : function(w, h){
33163         this.field.setSize(w, h);
33164         if(this.el){
33165             this.el.sync();
33166         }
33167     },
33168
33169     /**
33170      * Realigns the editor to the bound field based on the current alignment config value.
33171      */
33172     realign : function(){
33173         this.el.alignTo(this.boundEl, this.alignment);
33174     },
33175
33176     /**
33177      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33178      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33179      */
33180     completeEdit : function(remainVisible){
33181         if(!this.editing){
33182             return;
33183         }
33184         var v = this.getValue();
33185         if(this.revertInvalid !== false && !this.field.isValid()){
33186             v = this.startValue;
33187             this.cancelEdit(true);
33188         }
33189         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33190             this.editing = false;
33191             this.hide();
33192             return;
33193         }
33194         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33195             this.editing = false;
33196             if(this.updateEl && this.boundEl){
33197                 this.boundEl.update(v);
33198             }
33199             if(remainVisible !== true){
33200                 this.hide();
33201             }
33202             this.fireEvent("complete", this, v, this.startValue);
33203         }
33204     },
33205
33206     // private
33207     onShow : function(){
33208         this.el.show();
33209         if(this.hideEl !== false){
33210             this.boundEl.hide();
33211         }
33212         this.field.show();
33213         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33214             this.fixIEFocus = true;
33215             this.deferredFocus.defer(50, this);
33216         }else{
33217             this.field.focus();
33218         }
33219         this.fireEvent("startedit", this.boundEl, this.startValue);
33220     },
33221
33222     deferredFocus : function(){
33223         if(this.editing){
33224             this.field.focus();
33225         }
33226     },
33227
33228     /**
33229      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33230      * reverted to the original starting value.
33231      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33232      * cancel (defaults to false)
33233      */
33234     cancelEdit : function(remainVisible){
33235         if(this.editing){
33236             this.setValue(this.startValue);
33237             if(remainVisible !== true){
33238                 this.hide();
33239             }
33240         }
33241     },
33242
33243     // private
33244     onBlur : function(){
33245         if(this.allowBlur !== true && this.editing){
33246             this.completeEdit();
33247         }
33248     },
33249
33250     // private
33251     onHide : function(){
33252         if(this.editing){
33253             this.completeEdit();
33254             return;
33255         }
33256         this.field.blur();
33257         if(this.field.collapse){
33258             this.field.collapse();
33259         }
33260         this.el.hide();
33261         if(this.hideEl !== false){
33262             this.boundEl.show();
33263         }
33264         if(Roo.QuickTips){
33265             Roo.QuickTips.enable();
33266         }
33267     },
33268
33269     /**
33270      * Sets the data value of the editor
33271      * @param {Mixed} value Any valid value supported by the underlying field
33272      */
33273     setValue : function(v){
33274         this.field.setValue(v);
33275     },
33276
33277     /**
33278      * Gets the data value of the editor
33279      * @return {Mixed} The data value
33280      */
33281     getValue : function(){
33282         return this.field.getValue();
33283     }
33284 });/*
33285  * Based on:
33286  * Ext JS Library 1.1.1
33287  * Copyright(c) 2006-2007, Ext JS, LLC.
33288  *
33289  * Originally Released Under LGPL - original licence link has changed is not relivant.
33290  *
33291  * Fork - LGPL
33292  * <script type="text/javascript">
33293  */
33294  
33295 /**
33296  * @class Roo.BasicDialog
33297  * @extends Roo.util.Observable
33298  * @parent none builder
33299  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33300  * <pre><code>
33301 var dlg = new Roo.BasicDialog("my-dlg", {
33302     height: 200,
33303     width: 300,
33304     minHeight: 100,
33305     minWidth: 150,
33306     modal: true,
33307     proxyDrag: true,
33308     shadow: true
33309 });
33310 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33311 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33312 dlg.addButton('Cancel', dlg.hide, dlg);
33313 dlg.show();
33314 </code></pre>
33315   <b>A Dialog should always be a direct child of the body element.</b>
33316  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33317  * @cfg {String} title Default text to display in the title bar (defaults to null)
33318  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33319  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33320  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33321  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33322  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33323  * (defaults to null with no animation)
33324  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33325  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33326  * property for valid values (defaults to 'all')
33327  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33328  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33329  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33330  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33331  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33332  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33333  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33334  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33335  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33336  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33337  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33338  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33339  * draggable = true (defaults to false)
33340  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33341  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33342  * shadow (defaults to false)
33343  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33344  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33345  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33346  * @cfg {Array} buttons Array of buttons
33347  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33348  * @constructor
33349  * Create a new BasicDialog.
33350  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33351  * @param {Object} config Configuration options
33352  */
33353 Roo.BasicDialog = function(el, config){
33354     this.el = Roo.get(el);
33355     var dh = Roo.DomHelper;
33356     if(!this.el && config && config.autoCreate){
33357         if(typeof config.autoCreate == "object"){
33358             if(!config.autoCreate.id){
33359                 config.autoCreate.id = el;
33360             }
33361             this.el = dh.append(document.body,
33362                         config.autoCreate, true);
33363         }else{
33364             this.el = dh.append(document.body,
33365                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33366         }
33367     }
33368     el = this.el;
33369     el.setDisplayed(true);
33370     el.hide = this.hideAction;
33371     this.id = el.id;
33372     el.addClass("x-dlg");
33373
33374     Roo.apply(this, config);
33375
33376     this.proxy = el.createProxy("x-dlg-proxy");
33377     this.proxy.hide = this.hideAction;
33378     this.proxy.setOpacity(.5);
33379     this.proxy.hide();
33380
33381     if(config.width){
33382         el.setWidth(config.width);
33383     }
33384     if(config.height){
33385         el.setHeight(config.height);
33386     }
33387     this.size = el.getSize();
33388     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33389         this.xy = [config.x,config.y];
33390     }else{
33391         this.xy = el.getCenterXY(true);
33392     }
33393     /** The header element @type Roo.Element */
33394     this.header = el.child("> .x-dlg-hd");
33395     /** The body element @type Roo.Element */
33396     this.body = el.child("> .x-dlg-bd");
33397     /** The footer element @type Roo.Element */
33398     this.footer = el.child("> .x-dlg-ft");
33399
33400     if(!this.header){
33401         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33402     }
33403     if(!this.body){
33404         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33405     }
33406
33407     this.header.unselectable();
33408     if(this.title){
33409         this.header.update(this.title);
33410     }
33411     // this element allows the dialog to be focused for keyboard event
33412     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33413     this.focusEl.swallowEvent("click", true);
33414
33415     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33416
33417     // wrap the body and footer for special rendering
33418     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33419     if(this.footer){
33420         this.bwrap.dom.appendChild(this.footer.dom);
33421     }
33422
33423     this.bg = this.el.createChild({
33424         tag: "div", cls:"x-dlg-bg",
33425         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33426     });
33427     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33428
33429
33430     if(this.autoScroll !== false && !this.autoTabs){
33431         this.body.setStyle("overflow", "auto");
33432     }
33433
33434     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33435
33436     if(this.closable !== false){
33437         this.el.addClass("x-dlg-closable");
33438         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33439         this.close.on("click", this.closeClick, this);
33440         this.close.addClassOnOver("x-dlg-close-over");
33441     }
33442     if(this.collapsible !== false){
33443         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33444         this.collapseBtn.on("click", this.collapseClick, this);
33445         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33446         this.header.on("dblclick", this.collapseClick, this);
33447     }
33448     if(this.resizable !== false){
33449         this.el.addClass("x-dlg-resizable");
33450         this.resizer = new Roo.Resizable(el, {
33451             minWidth: this.minWidth || 80,
33452             minHeight:this.minHeight || 80,
33453             handles: this.resizeHandles || "all",
33454             pinned: true
33455         });
33456         this.resizer.on("beforeresize", this.beforeResize, this);
33457         this.resizer.on("resize", this.onResize, this);
33458     }
33459     if(this.draggable !== false){
33460         el.addClass("x-dlg-draggable");
33461         if (!this.proxyDrag) {
33462             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33463         }
33464         else {
33465             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33466         }
33467         dd.setHandleElId(this.header.id);
33468         dd.endDrag = this.endMove.createDelegate(this);
33469         dd.startDrag = this.startMove.createDelegate(this);
33470         dd.onDrag = this.onDrag.createDelegate(this);
33471         dd.scroll = false;
33472         this.dd = dd;
33473     }
33474     if(this.modal){
33475         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33476         this.mask.enableDisplayMode("block");
33477         this.mask.hide();
33478         this.el.addClass("x-dlg-modal");
33479     }
33480     if(this.shadow){
33481         this.shadow = new Roo.Shadow({
33482             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33483             offset : this.shadowOffset
33484         });
33485     }else{
33486         this.shadowOffset = 0;
33487     }
33488     if(Roo.useShims && this.shim !== false){
33489         this.shim = this.el.createShim();
33490         this.shim.hide = this.hideAction;
33491         this.shim.hide();
33492     }else{
33493         this.shim = false;
33494     }
33495     if(this.autoTabs){
33496         this.initTabs();
33497     }
33498     if (this.buttons) { 
33499         var bts= this.buttons;
33500         this.buttons = [];
33501         Roo.each(bts, function(b) {
33502             this.addButton(b);
33503         }, this);
33504     }
33505     
33506     
33507     this.addEvents({
33508         /**
33509          * @event keydown
33510          * Fires when a key is pressed
33511          * @param {Roo.BasicDialog} this
33512          * @param {Roo.EventObject} e
33513          */
33514         "keydown" : true,
33515         /**
33516          * @event move
33517          * Fires when this dialog is moved by the user.
33518          * @param {Roo.BasicDialog} this
33519          * @param {Number} x The new page X
33520          * @param {Number} y The new page Y
33521          */
33522         "move" : true,
33523         /**
33524          * @event resize
33525          * Fires when this dialog is resized by the user.
33526          * @param {Roo.BasicDialog} this
33527          * @param {Number} width The new width
33528          * @param {Number} height The new height
33529          */
33530         "resize" : true,
33531         /**
33532          * @event beforehide
33533          * Fires before this dialog is hidden.
33534          * @param {Roo.BasicDialog} this
33535          */
33536         "beforehide" : true,
33537         /**
33538          * @event hide
33539          * Fires when this dialog is hidden.
33540          * @param {Roo.BasicDialog} this
33541          */
33542         "hide" : true,
33543         /**
33544          * @event beforeshow
33545          * Fires before this dialog is shown.
33546          * @param {Roo.BasicDialog} this
33547          */
33548         "beforeshow" : true,
33549         /**
33550          * @event show
33551          * Fires when this dialog is shown.
33552          * @param {Roo.BasicDialog} this
33553          */
33554         "show" : true
33555     });
33556     el.on("keydown", this.onKeyDown, this);
33557     el.on("mousedown", this.toFront, this);
33558     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33559     this.el.hide();
33560     Roo.DialogManager.register(this);
33561     Roo.BasicDialog.superclass.constructor.call(this);
33562 };
33563
33564 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33565     shadowOffset: Roo.isIE ? 6 : 5,
33566     minHeight: 80,
33567     minWidth: 200,
33568     minButtonWidth: 75,
33569     defaultButton: null,
33570     buttonAlign: "right",
33571     tabTag: 'div',
33572     firstShow: true,
33573
33574     /**
33575      * Sets the dialog title text
33576      * @param {String} text The title text to display
33577      * @return {Roo.BasicDialog} this
33578      */
33579     setTitle : function(text){
33580         this.header.update(text);
33581         return this;
33582     },
33583
33584     // private
33585     closeClick : function(){
33586         this.hide();
33587     },
33588
33589     // private
33590     collapseClick : function(){
33591         this[this.collapsed ? "expand" : "collapse"]();
33592     },
33593
33594     /**
33595      * Collapses the dialog to its minimized state (only the title bar is visible).
33596      * Equivalent to the user clicking the collapse dialog button.
33597      */
33598     collapse : function(){
33599         if(!this.collapsed){
33600             this.collapsed = true;
33601             this.el.addClass("x-dlg-collapsed");
33602             this.restoreHeight = this.el.getHeight();
33603             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33604         }
33605     },
33606
33607     /**
33608      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33609      * clicking the expand dialog button.
33610      */
33611     expand : function(){
33612         if(this.collapsed){
33613             this.collapsed = false;
33614             this.el.removeClass("x-dlg-collapsed");
33615             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33616         }
33617     },
33618
33619     /**
33620      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33621      * @return {Roo.TabPanel} The tabs component
33622      */
33623     initTabs : function(){
33624         var tabs = this.getTabs();
33625         while(tabs.getTab(0)){
33626             tabs.removeTab(0);
33627         }
33628         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33629             var dom = el.dom;
33630             tabs.addTab(Roo.id(dom), dom.title);
33631             dom.title = "";
33632         });
33633         tabs.activate(0);
33634         return tabs;
33635     },
33636
33637     // private
33638     beforeResize : function(){
33639         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33640     },
33641
33642     // private
33643     onResize : function(){
33644         this.refreshSize();
33645         this.syncBodyHeight();
33646         this.adjustAssets();
33647         this.focus();
33648         this.fireEvent("resize", this, this.size.width, this.size.height);
33649     },
33650
33651     // private
33652     onKeyDown : function(e){
33653         if(this.isVisible()){
33654             this.fireEvent("keydown", this, e);
33655         }
33656     },
33657
33658     /**
33659      * Resizes the dialog.
33660      * @param {Number} width
33661      * @param {Number} height
33662      * @return {Roo.BasicDialog} this
33663      */
33664     resizeTo : function(width, height){
33665         this.el.setSize(width, height);
33666         this.size = {width: width, height: height};
33667         this.syncBodyHeight();
33668         if(this.fixedcenter){
33669             this.center();
33670         }
33671         if(this.isVisible()){
33672             this.constrainXY();
33673             this.adjustAssets();
33674         }
33675         this.fireEvent("resize", this, width, height);
33676         return this;
33677     },
33678
33679
33680     /**
33681      * Resizes the dialog to fit the specified content size.
33682      * @param {Number} width
33683      * @param {Number} height
33684      * @return {Roo.BasicDialog} this
33685      */
33686     setContentSize : function(w, h){
33687         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33688         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33689         //if(!this.el.isBorderBox()){
33690             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33691             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33692         //}
33693         if(this.tabs){
33694             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33695             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33696         }
33697         this.resizeTo(w, h);
33698         return this;
33699     },
33700
33701     /**
33702      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33703      * executed in response to a particular key being pressed while the dialog is active.
33704      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33705      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33706      * @param {Function} fn The function to call
33707      * @param {Object} scope (optional) The scope of the function
33708      * @return {Roo.BasicDialog} this
33709      */
33710     addKeyListener : function(key, fn, scope){
33711         var keyCode, shift, ctrl, alt;
33712         if(typeof key == "object" && !(key instanceof Array)){
33713             keyCode = key["key"];
33714             shift = key["shift"];
33715             ctrl = key["ctrl"];
33716             alt = key["alt"];
33717         }else{
33718             keyCode = key;
33719         }
33720         var handler = function(dlg, e){
33721             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33722                 var k = e.getKey();
33723                 if(keyCode instanceof Array){
33724                     for(var i = 0, len = keyCode.length; i < len; i++){
33725                         if(keyCode[i] == k){
33726                           fn.call(scope || window, dlg, k, e);
33727                           return;
33728                         }
33729                     }
33730                 }else{
33731                     if(k == keyCode){
33732                         fn.call(scope || window, dlg, k, e);
33733                     }
33734                 }
33735             }
33736         };
33737         this.on("keydown", handler);
33738         return this;
33739     },
33740
33741     /**
33742      * Returns the TabPanel component (creates it if it doesn't exist).
33743      * Note: If you wish to simply check for the existence of tabs without creating them,
33744      * check for a null 'tabs' property.
33745      * @return {Roo.TabPanel} The tabs component
33746      */
33747     getTabs : function(){
33748         if(!this.tabs){
33749             this.el.addClass("x-dlg-auto-tabs");
33750             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33751             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33752         }
33753         return this.tabs;
33754     },
33755
33756     /**
33757      * Adds a button to the footer section of the dialog.
33758      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33759      * object or a valid Roo.DomHelper element config
33760      * @param {Function} handler The function called when the button is clicked
33761      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33762      * @return {Roo.Button} The new button
33763      */
33764     addButton : function(config, handler, scope){
33765         var dh = Roo.DomHelper;
33766         if(!this.footer){
33767             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33768         }
33769         if(!this.btnContainer){
33770             var tb = this.footer.createChild({
33771
33772                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33773                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33774             }, null, true);
33775             this.btnContainer = tb.firstChild.firstChild.firstChild;
33776         }
33777         var bconfig = {
33778             handler: handler,
33779             scope: scope,
33780             minWidth: this.minButtonWidth,
33781             hideParent:true
33782         };
33783         if(typeof config == "string"){
33784             bconfig.text = config;
33785         }else{
33786             if(config.tag){
33787                 bconfig.dhconfig = config;
33788             }else{
33789                 Roo.apply(bconfig, config);
33790             }
33791         }
33792         var fc = false;
33793         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33794             bconfig.position = Math.max(0, bconfig.position);
33795             fc = this.btnContainer.childNodes[bconfig.position];
33796         }
33797          
33798         var btn = new Roo.Button(
33799             fc ? 
33800                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33801                 : this.btnContainer.appendChild(document.createElement("td")),
33802             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33803             bconfig
33804         );
33805         this.syncBodyHeight();
33806         if(!this.buttons){
33807             /**
33808              * Array of all the buttons that have been added to this dialog via addButton
33809              * @type Array
33810              */
33811             this.buttons = [];
33812         }
33813         this.buttons.push(btn);
33814         return btn;
33815     },
33816
33817     /**
33818      * Sets the default button to be focused when the dialog is displayed.
33819      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33820      * @return {Roo.BasicDialog} this
33821      */
33822     setDefaultButton : function(btn){
33823         this.defaultButton = btn;
33824         return this;
33825     },
33826
33827     // private
33828     getHeaderFooterHeight : function(safe){
33829         var height = 0;
33830         if(this.header){
33831            height += this.header.getHeight();
33832         }
33833         if(this.footer){
33834            var fm = this.footer.getMargins();
33835             height += (this.footer.getHeight()+fm.top+fm.bottom);
33836         }
33837         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33838         height += this.centerBg.getPadding("tb");
33839         return height;
33840     },
33841
33842     // private
33843     syncBodyHeight : function()
33844     {
33845         var bd = this.body, // the text
33846             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33847             bw = this.bwrap;
33848         var height = this.size.height - this.getHeaderFooterHeight(false);
33849         bd.setHeight(height-bd.getMargins("tb"));
33850         var hh = this.header.getHeight();
33851         var h = this.size.height-hh;
33852         cb.setHeight(h);
33853         
33854         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33855         bw.setHeight(h-cb.getPadding("tb"));
33856         
33857         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33858         bd.setWidth(bw.getWidth(true));
33859         if(this.tabs){
33860             this.tabs.syncHeight();
33861             if(Roo.isIE){
33862                 this.tabs.el.repaint();
33863             }
33864         }
33865     },
33866
33867     /**
33868      * Restores the previous state of the dialog if Roo.state is configured.
33869      * @return {Roo.BasicDialog} this
33870      */
33871     restoreState : function(){
33872         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33873         if(box && box.width){
33874             this.xy = [box.x, box.y];
33875             this.resizeTo(box.width, box.height);
33876         }
33877         return this;
33878     },
33879
33880     // private
33881     beforeShow : function(){
33882         this.expand();
33883         if(this.fixedcenter){
33884             this.xy = this.el.getCenterXY(true);
33885         }
33886         if(this.modal){
33887             Roo.get(document.body).addClass("x-body-masked");
33888             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33889             this.mask.show();
33890         }
33891         this.constrainXY();
33892     },
33893
33894     // private
33895     animShow : function(){
33896         var b = Roo.get(this.animateTarget).getBox();
33897         this.proxy.setSize(b.width, b.height);
33898         this.proxy.setLocation(b.x, b.y);
33899         this.proxy.show();
33900         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33901                     true, .35, this.showEl.createDelegate(this));
33902     },
33903
33904     /**
33905      * Shows the dialog.
33906      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33907      * @return {Roo.BasicDialog} this
33908      */
33909     show : function(animateTarget){
33910         if (this.fireEvent("beforeshow", this) === false){
33911             return;
33912         }
33913         if(this.syncHeightBeforeShow){
33914             this.syncBodyHeight();
33915         }else if(this.firstShow){
33916             this.firstShow = false;
33917             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33918         }
33919         this.animateTarget = animateTarget || this.animateTarget;
33920         if(!this.el.isVisible()){
33921             this.beforeShow();
33922             if(this.animateTarget && Roo.get(this.animateTarget)){
33923                 this.animShow();
33924             }else{
33925                 this.showEl();
33926             }
33927         }
33928         return this;
33929     },
33930
33931     // private
33932     showEl : function(){
33933         this.proxy.hide();
33934         this.el.setXY(this.xy);
33935         this.el.show();
33936         this.adjustAssets(true);
33937         this.toFront();
33938         this.focus();
33939         // IE peekaboo bug - fix found by Dave Fenwick
33940         if(Roo.isIE){
33941             this.el.repaint();
33942         }
33943         this.fireEvent("show", this);
33944     },
33945
33946     /**
33947      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33948      * dialog itself will receive focus.
33949      */
33950     focus : function(){
33951         if(this.defaultButton){
33952             this.defaultButton.focus();
33953         }else{
33954             this.focusEl.focus();
33955         }
33956     },
33957
33958     // private
33959     constrainXY : function(){
33960         if(this.constraintoviewport !== false){
33961             if(!this.viewSize){
33962                 if(this.container){
33963                     var s = this.container.getSize();
33964                     this.viewSize = [s.width, s.height];
33965                 }else{
33966                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33967                 }
33968             }
33969             var s = Roo.get(this.container||document).getScroll();
33970
33971             var x = this.xy[0], y = this.xy[1];
33972             var w = this.size.width, h = this.size.height;
33973             var vw = this.viewSize[0], vh = this.viewSize[1];
33974             // only move it if it needs it
33975             var moved = false;
33976             // first validate right/bottom
33977             if(x + w > vw+s.left){
33978                 x = vw - w;
33979                 moved = true;
33980             }
33981             if(y + h > vh+s.top){
33982                 y = vh - h;
33983                 moved = true;
33984             }
33985             // then make sure top/left isn't negative
33986             if(x < s.left){
33987                 x = s.left;
33988                 moved = true;
33989             }
33990             if(y < s.top){
33991                 y = s.top;
33992                 moved = true;
33993             }
33994             if(moved){
33995                 // cache xy
33996                 this.xy = [x, y];
33997                 if(this.isVisible()){
33998                     this.el.setLocation(x, y);
33999                     this.adjustAssets();
34000                 }
34001             }
34002         }
34003     },
34004
34005     // private
34006     onDrag : function(){
34007         if(!this.proxyDrag){
34008             this.xy = this.el.getXY();
34009             this.adjustAssets();
34010         }
34011     },
34012
34013     // private
34014     adjustAssets : function(doShow){
34015         var x = this.xy[0], y = this.xy[1];
34016         var w = this.size.width, h = this.size.height;
34017         if(doShow === true){
34018             if(this.shadow){
34019                 this.shadow.show(this.el);
34020             }
34021             if(this.shim){
34022                 this.shim.show();
34023             }
34024         }
34025         if(this.shadow && this.shadow.isVisible()){
34026             this.shadow.show(this.el);
34027         }
34028         if(this.shim && this.shim.isVisible()){
34029             this.shim.setBounds(x, y, w, h);
34030         }
34031     },
34032
34033     // private
34034     adjustViewport : function(w, h){
34035         if(!w || !h){
34036             w = Roo.lib.Dom.getViewWidth();
34037             h = Roo.lib.Dom.getViewHeight();
34038         }
34039         // cache the size
34040         this.viewSize = [w, h];
34041         if(this.modal && this.mask.isVisible()){
34042             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34043             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34044         }
34045         if(this.isVisible()){
34046             this.constrainXY();
34047         }
34048     },
34049
34050     /**
34051      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34052      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34053      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34054      */
34055     destroy : function(removeEl){
34056         if(this.isVisible()){
34057             this.animateTarget = null;
34058             this.hide();
34059         }
34060         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34061         if(this.tabs){
34062             this.tabs.destroy(removeEl);
34063         }
34064         Roo.destroy(
34065              this.shim,
34066              this.proxy,
34067              this.resizer,
34068              this.close,
34069              this.mask
34070         );
34071         if(this.dd){
34072             this.dd.unreg();
34073         }
34074         if(this.buttons){
34075            for(var i = 0, len = this.buttons.length; i < len; i++){
34076                this.buttons[i].destroy();
34077            }
34078         }
34079         this.el.removeAllListeners();
34080         if(removeEl === true){
34081             this.el.update("");
34082             this.el.remove();
34083         }
34084         Roo.DialogManager.unregister(this);
34085     },
34086
34087     // private
34088     startMove : function(){
34089         if(this.proxyDrag){
34090             this.proxy.show();
34091         }
34092         if(this.constraintoviewport !== false){
34093             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34094         }
34095     },
34096
34097     // private
34098     endMove : function(){
34099         if(!this.proxyDrag){
34100             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34101         }else{
34102             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34103             this.proxy.hide();
34104         }
34105         this.refreshSize();
34106         this.adjustAssets();
34107         this.focus();
34108         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34109     },
34110
34111     /**
34112      * Brings this dialog to the front of any other visible dialogs
34113      * @return {Roo.BasicDialog} this
34114      */
34115     toFront : function(){
34116         Roo.DialogManager.bringToFront(this);
34117         return this;
34118     },
34119
34120     /**
34121      * Sends this dialog to the back (under) of any other visible dialogs
34122      * @return {Roo.BasicDialog} this
34123      */
34124     toBack : function(){
34125         Roo.DialogManager.sendToBack(this);
34126         return this;
34127     },
34128
34129     /**
34130      * Centers this dialog in the viewport
34131      * @return {Roo.BasicDialog} this
34132      */
34133     center : function(){
34134         var xy = this.el.getCenterXY(true);
34135         this.moveTo(xy[0], xy[1]);
34136         return this;
34137     },
34138
34139     /**
34140      * Moves the dialog's top-left corner to the specified point
34141      * @param {Number} x
34142      * @param {Number} y
34143      * @return {Roo.BasicDialog} this
34144      */
34145     moveTo : function(x, y){
34146         this.xy = [x,y];
34147         if(this.isVisible()){
34148             this.el.setXY(this.xy);
34149             this.adjustAssets();
34150         }
34151         return this;
34152     },
34153
34154     /**
34155      * Aligns the dialog to the specified element
34156      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34157      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34158      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34159      * @return {Roo.BasicDialog} this
34160      */
34161     alignTo : function(element, position, offsets){
34162         this.xy = this.el.getAlignToXY(element, position, offsets);
34163         if(this.isVisible()){
34164             this.el.setXY(this.xy);
34165             this.adjustAssets();
34166         }
34167         return this;
34168     },
34169
34170     /**
34171      * Anchors an element to another element and realigns it when the window is resized.
34172      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34173      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34174      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34175      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34176      * is a number, it is used as the buffer delay (defaults to 50ms).
34177      * @return {Roo.BasicDialog} this
34178      */
34179     anchorTo : function(el, alignment, offsets, monitorScroll){
34180         var action = function(){
34181             this.alignTo(el, alignment, offsets);
34182         };
34183         Roo.EventManager.onWindowResize(action, this);
34184         var tm = typeof monitorScroll;
34185         if(tm != 'undefined'){
34186             Roo.EventManager.on(window, 'scroll', action, this,
34187                 {buffer: tm == 'number' ? monitorScroll : 50});
34188         }
34189         action.call(this);
34190         return this;
34191     },
34192
34193     /**
34194      * Returns true if the dialog is visible
34195      * @return {Boolean}
34196      */
34197     isVisible : function(){
34198         return this.el.isVisible();
34199     },
34200
34201     // private
34202     animHide : function(callback){
34203         var b = Roo.get(this.animateTarget).getBox();
34204         this.proxy.show();
34205         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34206         this.el.hide();
34207         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34208                     this.hideEl.createDelegate(this, [callback]));
34209     },
34210
34211     /**
34212      * Hides the dialog.
34213      * @param {Function} callback (optional) Function to call when the dialog is hidden
34214      * @return {Roo.BasicDialog} this
34215      */
34216     hide : function(callback){
34217         if (this.fireEvent("beforehide", this) === false){
34218             return;
34219         }
34220         if(this.shadow){
34221             this.shadow.hide();
34222         }
34223         if(this.shim) {
34224           this.shim.hide();
34225         }
34226         // sometimes animateTarget seems to get set.. causing problems...
34227         // this just double checks..
34228         if(this.animateTarget && Roo.get(this.animateTarget)) {
34229            this.animHide(callback);
34230         }else{
34231             this.el.hide();
34232             this.hideEl(callback);
34233         }
34234         return this;
34235     },
34236
34237     // private
34238     hideEl : function(callback){
34239         this.proxy.hide();
34240         if(this.modal){
34241             this.mask.hide();
34242             Roo.get(document.body).removeClass("x-body-masked");
34243         }
34244         this.fireEvent("hide", this);
34245         if(typeof callback == "function"){
34246             callback();
34247         }
34248     },
34249
34250     // private
34251     hideAction : function(){
34252         this.setLeft("-10000px");
34253         this.setTop("-10000px");
34254         this.setStyle("visibility", "hidden");
34255     },
34256
34257     // private
34258     refreshSize : function(){
34259         this.size = this.el.getSize();
34260         this.xy = this.el.getXY();
34261         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34262     },
34263
34264     // private
34265     // z-index is managed by the DialogManager and may be overwritten at any time
34266     setZIndex : function(index){
34267         if(this.modal){
34268             this.mask.setStyle("z-index", index);
34269         }
34270         if(this.shim){
34271             this.shim.setStyle("z-index", ++index);
34272         }
34273         if(this.shadow){
34274             this.shadow.setZIndex(++index);
34275         }
34276         this.el.setStyle("z-index", ++index);
34277         if(this.proxy){
34278             this.proxy.setStyle("z-index", ++index);
34279         }
34280         if(this.resizer){
34281             this.resizer.proxy.setStyle("z-index", ++index);
34282         }
34283
34284         this.lastZIndex = index;
34285     },
34286
34287     /**
34288      * Returns the element for this dialog
34289      * @return {Roo.Element} The underlying dialog Element
34290      */
34291     getEl : function(){
34292         return this.el;
34293     }
34294 });
34295
34296 /**
34297  * @class Roo.DialogManager
34298  * Provides global access to BasicDialogs that have been created and
34299  * support for z-indexing (layering) multiple open dialogs.
34300  */
34301 Roo.DialogManager = function(){
34302     var list = {};
34303     var accessList = [];
34304     var front = null;
34305
34306     // private
34307     var sortDialogs = function(d1, d2){
34308         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34309     };
34310
34311     // private
34312     var orderDialogs = function(){
34313         accessList.sort(sortDialogs);
34314         var seed = Roo.DialogManager.zseed;
34315         for(var i = 0, len = accessList.length; i < len; i++){
34316             var dlg = accessList[i];
34317             if(dlg){
34318                 dlg.setZIndex(seed + (i*10));
34319             }
34320         }
34321     };
34322
34323     return {
34324         /**
34325          * The starting z-index for BasicDialogs (defaults to 9000)
34326          * @type Number The z-index value
34327          */
34328         zseed : 9000,
34329
34330         // private
34331         register : function(dlg){
34332             list[dlg.id] = dlg;
34333             accessList.push(dlg);
34334         },
34335
34336         // private
34337         unregister : function(dlg){
34338             delete list[dlg.id];
34339             var i=0;
34340             var len=0;
34341             if(!accessList.indexOf){
34342                 for(  i = 0, len = accessList.length; i < len; i++){
34343                     if(accessList[i] == dlg){
34344                         accessList.splice(i, 1);
34345                         return;
34346                     }
34347                 }
34348             }else{
34349                  i = accessList.indexOf(dlg);
34350                 if(i != -1){
34351                     accessList.splice(i, 1);
34352                 }
34353             }
34354         },
34355
34356         /**
34357          * Gets a registered dialog by id
34358          * @param {String/Object} id The id of the dialog or a dialog
34359          * @return {Roo.BasicDialog} this
34360          */
34361         get : function(id){
34362             return typeof id == "object" ? id : list[id];
34363         },
34364
34365         /**
34366          * Brings the specified dialog to the front
34367          * @param {String/Object} dlg The id of the dialog or a dialog
34368          * @return {Roo.BasicDialog} this
34369          */
34370         bringToFront : function(dlg){
34371             dlg = this.get(dlg);
34372             if(dlg != front){
34373                 front = dlg;
34374                 dlg._lastAccess = new Date().getTime();
34375                 orderDialogs();
34376             }
34377             return dlg;
34378         },
34379
34380         /**
34381          * Sends the specified dialog to the back
34382          * @param {String/Object} dlg The id of the dialog or a dialog
34383          * @return {Roo.BasicDialog} this
34384          */
34385         sendToBack : function(dlg){
34386             dlg = this.get(dlg);
34387             dlg._lastAccess = -(new Date().getTime());
34388             orderDialogs();
34389             return dlg;
34390         },
34391
34392         /**
34393          * Hides all dialogs
34394          */
34395         hideAll : function(){
34396             for(var id in list){
34397                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34398                     list[id].hide();
34399                 }
34400             }
34401         }
34402     };
34403 }();
34404
34405 /**
34406  * @class Roo.LayoutDialog
34407  * @extends Roo.BasicDialog
34408  * @children Roo.ContentPanel
34409  * @parent builder none
34410  * Dialog which provides adjustments for working with a layout in a Dialog.
34411  * Add your necessary layout config options to the dialog's config.<br>
34412  * Example usage (including a nested layout):
34413  * <pre><code>
34414 if(!dialog){
34415     dialog = new Roo.LayoutDialog("download-dlg", {
34416         modal: true,
34417         width:600,
34418         height:450,
34419         shadow:true,
34420         minWidth:500,
34421         minHeight:350,
34422         autoTabs:true,
34423         proxyDrag:true,
34424         // layout config merges with the dialog config
34425         center:{
34426             tabPosition: "top",
34427             alwaysShowTabs: true
34428         }
34429     });
34430     dialog.addKeyListener(27, dialog.hide, dialog);
34431     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34432     dialog.addButton("Build It!", this.getDownload, this);
34433
34434     // we can even add nested layouts
34435     var innerLayout = new Roo.BorderLayout("dl-inner", {
34436         east: {
34437             initialSize: 200,
34438             autoScroll:true,
34439             split:true
34440         },
34441         center: {
34442             autoScroll:true
34443         }
34444     });
34445     innerLayout.beginUpdate();
34446     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34447     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34448     innerLayout.endUpdate(true);
34449
34450     var layout = dialog.getLayout();
34451     layout.beginUpdate();
34452     layout.add("center", new Roo.ContentPanel("standard-panel",
34453                         {title: "Download the Source", fitToFrame:true}));
34454     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34455                {title: "Build your own roo.js"}));
34456     layout.getRegion("center").showPanel(sp);
34457     layout.endUpdate();
34458 }
34459 </code></pre>
34460     * @constructor
34461     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34462     * @param {Object} config configuration options
34463   */
34464 Roo.LayoutDialog = function(el, cfg){
34465     
34466     var config=  cfg;
34467     if (typeof(cfg) == 'undefined') {
34468         config = Roo.apply({}, el);
34469         // not sure why we use documentElement here.. - it should always be body.
34470         // IE7 borks horribly if we use documentElement.
34471         // webkit also does not like documentElement - it creates a body element...
34472         el = Roo.get( document.body || document.documentElement ).createChild();
34473         //config.autoCreate = true;
34474     }
34475     
34476     
34477     config.autoTabs = false;
34478     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34479     this.body.setStyle({overflow:"hidden", position:"relative"});
34480     this.layout = new Roo.BorderLayout(this.body.dom, config);
34481     this.layout.monitorWindowResize = false;
34482     this.el.addClass("x-dlg-auto-layout");
34483     // fix case when center region overwrites center function
34484     this.center = Roo.BasicDialog.prototype.center;
34485     this.on("show", this.layout.layout, this.layout, true);
34486     if (config.items) {
34487         var xitems = config.items;
34488         delete config.items;
34489         Roo.each(xitems, this.addxtype, this);
34490     }
34491     
34492     
34493 };
34494 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34495     
34496     
34497     /**
34498      * @cfg {Roo.LayoutRegion} east  
34499      */
34500     /**
34501      * @cfg {Roo.LayoutRegion} west
34502      */
34503     /**
34504      * @cfg {Roo.LayoutRegion} south
34505      */
34506     /**
34507      * @cfg {Roo.LayoutRegion} north
34508      */
34509     /**
34510      * @cfg {Roo.LayoutRegion} center
34511      */
34512     /**
34513      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34514      */
34515     
34516     
34517     /**
34518      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34519      * @deprecated
34520      */
34521     endUpdate : function(){
34522         this.layout.endUpdate();
34523     },
34524
34525     /**
34526      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34527      *  @deprecated
34528      */
34529     beginUpdate : function(){
34530         this.layout.beginUpdate();
34531     },
34532
34533     /**
34534      * Get the BorderLayout for this dialog
34535      * @return {Roo.BorderLayout}
34536      */
34537     getLayout : function(){
34538         return this.layout;
34539     },
34540
34541     showEl : function(){
34542         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34543         if(Roo.isIE7){
34544             this.layout.layout();
34545         }
34546     },
34547
34548     // private
34549     // Use the syncHeightBeforeShow config option to control this automatically
34550     syncBodyHeight : function(){
34551         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34552         if(this.layout){this.layout.layout();}
34553     },
34554     
34555       /**
34556      * Add an xtype element (actually adds to the layout.)
34557      * @return {Object} xdata xtype object data.
34558      */
34559     
34560     addxtype : function(c) {
34561         return this.layout.addxtype(c);
34562     }
34563 });/*
34564  * Based on:
34565  * Ext JS Library 1.1.1
34566  * Copyright(c) 2006-2007, Ext JS, LLC.
34567  *
34568  * Originally Released Under LGPL - original licence link has changed is not relivant.
34569  *
34570  * Fork - LGPL
34571  * <script type="text/javascript">
34572  */
34573  
34574 /**
34575  * @class Roo.MessageBox
34576  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34577  * Example usage:
34578  *<pre><code>
34579 // Basic alert:
34580 Roo.Msg.alert('Status', 'Changes saved successfully.');
34581
34582 // Prompt for user data:
34583 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34584     if (btn == 'ok'){
34585         // process text value...
34586     }
34587 });
34588
34589 // Show a dialog using config options:
34590 Roo.Msg.show({
34591    title:'Save Changes?',
34592    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34593    buttons: Roo.Msg.YESNOCANCEL,
34594    fn: processResult,
34595    animEl: 'elId'
34596 });
34597 </code></pre>
34598  * @static
34599  */
34600 Roo.MessageBox = function(){
34601     var dlg, opt, mask, waitTimer;
34602     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34603     var buttons, activeTextEl, bwidth;
34604
34605     // private
34606     var handleButton = function(button){
34607         dlg.hide();
34608         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34609     };
34610
34611     // private
34612     var handleHide = function(){
34613         if(opt && opt.cls){
34614             dlg.el.removeClass(opt.cls);
34615         }
34616         if(waitTimer){
34617             Roo.TaskMgr.stop(waitTimer);
34618             waitTimer = null;
34619         }
34620     };
34621
34622     // private
34623     var updateButtons = function(b){
34624         var width = 0;
34625         if(!b){
34626             buttons["ok"].hide();
34627             buttons["cancel"].hide();
34628             buttons["yes"].hide();
34629             buttons["no"].hide();
34630             dlg.footer.dom.style.display = 'none';
34631             return width;
34632         }
34633         dlg.footer.dom.style.display = '';
34634         for(var k in buttons){
34635             if(typeof buttons[k] != "function"){
34636                 if(b[k]){
34637                     buttons[k].show();
34638                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34639                     width += buttons[k].el.getWidth()+15;
34640                 }else{
34641                     buttons[k].hide();
34642                 }
34643             }
34644         }
34645         return width;
34646     };
34647
34648     // private
34649     var handleEsc = function(d, k, e){
34650         if(opt && opt.closable !== false){
34651             dlg.hide();
34652         }
34653         if(e){
34654             e.stopEvent();
34655         }
34656     };
34657
34658     return {
34659         /**
34660          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34661          * @return {Roo.BasicDialog} The BasicDialog element
34662          */
34663         getDialog : function(){
34664            if(!dlg){
34665                 dlg = new Roo.BasicDialog("x-msg-box", {
34666                     autoCreate : true,
34667                     shadow: true,
34668                     draggable: true,
34669                     resizable:false,
34670                     constraintoviewport:false,
34671                     fixedcenter:true,
34672                     collapsible : false,
34673                     shim:true,
34674                     modal: true,
34675                     width:400, height:100,
34676                     buttonAlign:"center",
34677                     closeClick : function(){
34678                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34679                             handleButton("no");
34680                         }else{
34681                             handleButton("cancel");
34682                         }
34683                     }
34684                 });
34685                 dlg.on("hide", handleHide);
34686                 mask = dlg.mask;
34687                 dlg.addKeyListener(27, handleEsc);
34688                 buttons = {};
34689                 var bt = this.buttonText;
34690                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34691                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34692                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34693                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34694                 bodyEl = dlg.body.createChild({
34695
34696                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
34697                 });
34698                 msgEl = bodyEl.dom.firstChild;
34699                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34700                 textboxEl.enableDisplayMode();
34701                 textboxEl.addKeyListener([10,13], function(){
34702                     if(dlg.isVisible() && opt && opt.buttons){
34703                         if(opt.buttons.ok){
34704                             handleButton("ok");
34705                         }else if(opt.buttons.yes){
34706                             handleButton("yes");
34707                         }
34708                     }
34709                 });
34710                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34711                 textareaEl.enableDisplayMode();
34712                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34713                 progressEl.enableDisplayMode();
34714                 var pf = progressEl.dom.firstChild;
34715                 if (pf) {
34716                     pp = Roo.get(pf.firstChild);
34717                     pp.setHeight(pf.offsetHeight);
34718                 }
34719                 
34720             }
34721             return dlg;
34722         },
34723
34724         /**
34725          * Updates the message box body text
34726          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34727          * the XHTML-compliant non-breaking space character '&amp;#160;')
34728          * @return {Roo.MessageBox} This message box
34729          */
34730         updateText : function(text){
34731             if(!dlg.isVisible() && !opt.width){
34732                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34733             }
34734             msgEl.innerHTML = text || '&#160;';
34735       
34736             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34737             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34738             var w = Math.max(
34739                     Math.min(opt.width || cw , this.maxWidth), 
34740                     Math.max(opt.minWidth || this.minWidth, bwidth)
34741             );
34742             if(opt.prompt){
34743                 activeTextEl.setWidth(w);
34744             }
34745             if(dlg.isVisible()){
34746                 dlg.fixedcenter = false;
34747             }
34748             // to big, make it scroll. = But as usual stupid IE does not support
34749             // !important..
34750             
34751             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34752                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34753                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34754             } else {
34755                 bodyEl.dom.style.height = '';
34756                 bodyEl.dom.style.overflowY = '';
34757             }
34758             if (cw > w) {
34759                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34760             } else {
34761                 bodyEl.dom.style.overflowX = '';
34762             }
34763             
34764             dlg.setContentSize(w, bodyEl.getHeight());
34765             if(dlg.isVisible()){
34766                 dlg.fixedcenter = true;
34767             }
34768             return this;
34769         },
34770
34771         /**
34772          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34773          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34774          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34775          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34776          * @return {Roo.MessageBox} This message box
34777          */
34778         updateProgress : function(value, text){
34779             if(text){
34780                 this.updateText(text);
34781             }
34782             if (pp) { // weird bug on my firefox - for some reason this is not defined
34783                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34784             }
34785             return this;
34786         },        
34787
34788         /**
34789          * Returns true if the message box is currently displayed
34790          * @return {Boolean} True if the message box is visible, else false
34791          */
34792         isVisible : function(){
34793             return dlg && dlg.isVisible();  
34794         },
34795
34796         /**
34797          * Hides the message box if it is displayed
34798          */
34799         hide : function(){
34800             if(this.isVisible()){
34801                 dlg.hide();
34802             }  
34803         },
34804
34805         /**
34806          * Displays a new message box, or reinitializes an existing message box, based on the config options
34807          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34808          * The following config object properties are supported:
34809          * <pre>
34810 Property    Type             Description
34811 ----------  ---------------  ------------------------------------------------------------------------------------
34812 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34813                                    closes (defaults to undefined)
34814 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34815                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34816 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34817                                    progress and wait dialogs will ignore this property and always hide the
34818                                    close button as they can only be closed programmatically.
34819 cls               String           A custom CSS class to apply to the message box element
34820 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34821                                    displayed (defaults to 75)
34822 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34823                                    function will be btn (the name of the button that was clicked, if applicable,
34824                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34825                                    Progress and wait dialogs will ignore this option since they do not respond to
34826                                    user actions and can only be closed programmatically, so any required function
34827                                    should be called by the same code after it closes the dialog.
34828 icon              String           A CSS class that provides a background image to be used as an icon for
34829                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34830 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34831 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34832 modal             Boolean          False to allow user interaction with the page while the message box is
34833                                    displayed (defaults to true)
34834 msg               String           A string that will replace the existing message box body text (defaults
34835                                    to the XHTML-compliant non-breaking space character '&#160;')
34836 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34837 progress          Boolean          True to display a progress bar (defaults to false)
34838 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34839 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34840 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34841 title             String           The title text
34842 value             String           The string value to set into the active textbox element if displayed
34843 wait              Boolean          True to display a progress bar (defaults to false)
34844 width             Number           The width of the dialog in pixels
34845 </pre>
34846          *
34847          * Example usage:
34848          * <pre><code>
34849 Roo.Msg.show({
34850    title: 'Address',
34851    msg: 'Please enter your address:',
34852    width: 300,
34853    buttons: Roo.MessageBox.OKCANCEL,
34854    multiline: true,
34855    fn: saveAddress,
34856    animEl: 'addAddressBtn'
34857 });
34858 </code></pre>
34859          * @param {Object} config Configuration options
34860          * @return {Roo.MessageBox} This message box
34861          */
34862         show : function(options)
34863         {
34864             
34865             // this causes nightmares if you show one dialog after another
34866             // especially on callbacks..
34867              
34868             if(this.isVisible()){
34869                 
34870                 this.hide();
34871                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34872                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34873                 Roo.log("New Dialog Message:" +  options.msg )
34874                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34875                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34876                 
34877             }
34878             var d = this.getDialog();
34879             opt = options;
34880             d.setTitle(opt.title || "&#160;");
34881             d.close.setDisplayed(opt.closable !== false);
34882             activeTextEl = textboxEl;
34883             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34884             if(opt.prompt){
34885                 if(opt.multiline){
34886                     textboxEl.hide();
34887                     textareaEl.show();
34888                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34889                         opt.multiline : this.defaultTextHeight);
34890                     activeTextEl = textareaEl;
34891                 }else{
34892                     textboxEl.show();
34893                     textareaEl.hide();
34894                 }
34895             }else{
34896                 textboxEl.hide();
34897                 textareaEl.hide();
34898             }
34899             progressEl.setDisplayed(opt.progress === true);
34900             this.updateProgress(0);
34901             activeTextEl.dom.value = opt.value || "";
34902             if(opt.prompt){
34903                 dlg.setDefaultButton(activeTextEl);
34904             }else{
34905                 var bs = opt.buttons;
34906                 var db = null;
34907                 if(bs && bs.ok){
34908                     db = buttons["ok"];
34909                 }else if(bs && bs.yes){
34910                     db = buttons["yes"];
34911                 }
34912                 dlg.setDefaultButton(db);
34913             }
34914             bwidth = updateButtons(opt.buttons);
34915             this.updateText(opt.msg);
34916             if(opt.cls){
34917                 d.el.addClass(opt.cls);
34918             }
34919             d.proxyDrag = opt.proxyDrag === true;
34920             d.modal = opt.modal !== false;
34921             d.mask = opt.modal !== false ? mask : false;
34922             if(!d.isVisible()){
34923                 // force it to the end of the z-index stack so it gets a cursor in FF
34924                 document.body.appendChild(dlg.el.dom);
34925                 d.animateTarget = null;
34926                 d.show(options.animEl);
34927             }
34928             return this;
34929         },
34930
34931         /**
34932          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34933          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34934          * and closing the message box when the process is complete.
34935          * @param {String} title The title bar text
34936          * @param {String} msg The message box body text
34937          * @return {Roo.MessageBox} This message box
34938          */
34939         progress : function(title, msg){
34940             this.show({
34941                 title : title,
34942                 msg : msg,
34943                 buttons: false,
34944                 progress:true,
34945                 closable:false,
34946                 minWidth: this.minProgressWidth,
34947                 modal : true
34948             });
34949             return this;
34950         },
34951
34952         /**
34953          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34954          * If a callback function is passed it will be called after the user clicks the button, and the
34955          * id of the button that was clicked will be passed as the only parameter to the callback
34956          * (could also be the top-right close button).
34957          * @param {String} title The title bar text
34958          * @param {String} msg The message box body text
34959          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34960          * @param {Object} scope (optional) The scope of the callback function
34961          * @return {Roo.MessageBox} This message box
34962          */
34963         alert : function(title, msg, fn, scope){
34964             this.show({
34965                 title : title,
34966                 msg : msg,
34967                 buttons: this.OK,
34968                 fn: fn,
34969                 scope : scope,
34970                 modal : true
34971             });
34972             return this;
34973         },
34974
34975         /**
34976          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34977          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34978          * You are responsible for closing the message box when the process is complete.
34979          * @param {String} msg The message box body text
34980          * @param {String} title (optional) The title bar text
34981          * @return {Roo.MessageBox} This message box
34982          */
34983         wait : function(msg, title){
34984             this.show({
34985                 title : title,
34986                 msg : msg,
34987                 buttons: false,
34988                 closable:false,
34989                 progress:true,
34990                 modal:true,
34991                 width:300,
34992                 wait:true
34993             });
34994             waitTimer = Roo.TaskMgr.start({
34995                 run: function(i){
34996                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34997                 },
34998                 interval: 1000
34999             });
35000             return this;
35001         },
35002
35003         /**
35004          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35005          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35006          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35007          * @param {String} title The title bar text
35008          * @param {String} msg The message box body text
35009          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35010          * @param {Object} scope (optional) The scope of the callback function
35011          * @return {Roo.MessageBox} This message box
35012          */
35013         confirm : function(title, msg, fn, scope){
35014             this.show({
35015                 title : title,
35016                 msg : msg,
35017                 buttons: this.YESNO,
35018                 fn: fn,
35019                 scope : scope,
35020                 modal : true
35021             });
35022             return this;
35023         },
35024
35025         /**
35026          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35027          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35028          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35029          * (could also be the top-right close button) and the text that was entered will be passed as the two
35030          * parameters to the callback.
35031          * @param {String} title The title bar text
35032          * @param {String} msg The message box body text
35033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35034          * @param {Object} scope (optional) The scope of the callback function
35035          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35036          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35037          * @return {Roo.MessageBox} This message box
35038          */
35039         prompt : function(title, msg, fn, scope, multiline){
35040             this.show({
35041                 title : title,
35042                 msg : msg,
35043                 buttons: this.OKCANCEL,
35044                 fn: fn,
35045                 minWidth:250,
35046                 scope : scope,
35047                 prompt:true,
35048                 multiline: multiline,
35049                 modal : true
35050             });
35051             return this;
35052         },
35053
35054         /**
35055          * Button config that displays a single OK button
35056          * @type Object
35057          */
35058         OK : {ok:true},
35059         /**
35060          * Button config that displays Yes and No buttons
35061          * @type Object
35062          */
35063         YESNO : {yes:true, no:true},
35064         /**
35065          * Button config that displays OK and Cancel buttons
35066          * @type Object
35067          */
35068         OKCANCEL : {ok:true, cancel:true},
35069         /**
35070          * Button config that displays Yes, No and Cancel buttons
35071          * @type Object
35072          */
35073         YESNOCANCEL : {yes:true, no:true, cancel:true},
35074
35075         /**
35076          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35077          * @type Number
35078          */
35079         defaultTextHeight : 75,
35080         /**
35081          * The maximum width in pixels of the message box (defaults to 600)
35082          * @type Number
35083          */
35084         maxWidth : 600,
35085         /**
35086          * The minimum width in pixels of the message box (defaults to 100)
35087          * @type Number
35088          */
35089         minWidth : 100,
35090         /**
35091          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35092          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35093          * @type Number
35094          */
35095         minProgressWidth : 250,
35096         /**
35097          * An object containing the default button text strings that can be overriden for localized language support.
35098          * Supported properties are: ok, cancel, yes and no.
35099          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35100          * @type Object
35101          */
35102         buttonText : {
35103             ok : "OK",
35104             cancel : "Cancel",
35105             yes : "Yes",
35106             no : "No"
35107         }
35108     };
35109 }();
35110
35111 /**
35112  * Shorthand for {@link Roo.MessageBox}
35113  */
35114 Roo.Msg = Roo.MessageBox;/*
35115  * Based on:
35116  * Ext JS Library 1.1.1
35117  * Copyright(c) 2006-2007, Ext JS, LLC.
35118  *
35119  * Originally Released Under LGPL - original licence link has changed is not relivant.
35120  *
35121  * Fork - LGPL
35122  * <script type="text/javascript">
35123  */
35124 /**
35125  * @class Roo.QuickTips
35126  * Provides attractive and customizable tooltips for any element.
35127  * @static
35128  */
35129 Roo.QuickTips = function(){
35130     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35131     var ce, bd, xy, dd;
35132     var visible = false, disabled = true, inited = false;
35133     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35134     
35135     var onOver = function(e){
35136         if(disabled){
35137             return;
35138         }
35139         var t = e.getTarget();
35140         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35141             return;
35142         }
35143         if(ce && t == ce.el){
35144             clearTimeout(hideProc);
35145             return;
35146         }
35147         if(t && tagEls[t.id]){
35148             tagEls[t.id].el = t;
35149             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35150             return;
35151         }
35152         var ttp, et = Roo.fly(t);
35153         var ns = cfg.namespace;
35154         if(tm.interceptTitles && t.title){
35155             ttp = t.title;
35156             t.qtip = ttp;
35157             t.removeAttribute("title");
35158             e.preventDefault();
35159         }else{
35160             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35161         }
35162         if(ttp){
35163             showProc = show.defer(tm.showDelay, tm, [{
35164                 el: t, 
35165                 text: ttp.replace(/\\n/g,'<br/>'),
35166                 width: et.getAttributeNS(ns, cfg.width),
35167                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35168                 title: et.getAttributeNS(ns, cfg.title),
35169                     cls: et.getAttributeNS(ns, cfg.cls)
35170             }]);
35171         }
35172     };
35173     
35174     var onOut = function(e){
35175         clearTimeout(showProc);
35176         var t = e.getTarget();
35177         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35178             hideProc = setTimeout(hide, tm.hideDelay);
35179         }
35180     };
35181     
35182     var onMove = function(e){
35183         if(disabled){
35184             return;
35185         }
35186         xy = e.getXY();
35187         xy[1] += 18;
35188         if(tm.trackMouse && ce){
35189             el.setXY(xy);
35190         }
35191     };
35192     
35193     var onDown = function(e){
35194         clearTimeout(showProc);
35195         clearTimeout(hideProc);
35196         if(!e.within(el)){
35197             if(tm.hideOnClick){
35198                 hide();
35199                 tm.disable();
35200                 tm.enable.defer(100, tm);
35201             }
35202         }
35203     };
35204     
35205     var getPad = function(){
35206         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35207     };
35208
35209     var show = function(o){
35210         if(disabled){
35211             return;
35212         }
35213         clearTimeout(dismissProc);
35214         ce = o;
35215         if(removeCls){ // in case manually hidden
35216             el.removeClass(removeCls);
35217             removeCls = null;
35218         }
35219         if(ce.cls){
35220             el.addClass(ce.cls);
35221             removeCls = ce.cls;
35222         }
35223         if(ce.title){
35224             tipTitle.update(ce.title);
35225             tipTitle.show();
35226         }else{
35227             tipTitle.update('');
35228             tipTitle.hide();
35229         }
35230         el.dom.style.width  = tm.maxWidth+'px';
35231         //tipBody.dom.style.width = '';
35232         tipBodyText.update(o.text);
35233         var p = getPad(), w = ce.width;
35234         if(!w){
35235             var td = tipBodyText.dom;
35236             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35237             if(aw > tm.maxWidth){
35238                 w = tm.maxWidth;
35239             }else if(aw < tm.minWidth){
35240                 w = tm.minWidth;
35241             }else{
35242                 w = aw;
35243             }
35244         }
35245         //tipBody.setWidth(w);
35246         el.setWidth(parseInt(w, 10) + p);
35247         if(ce.autoHide === false){
35248             close.setDisplayed(true);
35249             if(dd){
35250                 dd.unlock();
35251             }
35252         }else{
35253             close.setDisplayed(false);
35254             if(dd){
35255                 dd.lock();
35256             }
35257         }
35258         if(xy){
35259             el.avoidY = xy[1]-18;
35260             el.setXY(xy);
35261         }
35262         if(tm.animate){
35263             el.setOpacity(.1);
35264             el.setStyle("visibility", "visible");
35265             el.fadeIn({callback: afterShow});
35266         }else{
35267             afterShow();
35268         }
35269     };
35270     
35271     var afterShow = function(){
35272         if(ce){
35273             el.show();
35274             esc.enable();
35275             if(tm.autoDismiss && ce.autoHide !== false){
35276                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35277             }
35278         }
35279     };
35280     
35281     var hide = function(noanim){
35282         clearTimeout(dismissProc);
35283         clearTimeout(hideProc);
35284         ce = null;
35285         if(el.isVisible()){
35286             esc.disable();
35287             if(noanim !== true && tm.animate){
35288                 el.fadeOut({callback: afterHide});
35289             }else{
35290                 afterHide();
35291             } 
35292         }
35293     };
35294     
35295     var afterHide = function(){
35296         el.hide();
35297         if(removeCls){
35298             el.removeClass(removeCls);
35299             removeCls = null;
35300         }
35301     };
35302     
35303     return {
35304         /**
35305         * @cfg {Number} minWidth
35306         * The minimum width of the quick tip (defaults to 40)
35307         */
35308        minWidth : 40,
35309         /**
35310         * @cfg {Number} maxWidth
35311         * The maximum width of the quick tip (defaults to 300)
35312         */
35313        maxWidth : 300,
35314         /**
35315         * @cfg {Boolean} interceptTitles
35316         * True to automatically use the element's DOM title value if available (defaults to false)
35317         */
35318        interceptTitles : false,
35319         /**
35320         * @cfg {Boolean} trackMouse
35321         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35322         */
35323        trackMouse : false,
35324         /**
35325         * @cfg {Boolean} hideOnClick
35326         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35327         */
35328        hideOnClick : true,
35329         /**
35330         * @cfg {Number} showDelay
35331         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35332         */
35333        showDelay : 500,
35334         /**
35335         * @cfg {Number} hideDelay
35336         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35337         */
35338        hideDelay : 200,
35339         /**
35340         * @cfg {Boolean} autoHide
35341         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35342         * Used in conjunction with hideDelay.
35343         */
35344        autoHide : true,
35345         /**
35346         * @cfg {Boolean}
35347         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35348         * (defaults to true).  Used in conjunction with autoDismissDelay.
35349         */
35350        autoDismiss : true,
35351         /**
35352         * @cfg {Number}
35353         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35354         */
35355        autoDismissDelay : 5000,
35356        /**
35357         * @cfg {Boolean} animate
35358         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35359         */
35360        animate : false,
35361
35362        /**
35363         * @cfg {String} title
35364         * Title text to display (defaults to '').  This can be any valid HTML markup.
35365         */
35366         title: '',
35367        /**
35368         * @cfg {String} text
35369         * Body text to display (defaults to '').  This can be any valid HTML markup.
35370         */
35371         text : '',
35372        /**
35373         * @cfg {String} cls
35374         * A CSS class to apply to the base quick tip element (defaults to '').
35375         */
35376         cls : '',
35377        /**
35378         * @cfg {Number} width
35379         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35380         * minWidth or maxWidth.
35381         */
35382         width : null,
35383
35384     /**
35385      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35386      * or display QuickTips in a page.
35387      */
35388        init : function(){
35389           tm = Roo.QuickTips;
35390           cfg = tm.tagConfig;
35391           if(!inited){
35392               if(!Roo.isReady){ // allow calling of init() before onReady
35393                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35394                   return;
35395               }
35396               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35397               el.fxDefaults = {stopFx: true};
35398               // maximum custom styling
35399               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
35400               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
35401               tipTitle = el.child('h3');
35402               tipTitle.enableDisplayMode("block");
35403               tipBody = el.child('div.x-tip-bd');
35404               tipBodyText = el.child('div.x-tip-bd-inner');
35405               //bdLeft = el.child('div.x-tip-bd-left');
35406               //bdRight = el.child('div.x-tip-bd-right');
35407               close = el.child('div.x-tip-close');
35408               close.enableDisplayMode("block");
35409               close.on("click", hide);
35410               var d = Roo.get(document);
35411               d.on("mousedown", onDown);
35412               d.on("mouseover", onOver);
35413               d.on("mouseout", onOut);
35414               d.on("mousemove", onMove);
35415               esc = d.addKeyListener(27, hide);
35416               esc.disable();
35417               if(Roo.dd.DD){
35418                   dd = el.initDD("default", null, {
35419                       onDrag : function(){
35420                           el.sync();  
35421                       }
35422                   });
35423                   dd.setHandleElId(tipTitle.id);
35424                   dd.lock();
35425               }
35426               inited = true;
35427           }
35428           this.enable(); 
35429        },
35430
35431     /**
35432      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35433      * are supported:
35434      * <pre>
35435 Property    Type                   Description
35436 ----------  ---------------------  ------------------------------------------------------------------------
35437 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35438      * </ul>
35439      * @param {Object} config The config object
35440      */
35441        register : function(config){
35442            var cs = config instanceof Array ? config : arguments;
35443            for(var i = 0, len = cs.length; i < len; i++) {
35444                var c = cs[i];
35445                var target = c.target;
35446                if(target){
35447                    if(target instanceof Array){
35448                        for(var j = 0, jlen = target.length; j < jlen; j++){
35449                            tagEls[target[j]] = c;
35450                        }
35451                    }else{
35452                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35453                    }
35454                }
35455            }
35456        },
35457
35458     /**
35459      * Removes this quick tip from its element and destroys it.
35460      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35461      */
35462        unregister : function(el){
35463            delete tagEls[Roo.id(el)];
35464        },
35465
35466     /**
35467      * Enable this quick tip.
35468      */
35469        enable : function(){
35470            if(inited && disabled){
35471                locks.pop();
35472                if(locks.length < 1){
35473                    disabled = false;
35474                }
35475            }
35476        },
35477
35478     /**
35479      * Disable this quick tip.
35480      */
35481        disable : function(){
35482           disabled = true;
35483           clearTimeout(showProc);
35484           clearTimeout(hideProc);
35485           clearTimeout(dismissProc);
35486           if(ce){
35487               hide(true);
35488           }
35489           locks.push(1);
35490        },
35491
35492     /**
35493      * Returns true if the quick tip is enabled, else false.
35494      */
35495        isEnabled : function(){
35496             return !disabled;
35497        },
35498
35499         // private
35500        tagConfig : {
35501            namespace : "roo", // was ext?? this may break..
35502            alt_namespace : "ext",
35503            attribute : "qtip",
35504            width : "width",
35505            target : "target",
35506            title : "qtitle",
35507            hide : "hide",
35508            cls : "qclass"
35509        }
35510    };
35511 }();
35512
35513 // backwards compat
35514 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35515  * Based on:
35516  * Ext JS Library 1.1.1
35517  * Copyright(c) 2006-2007, Ext JS, LLC.
35518  *
35519  * Originally Released Under LGPL - original licence link has changed is not relivant.
35520  *
35521  * Fork - LGPL
35522  * <script type="text/javascript">
35523  */
35524  
35525
35526 /**
35527  * @class Roo.tree.TreePanel
35528  * @extends Roo.data.Tree
35529  * @cfg {Roo.tree.TreeNode} root The root node
35530  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35531  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35532  * @cfg {Boolean} enableDD true to enable drag and drop
35533  * @cfg {Boolean} enableDrag true to enable just drag
35534  * @cfg {Boolean} enableDrop true to enable just drop
35535  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35536  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35537  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35538  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35539  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35540  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35541  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35542  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35543  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35544  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35545  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35546  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35547  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35548  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35549  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35550  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35551  * 
35552  * @constructor
35553  * @param {String/HTMLElement/Element} el The container element
35554  * @param {Object} config
35555  */
35556 Roo.tree.TreePanel = function(el, config){
35557     var root = false;
35558     var loader = false;
35559     if (config.root) {
35560         root = config.root;
35561         delete config.root;
35562     }
35563     if (config.loader) {
35564         loader = config.loader;
35565         delete config.loader;
35566     }
35567     
35568     Roo.apply(this, config);
35569     Roo.tree.TreePanel.superclass.constructor.call(this);
35570     this.el = Roo.get(el);
35571     this.el.addClass('x-tree');
35572     //console.log(root);
35573     if (root) {
35574         this.setRootNode( Roo.factory(root, Roo.tree));
35575     }
35576     if (loader) {
35577         this.loader = Roo.factory(loader, Roo.tree);
35578     }
35579    /**
35580     * Read-only. The id of the container element becomes this TreePanel's id.
35581     */
35582     this.id = this.el.id;
35583     this.addEvents({
35584         /**
35585         * @event beforeload
35586         * Fires before a node is loaded, return false to cancel
35587         * @param {Node} node The node being loaded
35588         */
35589         "beforeload" : true,
35590         /**
35591         * @event load
35592         * Fires when a node is loaded
35593         * @param {Node} node The node that was loaded
35594         */
35595         "load" : true,
35596         /**
35597         * @event textchange
35598         * Fires when the text for a node is changed
35599         * @param {Node} node The node
35600         * @param {String} text The new text
35601         * @param {String} oldText The old text
35602         */
35603         "textchange" : true,
35604         /**
35605         * @event beforeexpand
35606         * Fires before a node is expanded, return false to cancel.
35607         * @param {Node} node The node
35608         * @param {Boolean} deep
35609         * @param {Boolean} anim
35610         */
35611         "beforeexpand" : true,
35612         /**
35613         * @event beforecollapse
35614         * Fires before a node is collapsed, return false to cancel.
35615         * @param {Node} node The node
35616         * @param {Boolean} deep
35617         * @param {Boolean} anim
35618         */
35619         "beforecollapse" : true,
35620         /**
35621         * @event expand
35622         * Fires when a node is expanded
35623         * @param {Node} node The node
35624         */
35625         "expand" : true,
35626         /**
35627         * @event disabledchange
35628         * Fires when the disabled status of a node changes
35629         * @param {Node} node The node
35630         * @param {Boolean} disabled
35631         */
35632         "disabledchange" : true,
35633         /**
35634         * @event collapse
35635         * Fires when a node is collapsed
35636         * @param {Node} node The node
35637         */
35638         "collapse" : true,
35639         /**
35640         * @event beforeclick
35641         * Fires before click processing on a node. Return false to cancel the default action.
35642         * @param {Node} node The node
35643         * @param {Roo.EventObject} e The event object
35644         */
35645         "beforeclick":true,
35646         /**
35647         * @event checkchange
35648         * Fires when a node with a checkbox's checked property changes
35649         * @param {Node} this This node
35650         * @param {Boolean} checked
35651         */
35652         "checkchange":true,
35653         /**
35654         * @event click
35655         * Fires when a node is clicked
35656         * @param {Node} node The node
35657         * @param {Roo.EventObject} e The event object
35658         */
35659         "click":true,
35660         /**
35661         * @event dblclick
35662         * Fires when a node is double clicked
35663         * @param {Node} node The node
35664         * @param {Roo.EventObject} e The event object
35665         */
35666         "dblclick":true,
35667         /**
35668         * @event contextmenu
35669         * Fires when a node is right clicked
35670         * @param {Node} node The node
35671         * @param {Roo.EventObject} e The event object
35672         */
35673         "contextmenu":true,
35674         /**
35675         * @event beforechildrenrendered
35676         * Fires right before the child nodes for a node are rendered
35677         * @param {Node} node The node
35678         */
35679         "beforechildrenrendered":true,
35680         /**
35681         * @event startdrag
35682         * Fires when a node starts being dragged
35683         * @param {Roo.tree.TreePanel} this
35684         * @param {Roo.tree.TreeNode} node
35685         * @param {event} e The raw browser event
35686         */ 
35687        "startdrag" : true,
35688        /**
35689         * @event enddrag
35690         * Fires when a drag operation is complete
35691         * @param {Roo.tree.TreePanel} this
35692         * @param {Roo.tree.TreeNode} node
35693         * @param {event} e The raw browser event
35694         */
35695        "enddrag" : true,
35696        /**
35697         * @event dragdrop
35698         * Fires when a dragged node is dropped on a valid DD target
35699         * @param {Roo.tree.TreePanel} this
35700         * @param {Roo.tree.TreeNode} node
35701         * @param {DD} dd The dd it was dropped on
35702         * @param {event} e The raw browser event
35703         */
35704        "dragdrop" : true,
35705        /**
35706         * @event beforenodedrop
35707         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35708         * passed to handlers has the following properties:<br />
35709         * <ul style="padding:5px;padding-left:16px;">
35710         * <li>tree - The TreePanel</li>
35711         * <li>target - The node being targeted for the drop</li>
35712         * <li>data - The drag data from the drag source</li>
35713         * <li>point - The point of the drop - append, above or below</li>
35714         * <li>source - The drag source</li>
35715         * <li>rawEvent - Raw mouse event</li>
35716         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35717         * to be inserted by setting them on this object.</li>
35718         * <li>cancel - Set this to true to cancel the drop.</li>
35719         * </ul>
35720         * @param {Object} dropEvent
35721         */
35722        "beforenodedrop" : true,
35723        /**
35724         * @event nodedrop
35725         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35726         * passed to handlers has the following properties:<br />
35727         * <ul style="padding:5px;padding-left:16px;">
35728         * <li>tree - The TreePanel</li>
35729         * <li>target - The node being targeted for the drop</li>
35730         * <li>data - The drag data from the drag source</li>
35731         * <li>point - The point of the drop - append, above or below</li>
35732         * <li>source - The drag source</li>
35733         * <li>rawEvent - Raw mouse event</li>
35734         * <li>dropNode - Dropped node(s).</li>
35735         * </ul>
35736         * @param {Object} dropEvent
35737         */
35738        "nodedrop" : true,
35739         /**
35740         * @event nodedragover
35741         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35742         * passed to handlers has the following properties:<br />
35743         * <ul style="padding:5px;padding-left:16px;">
35744         * <li>tree - The TreePanel</li>
35745         * <li>target - The node being targeted for the drop</li>
35746         * <li>data - The drag data from the drag source</li>
35747         * <li>point - The point of the drop - append, above or below</li>
35748         * <li>source - The drag source</li>
35749         * <li>rawEvent - Raw mouse event</li>
35750         * <li>dropNode - Drop node(s) provided by the source.</li>
35751         * <li>cancel - Set this to true to signal drop not allowed.</li>
35752         * </ul>
35753         * @param {Object} dragOverEvent
35754         */
35755        "nodedragover" : true,
35756        /**
35757         * @event appendnode
35758         * Fires when append node to the tree
35759         * @param {Roo.tree.TreePanel} this
35760         * @param {Roo.tree.TreeNode} node
35761         * @param {Number} index The index of the newly appended node
35762         */
35763        "appendnode" : true
35764         
35765     });
35766     if(this.singleExpand){
35767        this.on("beforeexpand", this.restrictExpand, this);
35768     }
35769     if (this.editor) {
35770         this.editor.tree = this;
35771         this.editor = Roo.factory(this.editor, Roo.tree);
35772     }
35773     
35774     if (this.selModel) {
35775         this.selModel = Roo.factory(this.selModel, Roo.tree);
35776     }
35777    
35778 };
35779 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35780     rootVisible : true,
35781     animate: Roo.enableFx,
35782     lines : true,
35783     enableDD : false,
35784     hlDrop : Roo.enableFx,
35785   
35786     renderer: false,
35787     
35788     rendererTip: false,
35789     // private
35790     restrictExpand : function(node){
35791         var p = node.parentNode;
35792         if(p){
35793             if(p.expandedChild && p.expandedChild.parentNode == p){
35794                 p.expandedChild.collapse();
35795             }
35796             p.expandedChild = node;
35797         }
35798     },
35799
35800     // private override
35801     setRootNode : function(node){
35802         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35803         if(!this.rootVisible){
35804             node.ui = new Roo.tree.RootTreeNodeUI(node);
35805         }
35806         return node;
35807     },
35808
35809     /**
35810      * Returns the container element for this TreePanel
35811      */
35812     getEl : function(){
35813         return this.el;
35814     },
35815
35816     /**
35817      * Returns the default TreeLoader for this TreePanel
35818      */
35819     getLoader : function(){
35820         return this.loader;
35821     },
35822
35823     /**
35824      * Expand all nodes
35825      */
35826     expandAll : function(){
35827         this.root.expand(true);
35828     },
35829
35830     /**
35831      * Collapse all nodes
35832      */
35833     collapseAll : function(){
35834         this.root.collapse(true);
35835     },
35836
35837     /**
35838      * Returns the selection model used by this TreePanel
35839      */
35840     getSelectionModel : function(){
35841         if(!this.selModel){
35842             this.selModel = new Roo.tree.DefaultSelectionModel();
35843         }
35844         return this.selModel;
35845     },
35846
35847     /**
35848      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35849      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35850      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35851      * @return {Array}
35852      */
35853     getChecked : function(a, startNode){
35854         startNode = startNode || this.root;
35855         var r = [];
35856         var f = function(){
35857             if(this.attributes.checked){
35858                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35859             }
35860         }
35861         startNode.cascade(f);
35862         return r;
35863     },
35864
35865     /**
35866      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35867      * @param {String} path
35868      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35869      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35870      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35871      */
35872     expandPath : function(path, attr, callback){
35873         attr = attr || "id";
35874         var keys = path.split(this.pathSeparator);
35875         var curNode = this.root;
35876         if(curNode.attributes[attr] != keys[1]){ // invalid root
35877             if(callback){
35878                 callback(false, null);
35879             }
35880             return;
35881         }
35882         var index = 1;
35883         var f = function(){
35884             if(++index == keys.length){
35885                 if(callback){
35886                     callback(true, curNode);
35887                 }
35888                 return;
35889             }
35890             var c = curNode.findChild(attr, keys[index]);
35891             if(!c){
35892                 if(callback){
35893                     callback(false, curNode);
35894                 }
35895                 return;
35896             }
35897             curNode = c;
35898             c.expand(false, false, f);
35899         };
35900         curNode.expand(false, false, f);
35901     },
35902
35903     /**
35904      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35905      * @param {String} path
35906      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35907      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35908      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35909      */
35910     selectPath : function(path, attr, callback){
35911         attr = attr || "id";
35912         var keys = path.split(this.pathSeparator);
35913         var v = keys.pop();
35914         if(keys.length > 0){
35915             var f = function(success, node){
35916                 if(success && node){
35917                     var n = node.findChild(attr, v);
35918                     if(n){
35919                         n.select();
35920                         if(callback){
35921                             callback(true, n);
35922                         }
35923                     }else if(callback){
35924                         callback(false, n);
35925                     }
35926                 }else{
35927                     if(callback){
35928                         callback(false, n);
35929                     }
35930                 }
35931             };
35932             this.expandPath(keys.join(this.pathSeparator), attr, f);
35933         }else{
35934             this.root.select();
35935             if(callback){
35936                 callback(true, this.root);
35937             }
35938         }
35939     },
35940
35941     getTreeEl : function(){
35942         return this.el;
35943     },
35944
35945     /**
35946      * Trigger rendering of this TreePanel
35947      */
35948     render : function(){
35949         if (this.innerCt) {
35950             return this; // stop it rendering more than once!!
35951         }
35952         
35953         this.innerCt = this.el.createChild({tag:"ul",
35954                cls:"x-tree-root-ct " +
35955                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35956
35957         if(this.containerScroll){
35958             Roo.dd.ScrollManager.register(this.el);
35959         }
35960         if((this.enableDD || this.enableDrop) && !this.dropZone){
35961            /**
35962             * The dropZone used by this tree if drop is enabled
35963             * @type Roo.tree.TreeDropZone
35964             */
35965              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35966                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35967            });
35968         }
35969         if((this.enableDD || this.enableDrag) && !this.dragZone){
35970            /**
35971             * The dragZone used by this tree if drag is enabled
35972             * @type Roo.tree.TreeDragZone
35973             */
35974             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35975                ddGroup: this.ddGroup || "TreeDD",
35976                scroll: this.ddScroll
35977            });
35978         }
35979         this.getSelectionModel().init(this);
35980         if (!this.root) {
35981             Roo.log("ROOT not set in tree");
35982             return this;
35983         }
35984         this.root.render();
35985         if(!this.rootVisible){
35986             this.root.renderChildren();
35987         }
35988         return this;
35989     }
35990 });/*
35991  * Based on:
35992  * Ext JS Library 1.1.1
35993  * Copyright(c) 2006-2007, Ext JS, LLC.
35994  *
35995  * Originally Released Under LGPL - original licence link has changed is not relivant.
35996  *
35997  * Fork - LGPL
35998  * <script type="text/javascript">
35999  */
36000  
36001
36002 /**
36003  * @class Roo.tree.DefaultSelectionModel
36004  * @extends Roo.util.Observable
36005  * The default single selection for a TreePanel.
36006  * @param {Object} cfg Configuration
36007  */
36008 Roo.tree.DefaultSelectionModel = function(cfg){
36009    this.selNode = null;
36010    
36011    
36012    
36013    this.addEvents({
36014        /**
36015         * @event selectionchange
36016         * Fires when the selected node changes
36017         * @param {DefaultSelectionModel} this
36018         * @param {TreeNode} node the new selection
36019         */
36020        "selectionchange" : true,
36021
36022        /**
36023         * @event beforeselect
36024         * Fires before the selected node changes, return false to cancel the change
36025         * @param {DefaultSelectionModel} this
36026         * @param {TreeNode} node the new selection
36027         * @param {TreeNode} node the old selection
36028         */
36029        "beforeselect" : true
36030    });
36031    
36032     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36033 };
36034
36035 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36036     init : function(tree){
36037         this.tree = tree;
36038         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36039         tree.on("click", this.onNodeClick, this);
36040     },
36041     
36042     onNodeClick : function(node, e){
36043         if (e.ctrlKey && this.selNode == node)  {
36044             this.unselect(node);
36045             return;
36046         }
36047         this.select(node);
36048     },
36049     
36050     /**
36051      * Select a node.
36052      * @param {TreeNode} node The node to select
36053      * @return {TreeNode} The selected node
36054      */
36055     select : function(node){
36056         var last = this.selNode;
36057         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36058             if(last){
36059                 last.ui.onSelectedChange(false);
36060             }
36061             this.selNode = node;
36062             node.ui.onSelectedChange(true);
36063             this.fireEvent("selectionchange", this, node, last);
36064         }
36065         return node;
36066     },
36067     
36068     /**
36069      * Deselect a node.
36070      * @param {TreeNode} node The node to unselect
36071      */
36072     unselect : function(node){
36073         if(this.selNode == node){
36074             this.clearSelections();
36075         }    
36076     },
36077     
36078     /**
36079      * Clear all selections
36080      */
36081     clearSelections : function(){
36082         var n = this.selNode;
36083         if(n){
36084             n.ui.onSelectedChange(false);
36085             this.selNode = null;
36086             this.fireEvent("selectionchange", this, null);
36087         }
36088         return n;
36089     },
36090     
36091     /**
36092      * Get the selected node
36093      * @return {TreeNode} The selected node
36094      */
36095     getSelectedNode : function(){
36096         return this.selNode;    
36097     },
36098     
36099     /**
36100      * Returns true if the node is selected
36101      * @param {TreeNode} node The node to check
36102      * @return {Boolean}
36103      */
36104     isSelected : function(node){
36105         return this.selNode == node;  
36106     },
36107
36108     /**
36109      * Selects the node above the selected node in the tree, intelligently walking the nodes
36110      * @return TreeNode The new selection
36111      */
36112     selectPrevious : function(){
36113         var s = this.selNode || this.lastSelNode;
36114         if(!s){
36115             return null;
36116         }
36117         var ps = s.previousSibling;
36118         if(ps){
36119             if(!ps.isExpanded() || ps.childNodes.length < 1){
36120                 return this.select(ps);
36121             } else{
36122                 var lc = ps.lastChild;
36123                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36124                     lc = lc.lastChild;
36125                 }
36126                 return this.select(lc);
36127             }
36128         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36129             return this.select(s.parentNode);
36130         }
36131         return null;
36132     },
36133
36134     /**
36135      * Selects the node above the selected node in the tree, intelligently walking the nodes
36136      * @return TreeNode The new selection
36137      */
36138     selectNext : function(){
36139         var s = this.selNode || this.lastSelNode;
36140         if(!s){
36141             return null;
36142         }
36143         if(s.firstChild && s.isExpanded()){
36144              return this.select(s.firstChild);
36145          }else if(s.nextSibling){
36146              return this.select(s.nextSibling);
36147          }else if(s.parentNode){
36148             var newS = null;
36149             s.parentNode.bubble(function(){
36150                 if(this.nextSibling){
36151                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36152                     return false;
36153                 }
36154             });
36155             return newS;
36156          }
36157         return null;
36158     },
36159
36160     onKeyDown : function(e){
36161         var s = this.selNode || this.lastSelNode;
36162         // undesirable, but required
36163         var sm = this;
36164         if(!s){
36165             return;
36166         }
36167         var k = e.getKey();
36168         switch(k){
36169              case e.DOWN:
36170                  e.stopEvent();
36171                  this.selectNext();
36172              break;
36173              case e.UP:
36174                  e.stopEvent();
36175                  this.selectPrevious();
36176              break;
36177              case e.RIGHT:
36178                  e.preventDefault();
36179                  if(s.hasChildNodes()){
36180                      if(!s.isExpanded()){
36181                          s.expand();
36182                      }else if(s.firstChild){
36183                          this.select(s.firstChild, e);
36184                      }
36185                  }
36186              break;
36187              case e.LEFT:
36188                  e.preventDefault();
36189                  if(s.hasChildNodes() && s.isExpanded()){
36190                      s.collapse();
36191                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36192                      this.select(s.parentNode, e);
36193                  }
36194              break;
36195         };
36196     }
36197 });
36198
36199 /**
36200  * @class Roo.tree.MultiSelectionModel
36201  * @extends Roo.util.Observable
36202  * Multi selection for a TreePanel.
36203  * @param {Object} cfg Configuration
36204  */
36205 Roo.tree.MultiSelectionModel = function(){
36206    this.selNodes = [];
36207    this.selMap = {};
36208    this.addEvents({
36209        /**
36210         * @event selectionchange
36211         * Fires when the selected nodes change
36212         * @param {MultiSelectionModel} this
36213         * @param {Array} nodes Array of the selected nodes
36214         */
36215        "selectionchange" : true
36216    });
36217    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36218    
36219 };
36220
36221 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36222     init : function(tree){
36223         this.tree = tree;
36224         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36225         tree.on("click", this.onNodeClick, this);
36226     },
36227     
36228     onNodeClick : function(node, e){
36229         this.select(node, e, e.ctrlKey);
36230     },
36231     
36232     /**
36233      * Select a node.
36234      * @param {TreeNode} node The node to select
36235      * @param {EventObject} e (optional) An event associated with the selection
36236      * @param {Boolean} keepExisting True to retain existing selections
36237      * @return {TreeNode} The selected node
36238      */
36239     select : function(node, e, keepExisting){
36240         if(keepExisting !== true){
36241             this.clearSelections(true);
36242         }
36243         if(this.isSelected(node)){
36244             this.lastSelNode = node;
36245             return node;
36246         }
36247         this.selNodes.push(node);
36248         this.selMap[node.id] = node;
36249         this.lastSelNode = node;
36250         node.ui.onSelectedChange(true);
36251         this.fireEvent("selectionchange", this, this.selNodes);
36252         return node;
36253     },
36254     
36255     /**
36256      * Deselect a node.
36257      * @param {TreeNode} node The node to unselect
36258      */
36259     unselect : function(node){
36260         if(this.selMap[node.id]){
36261             node.ui.onSelectedChange(false);
36262             var sn = this.selNodes;
36263             var index = -1;
36264             if(sn.indexOf){
36265                 index = sn.indexOf(node);
36266             }else{
36267                 for(var i = 0, len = sn.length; i < len; i++){
36268                     if(sn[i] == node){
36269                         index = i;
36270                         break;
36271                     }
36272                 }
36273             }
36274             if(index != -1){
36275                 this.selNodes.splice(index, 1);
36276             }
36277             delete this.selMap[node.id];
36278             this.fireEvent("selectionchange", this, this.selNodes);
36279         }
36280     },
36281     
36282     /**
36283      * Clear all selections
36284      */
36285     clearSelections : function(suppressEvent){
36286         var sn = this.selNodes;
36287         if(sn.length > 0){
36288             for(var i = 0, len = sn.length; i < len; i++){
36289                 sn[i].ui.onSelectedChange(false);
36290             }
36291             this.selNodes = [];
36292             this.selMap = {};
36293             if(suppressEvent !== true){
36294                 this.fireEvent("selectionchange", this, this.selNodes);
36295             }
36296         }
36297     },
36298     
36299     /**
36300      * Returns true if the node is selected
36301      * @param {TreeNode} node The node to check
36302      * @return {Boolean}
36303      */
36304     isSelected : function(node){
36305         return this.selMap[node.id] ? true : false;  
36306     },
36307     
36308     /**
36309      * Returns an array of the selected nodes
36310      * @return {Array}
36311      */
36312     getSelectedNodes : function(){
36313         return this.selNodes;    
36314     },
36315
36316     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36317
36318     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36319
36320     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36321 });/*
36322  * Based on:
36323  * Ext JS Library 1.1.1
36324  * Copyright(c) 2006-2007, Ext JS, LLC.
36325  *
36326  * Originally Released Under LGPL - original licence link has changed is not relivant.
36327  *
36328  * Fork - LGPL
36329  * <script type="text/javascript">
36330  */
36331  
36332 /**
36333  * @class Roo.tree.TreeNode
36334  * @extends Roo.data.Node
36335  * @cfg {String} text The text for this node
36336  * @cfg {Boolean} expanded true to start the node expanded
36337  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36338  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36339  * @cfg {Boolean} disabled true to start the node disabled
36340  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36341  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36342  * @cfg {String} cls A css class to be added to the node
36343  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36344  * @cfg {String} href URL of the link used for the node (defaults to #)
36345  * @cfg {String} hrefTarget target frame for the link
36346  * @cfg {String} qtip An Ext QuickTip for the node
36347  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36348  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36349  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36350  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36351  * (defaults to undefined with no checkbox rendered)
36352  * @constructor
36353  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36354  */
36355 Roo.tree.TreeNode = function(attributes){
36356     attributes = attributes || {};
36357     if(typeof attributes == "string"){
36358         attributes = {text: attributes};
36359     }
36360     this.childrenRendered = false;
36361     this.rendered = false;
36362     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36363     this.expanded = attributes.expanded === true;
36364     this.isTarget = attributes.isTarget !== false;
36365     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36366     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36367
36368     /**
36369      * Read-only. The text for this node. To change it use setText().
36370      * @type String
36371      */
36372     this.text = attributes.text;
36373     /**
36374      * True if this node is disabled.
36375      * @type Boolean
36376      */
36377     this.disabled = attributes.disabled === true;
36378
36379     this.addEvents({
36380         /**
36381         * @event textchange
36382         * Fires when the text for this node is changed
36383         * @param {Node} this This node
36384         * @param {String} text The new text
36385         * @param {String} oldText The old text
36386         */
36387         "textchange" : true,
36388         /**
36389         * @event beforeexpand
36390         * Fires before this node is expanded, return false to cancel.
36391         * @param {Node} this This node
36392         * @param {Boolean} deep
36393         * @param {Boolean} anim
36394         */
36395         "beforeexpand" : true,
36396         /**
36397         * @event beforecollapse
36398         * Fires before this node is collapsed, return false to cancel.
36399         * @param {Node} this This node
36400         * @param {Boolean} deep
36401         * @param {Boolean} anim
36402         */
36403         "beforecollapse" : true,
36404         /**
36405         * @event expand
36406         * Fires when this node is expanded
36407         * @param {Node} this This node
36408         */
36409         "expand" : true,
36410         /**
36411         * @event disabledchange
36412         * Fires when the disabled status of this node changes
36413         * @param {Node} this This node
36414         * @param {Boolean} disabled
36415         */
36416         "disabledchange" : true,
36417         /**
36418         * @event collapse
36419         * Fires when this node is collapsed
36420         * @param {Node} this This node
36421         */
36422         "collapse" : true,
36423         /**
36424         * @event beforeclick
36425         * Fires before click processing. Return false to cancel the default action.
36426         * @param {Node} this This node
36427         * @param {Roo.EventObject} e The event object
36428         */
36429         "beforeclick":true,
36430         /**
36431         * @event checkchange
36432         * Fires when a node with a checkbox's checked property changes
36433         * @param {Node} this This node
36434         * @param {Boolean} checked
36435         */
36436         "checkchange":true,
36437         /**
36438         * @event click
36439         * Fires when this node is clicked
36440         * @param {Node} this This node
36441         * @param {Roo.EventObject} e The event object
36442         */
36443         "click":true,
36444         /**
36445         * @event dblclick
36446         * Fires when this node is double clicked
36447         * @param {Node} this This node
36448         * @param {Roo.EventObject} e The event object
36449         */
36450         "dblclick":true,
36451         /**
36452         * @event contextmenu
36453         * Fires when this node is right clicked
36454         * @param {Node} this This node
36455         * @param {Roo.EventObject} e The event object
36456         */
36457         "contextmenu":true,
36458         /**
36459         * @event beforechildrenrendered
36460         * Fires right before the child nodes for this node are rendered
36461         * @param {Node} this This node
36462         */
36463         "beforechildrenrendered":true
36464     });
36465
36466     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36467
36468     /**
36469      * Read-only. The UI for this node
36470      * @type TreeNodeUI
36471      */
36472     this.ui = new uiClass(this);
36473     
36474     // finally support items[]
36475     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36476         return;
36477     }
36478     
36479     
36480     Roo.each(this.attributes.items, function(c) {
36481         this.appendChild(Roo.factory(c,Roo.Tree));
36482     }, this);
36483     delete this.attributes.items;
36484     
36485     
36486     
36487 };
36488 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36489     preventHScroll: true,
36490     /**
36491      * Returns true if this node is expanded
36492      * @return {Boolean}
36493      */
36494     isExpanded : function(){
36495         return this.expanded;
36496     },
36497
36498     /**
36499      * Returns the UI object for this node
36500      * @return {TreeNodeUI}
36501      */
36502     getUI : function(){
36503         return this.ui;
36504     },
36505
36506     // private override
36507     setFirstChild : function(node){
36508         var of = this.firstChild;
36509         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36510         if(this.childrenRendered && of && node != of){
36511             of.renderIndent(true, true);
36512         }
36513         if(this.rendered){
36514             this.renderIndent(true, true);
36515         }
36516     },
36517
36518     // private override
36519     setLastChild : function(node){
36520         var ol = this.lastChild;
36521         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36522         if(this.childrenRendered && ol && node != ol){
36523             ol.renderIndent(true, true);
36524         }
36525         if(this.rendered){
36526             this.renderIndent(true, true);
36527         }
36528     },
36529
36530     // these methods are overridden to provide lazy rendering support
36531     // private override
36532     appendChild : function()
36533     {
36534         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36535         if(node && this.childrenRendered){
36536             node.render();
36537         }
36538         this.ui.updateExpandIcon();
36539         return node;
36540     },
36541
36542     // private override
36543     removeChild : function(node){
36544         this.ownerTree.getSelectionModel().unselect(node);
36545         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36546         // if it's been rendered remove dom node
36547         if(this.childrenRendered){
36548             node.ui.remove();
36549         }
36550         if(this.childNodes.length < 1){
36551             this.collapse(false, false);
36552         }else{
36553             this.ui.updateExpandIcon();
36554         }
36555         if(!this.firstChild) {
36556             this.childrenRendered = false;
36557         }
36558         return node;
36559     },
36560
36561     // private override
36562     insertBefore : function(node, refNode){
36563         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36564         if(newNode && refNode && this.childrenRendered){
36565             node.render();
36566         }
36567         this.ui.updateExpandIcon();
36568         return newNode;
36569     },
36570
36571     /**
36572      * Sets the text for this node
36573      * @param {String} text
36574      */
36575     setText : function(text){
36576         var oldText = this.text;
36577         this.text = text;
36578         this.attributes.text = text;
36579         if(this.rendered){ // event without subscribing
36580             this.ui.onTextChange(this, text, oldText);
36581         }
36582         this.fireEvent("textchange", this, text, oldText);
36583     },
36584
36585     /**
36586      * Triggers selection of this node
36587      */
36588     select : function(){
36589         this.getOwnerTree().getSelectionModel().select(this);
36590     },
36591
36592     /**
36593      * Triggers deselection of this node
36594      */
36595     unselect : function(){
36596         this.getOwnerTree().getSelectionModel().unselect(this);
36597     },
36598
36599     /**
36600      * Returns true if this node is selected
36601      * @return {Boolean}
36602      */
36603     isSelected : function(){
36604         return this.getOwnerTree().getSelectionModel().isSelected(this);
36605     },
36606
36607     /**
36608      * Expand this node.
36609      * @param {Boolean} deep (optional) True to expand all children as well
36610      * @param {Boolean} anim (optional) false to cancel the default animation
36611      * @param {Function} callback (optional) A callback to be called when
36612      * expanding this node completes (does not wait for deep expand to complete).
36613      * Called with 1 parameter, this node.
36614      */
36615     expand : function(deep, anim, callback){
36616         if(!this.expanded){
36617             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36618                 return;
36619             }
36620             if(!this.childrenRendered){
36621                 this.renderChildren();
36622             }
36623             this.expanded = true;
36624             
36625             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36626                 this.ui.animExpand(function(){
36627                     this.fireEvent("expand", this);
36628                     if(typeof callback == "function"){
36629                         callback(this);
36630                     }
36631                     if(deep === true){
36632                         this.expandChildNodes(true);
36633                     }
36634                 }.createDelegate(this));
36635                 return;
36636             }else{
36637                 this.ui.expand();
36638                 this.fireEvent("expand", this);
36639                 if(typeof callback == "function"){
36640                     callback(this);
36641                 }
36642             }
36643         }else{
36644            if(typeof callback == "function"){
36645                callback(this);
36646            }
36647         }
36648         if(deep === true){
36649             this.expandChildNodes(true);
36650         }
36651     },
36652
36653     isHiddenRoot : function(){
36654         return this.isRoot && !this.getOwnerTree().rootVisible;
36655     },
36656
36657     /**
36658      * Collapse this node.
36659      * @param {Boolean} deep (optional) True to collapse all children as well
36660      * @param {Boolean} anim (optional) false to cancel the default animation
36661      */
36662     collapse : function(deep, anim){
36663         if(this.expanded && !this.isHiddenRoot()){
36664             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36665                 return;
36666             }
36667             this.expanded = false;
36668             if((this.getOwnerTree().animate && anim !== false) || anim){
36669                 this.ui.animCollapse(function(){
36670                     this.fireEvent("collapse", this);
36671                     if(deep === true){
36672                         this.collapseChildNodes(true);
36673                     }
36674                 }.createDelegate(this));
36675                 return;
36676             }else{
36677                 this.ui.collapse();
36678                 this.fireEvent("collapse", this);
36679             }
36680         }
36681         if(deep === true){
36682             var cs = this.childNodes;
36683             for(var i = 0, len = cs.length; i < len; i++) {
36684                 cs[i].collapse(true, false);
36685             }
36686         }
36687     },
36688
36689     // private
36690     delayedExpand : function(delay){
36691         if(!this.expandProcId){
36692             this.expandProcId = this.expand.defer(delay, this);
36693         }
36694     },
36695
36696     // private
36697     cancelExpand : function(){
36698         if(this.expandProcId){
36699             clearTimeout(this.expandProcId);
36700         }
36701         this.expandProcId = false;
36702     },
36703
36704     /**
36705      * Toggles expanded/collapsed state of the node
36706      */
36707     toggle : function(){
36708         if(this.expanded){
36709             this.collapse();
36710         }else{
36711             this.expand();
36712         }
36713     },
36714
36715     /**
36716      * Ensures all parent nodes are expanded
36717      */
36718     ensureVisible : function(callback){
36719         var tree = this.getOwnerTree();
36720         tree.expandPath(this.parentNode.getPath(), false, function(){
36721             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36722             Roo.callback(callback);
36723         }.createDelegate(this));
36724     },
36725
36726     /**
36727      * Expand all child nodes
36728      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36729      */
36730     expandChildNodes : function(deep){
36731         var cs = this.childNodes;
36732         for(var i = 0, len = cs.length; i < len; i++) {
36733                 cs[i].expand(deep);
36734         }
36735     },
36736
36737     /**
36738      * Collapse all child nodes
36739      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36740      */
36741     collapseChildNodes : function(deep){
36742         var cs = this.childNodes;
36743         for(var i = 0, len = cs.length; i < len; i++) {
36744                 cs[i].collapse(deep);
36745         }
36746     },
36747
36748     /**
36749      * Disables this node
36750      */
36751     disable : function(){
36752         this.disabled = true;
36753         this.unselect();
36754         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36755             this.ui.onDisableChange(this, true);
36756         }
36757         this.fireEvent("disabledchange", this, true);
36758     },
36759
36760     /**
36761      * Enables this node
36762      */
36763     enable : function(){
36764         this.disabled = false;
36765         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36766             this.ui.onDisableChange(this, false);
36767         }
36768         this.fireEvent("disabledchange", this, false);
36769     },
36770
36771     // private
36772     renderChildren : function(suppressEvent){
36773         if(suppressEvent !== false){
36774             this.fireEvent("beforechildrenrendered", this);
36775         }
36776         var cs = this.childNodes;
36777         for(var i = 0, len = cs.length; i < len; i++){
36778             cs[i].render(true);
36779         }
36780         this.childrenRendered = true;
36781     },
36782
36783     // private
36784     sort : function(fn, scope){
36785         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36786         if(this.childrenRendered){
36787             var cs = this.childNodes;
36788             for(var i = 0, len = cs.length; i < len; i++){
36789                 cs[i].render(true);
36790             }
36791         }
36792     },
36793
36794     // private
36795     render : function(bulkRender){
36796         this.ui.render(bulkRender);
36797         if(!this.rendered){
36798             this.rendered = true;
36799             if(this.expanded){
36800                 this.expanded = false;
36801                 this.expand(false, false);
36802             }
36803         }
36804     },
36805
36806     // private
36807     renderIndent : function(deep, refresh){
36808         if(refresh){
36809             this.ui.childIndent = null;
36810         }
36811         this.ui.renderIndent();
36812         if(deep === true && this.childrenRendered){
36813             var cs = this.childNodes;
36814             for(var i = 0, len = cs.length; i < len; i++){
36815                 cs[i].renderIndent(true, refresh);
36816             }
36817         }
36818     }
36819 });/*
36820  * Based on:
36821  * Ext JS Library 1.1.1
36822  * Copyright(c) 2006-2007, Ext JS, LLC.
36823  *
36824  * Originally Released Under LGPL - original licence link has changed is not relivant.
36825  *
36826  * Fork - LGPL
36827  * <script type="text/javascript">
36828  */
36829  
36830 /**
36831  * @class Roo.tree.AsyncTreeNode
36832  * @extends Roo.tree.TreeNode
36833  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36834  * @constructor
36835  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36836  */
36837  Roo.tree.AsyncTreeNode = function(config){
36838     this.loaded = false;
36839     this.loading = false;
36840     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36841     /**
36842     * @event beforeload
36843     * Fires before this node is loaded, return false to cancel
36844     * @param {Node} this This node
36845     */
36846     this.addEvents({'beforeload':true, 'load': true});
36847     /**
36848     * @event load
36849     * Fires when this node is loaded
36850     * @param {Node} this This node
36851     */
36852     /**
36853      * The loader used by this node (defaults to using the tree's defined loader)
36854      * @type TreeLoader
36855      * @property loader
36856      */
36857 };
36858 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36859     expand : function(deep, anim, callback){
36860         if(this.loading){ // if an async load is already running, waiting til it's done
36861             var timer;
36862             var f = function(){
36863                 if(!this.loading){ // done loading
36864                     clearInterval(timer);
36865                     this.expand(deep, anim, callback);
36866                 }
36867             }.createDelegate(this);
36868             timer = setInterval(f, 200);
36869             return;
36870         }
36871         if(!this.loaded){
36872             if(this.fireEvent("beforeload", this) === false){
36873                 return;
36874             }
36875             this.loading = true;
36876             this.ui.beforeLoad(this);
36877             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36878             if(loader){
36879                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36880                 return;
36881             }
36882         }
36883         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36884     },
36885     
36886     /**
36887      * Returns true if this node is currently loading
36888      * @return {Boolean}
36889      */
36890     isLoading : function(){
36891         return this.loading;  
36892     },
36893     
36894     loadComplete : function(deep, anim, callback){
36895         this.loading = false;
36896         this.loaded = true;
36897         this.ui.afterLoad(this);
36898         this.fireEvent("load", this);
36899         this.expand(deep, anim, callback);
36900     },
36901     
36902     /**
36903      * Returns true if this node has been loaded
36904      * @return {Boolean}
36905      */
36906     isLoaded : function(){
36907         return this.loaded;
36908     },
36909     
36910     hasChildNodes : function(){
36911         if(!this.isLeaf() && !this.loaded){
36912             return true;
36913         }else{
36914             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36915         }
36916     },
36917
36918     /**
36919      * Trigger a reload for this node
36920      * @param {Function} callback
36921      */
36922     reload : function(callback){
36923         this.collapse(false, false);
36924         while(this.firstChild){
36925             this.removeChild(this.firstChild);
36926         }
36927         this.childrenRendered = false;
36928         this.loaded = false;
36929         if(this.isHiddenRoot()){
36930             this.expanded = false;
36931         }
36932         this.expand(false, false, callback);
36933     }
36934 });/*
36935  * Based on:
36936  * Ext JS Library 1.1.1
36937  * Copyright(c) 2006-2007, Ext JS, LLC.
36938  *
36939  * Originally Released Under LGPL - original licence link has changed is not relivant.
36940  *
36941  * Fork - LGPL
36942  * <script type="text/javascript">
36943  */
36944  
36945 /**
36946  * @class Roo.tree.TreeNodeUI
36947  * @constructor
36948  * @param {Object} node The node to render
36949  * The TreeNode UI implementation is separate from the
36950  * tree implementation. Unless you are customizing the tree UI,
36951  * you should never have to use this directly.
36952  */
36953 Roo.tree.TreeNodeUI = function(node){
36954     this.node = node;
36955     this.rendered = false;
36956     this.animating = false;
36957     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36958 };
36959
36960 Roo.tree.TreeNodeUI.prototype = {
36961     removeChild : function(node){
36962         if(this.rendered){
36963             this.ctNode.removeChild(node.ui.getEl());
36964         }
36965     },
36966
36967     beforeLoad : function(){
36968          this.addClass("x-tree-node-loading");
36969     },
36970
36971     afterLoad : function(){
36972          this.removeClass("x-tree-node-loading");
36973     },
36974
36975     onTextChange : function(node, text, oldText){
36976         if(this.rendered){
36977             this.textNode.innerHTML = text;
36978         }
36979     },
36980
36981     onDisableChange : function(node, state){
36982         this.disabled = state;
36983         if(state){
36984             this.addClass("x-tree-node-disabled");
36985         }else{
36986             this.removeClass("x-tree-node-disabled");
36987         }
36988     },
36989
36990     onSelectedChange : function(state){
36991         if(state){
36992             this.focus();
36993             this.addClass("x-tree-selected");
36994         }else{
36995             //this.blur();
36996             this.removeClass("x-tree-selected");
36997         }
36998     },
36999
37000     onMove : function(tree, node, oldParent, newParent, index, refNode){
37001         this.childIndent = null;
37002         if(this.rendered){
37003             var targetNode = newParent.ui.getContainer();
37004             if(!targetNode){//target not rendered
37005                 this.holder = document.createElement("div");
37006                 this.holder.appendChild(this.wrap);
37007                 return;
37008             }
37009             var insertBefore = refNode ? refNode.ui.getEl() : null;
37010             if(insertBefore){
37011                 targetNode.insertBefore(this.wrap, insertBefore);
37012             }else{
37013                 targetNode.appendChild(this.wrap);
37014             }
37015             this.node.renderIndent(true);
37016         }
37017     },
37018
37019     addClass : function(cls){
37020         if(this.elNode){
37021             Roo.fly(this.elNode).addClass(cls);
37022         }
37023     },
37024
37025     removeClass : function(cls){
37026         if(this.elNode){
37027             Roo.fly(this.elNode).removeClass(cls);
37028         }
37029     },
37030
37031     remove : function(){
37032         if(this.rendered){
37033             this.holder = document.createElement("div");
37034             this.holder.appendChild(this.wrap);
37035         }
37036     },
37037
37038     fireEvent : function(){
37039         return this.node.fireEvent.apply(this.node, arguments);
37040     },
37041
37042     initEvents : function(){
37043         this.node.on("move", this.onMove, this);
37044         var E = Roo.EventManager;
37045         var a = this.anchor;
37046
37047         var el = Roo.fly(a, '_treeui');
37048
37049         if(Roo.isOpera){ // opera render bug ignores the CSS
37050             el.setStyle("text-decoration", "none");
37051         }
37052
37053         el.on("click", this.onClick, this);
37054         el.on("dblclick", this.onDblClick, this);
37055
37056         if(this.checkbox){
37057             Roo.EventManager.on(this.checkbox,
37058                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37059         }
37060
37061         el.on("contextmenu", this.onContextMenu, this);
37062
37063         var icon = Roo.fly(this.iconNode);
37064         icon.on("click", this.onClick, this);
37065         icon.on("dblclick", this.onDblClick, this);
37066         icon.on("contextmenu", this.onContextMenu, this);
37067         E.on(this.ecNode, "click", this.ecClick, this, true);
37068
37069         if(this.node.disabled){
37070             this.addClass("x-tree-node-disabled");
37071         }
37072         if(this.node.hidden){
37073             this.addClass("x-tree-node-disabled");
37074         }
37075         var ot = this.node.getOwnerTree();
37076         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37077         if(dd && (!this.node.isRoot || ot.rootVisible)){
37078             Roo.dd.Registry.register(this.elNode, {
37079                 node: this.node,
37080                 handles: this.getDDHandles(),
37081                 isHandle: false
37082             });
37083         }
37084     },
37085
37086     getDDHandles : function(){
37087         return [this.iconNode, this.textNode];
37088     },
37089
37090     hide : function(){
37091         if(this.rendered){
37092             this.wrap.style.display = "none";
37093         }
37094     },
37095
37096     show : function(){
37097         if(this.rendered){
37098             this.wrap.style.display = "";
37099         }
37100     },
37101
37102     onContextMenu : function(e){
37103         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37104             e.preventDefault();
37105             this.focus();
37106             this.fireEvent("contextmenu", this.node, e);
37107         }
37108     },
37109
37110     onClick : function(e){
37111         if(this.dropping){
37112             e.stopEvent();
37113             return;
37114         }
37115         if(this.fireEvent("beforeclick", this.node, e) !== false){
37116             if(!this.disabled && this.node.attributes.href){
37117                 this.fireEvent("click", this.node, e);
37118                 return;
37119             }
37120             e.preventDefault();
37121             if(this.disabled){
37122                 return;
37123             }
37124
37125             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37126                 this.node.toggle();
37127             }
37128
37129             this.fireEvent("click", this.node, e);
37130         }else{
37131             e.stopEvent();
37132         }
37133     },
37134
37135     onDblClick : function(e){
37136         e.preventDefault();
37137         if(this.disabled){
37138             return;
37139         }
37140         if(this.checkbox){
37141             this.toggleCheck();
37142         }
37143         if(!this.animating && this.node.hasChildNodes()){
37144             this.node.toggle();
37145         }
37146         this.fireEvent("dblclick", this.node, e);
37147     },
37148
37149     onCheckChange : function(){
37150         var checked = this.checkbox.checked;
37151         this.node.attributes.checked = checked;
37152         this.fireEvent('checkchange', this.node, checked);
37153     },
37154
37155     ecClick : function(e){
37156         if(!this.animating && this.node.hasChildNodes()){
37157             this.node.toggle();
37158         }
37159     },
37160
37161     startDrop : function(){
37162         this.dropping = true;
37163     },
37164
37165     // delayed drop so the click event doesn't get fired on a drop
37166     endDrop : function(){
37167        setTimeout(function(){
37168            this.dropping = false;
37169        }.createDelegate(this), 50);
37170     },
37171
37172     expand : function(){
37173         this.updateExpandIcon();
37174         this.ctNode.style.display = "";
37175     },
37176
37177     focus : function(){
37178         if(!this.node.preventHScroll){
37179             try{this.anchor.focus();
37180             }catch(e){}
37181         }else if(!Roo.isIE){
37182             try{
37183                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37184                 var l = noscroll.scrollLeft;
37185                 this.anchor.focus();
37186                 noscroll.scrollLeft = l;
37187             }catch(e){}
37188         }
37189     },
37190
37191     toggleCheck : function(value){
37192         var cb = this.checkbox;
37193         if(cb){
37194             cb.checked = (value === undefined ? !cb.checked : value);
37195         }
37196     },
37197
37198     blur : function(){
37199         try{
37200             this.anchor.blur();
37201         }catch(e){}
37202     },
37203
37204     animExpand : function(callback){
37205         var ct = Roo.get(this.ctNode);
37206         ct.stopFx();
37207         if(!this.node.hasChildNodes()){
37208             this.updateExpandIcon();
37209             this.ctNode.style.display = "";
37210             Roo.callback(callback);
37211             return;
37212         }
37213         this.animating = true;
37214         this.updateExpandIcon();
37215
37216         ct.slideIn('t', {
37217            callback : function(){
37218                this.animating = false;
37219                Roo.callback(callback);
37220             },
37221             scope: this,
37222             duration: this.node.ownerTree.duration || .25
37223         });
37224     },
37225
37226     highlight : function(){
37227         var tree = this.node.getOwnerTree();
37228         Roo.fly(this.wrap).highlight(
37229             tree.hlColor || "C3DAF9",
37230             {endColor: tree.hlBaseColor}
37231         );
37232     },
37233
37234     collapse : function(){
37235         this.updateExpandIcon();
37236         this.ctNode.style.display = "none";
37237     },
37238
37239     animCollapse : function(callback){
37240         var ct = Roo.get(this.ctNode);
37241         ct.enableDisplayMode('block');
37242         ct.stopFx();
37243
37244         this.animating = true;
37245         this.updateExpandIcon();
37246
37247         ct.slideOut('t', {
37248             callback : function(){
37249                this.animating = false;
37250                Roo.callback(callback);
37251             },
37252             scope: this,
37253             duration: this.node.ownerTree.duration || .25
37254         });
37255     },
37256
37257     getContainer : function(){
37258         return this.ctNode;
37259     },
37260
37261     getEl : function(){
37262         return this.wrap;
37263     },
37264
37265     appendDDGhost : function(ghostNode){
37266         ghostNode.appendChild(this.elNode.cloneNode(true));
37267     },
37268
37269     getDDRepairXY : function(){
37270         return Roo.lib.Dom.getXY(this.iconNode);
37271     },
37272
37273     onRender : function(){
37274         this.render();
37275     },
37276
37277     render : function(bulkRender){
37278         var n = this.node, a = n.attributes;
37279         var targetNode = n.parentNode ?
37280               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37281
37282         if(!this.rendered){
37283             this.rendered = true;
37284
37285             this.renderElements(n, a, targetNode, bulkRender);
37286
37287             if(a.qtip){
37288                if(this.textNode.setAttributeNS){
37289                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37290                    if(a.qtipTitle){
37291                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37292                    }
37293                }else{
37294                    this.textNode.setAttribute("ext:qtip", a.qtip);
37295                    if(a.qtipTitle){
37296                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37297                    }
37298                }
37299             }else if(a.qtipCfg){
37300                 a.qtipCfg.target = Roo.id(this.textNode);
37301                 Roo.QuickTips.register(a.qtipCfg);
37302             }
37303             this.initEvents();
37304             if(!this.node.expanded){
37305                 this.updateExpandIcon();
37306             }
37307         }else{
37308             if(bulkRender === true) {
37309                 targetNode.appendChild(this.wrap);
37310             }
37311         }
37312     },
37313
37314     renderElements : function(n, a, targetNode, bulkRender)
37315     {
37316         // add some indent caching, this helps performance when rendering a large tree
37317         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37318         var t = n.getOwnerTree();
37319         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37320         if (typeof(n.attributes.html) != 'undefined') {
37321             txt = n.attributes.html;
37322         }
37323         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37324         var cb = typeof a.checked == 'boolean';
37325         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37326         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37327             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37328             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37329             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37330             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37331             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37332              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37333                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37334             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37335             "</li>"];
37336
37337         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37338             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37339                                 n.nextSibling.ui.getEl(), buf.join(""));
37340         }else{
37341             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37342         }
37343
37344         this.elNode = this.wrap.childNodes[0];
37345         this.ctNode = this.wrap.childNodes[1];
37346         var cs = this.elNode.childNodes;
37347         this.indentNode = cs[0];
37348         this.ecNode = cs[1];
37349         this.iconNode = cs[2];
37350         var index = 3;
37351         if(cb){
37352             this.checkbox = cs[3];
37353             index++;
37354         }
37355         this.anchor = cs[index];
37356         this.textNode = cs[index].firstChild;
37357     },
37358
37359     getAnchor : function(){
37360         return this.anchor;
37361     },
37362
37363     getTextEl : function(){
37364         return this.textNode;
37365     },
37366
37367     getIconEl : function(){
37368         return this.iconNode;
37369     },
37370
37371     isChecked : function(){
37372         return this.checkbox ? this.checkbox.checked : false;
37373     },
37374
37375     updateExpandIcon : function(){
37376         if(this.rendered){
37377             var n = this.node, c1, c2;
37378             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37379             var hasChild = n.hasChildNodes();
37380             if(hasChild){
37381                 if(n.expanded){
37382                     cls += "-minus";
37383                     c1 = "x-tree-node-collapsed";
37384                     c2 = "x-tree-node-expanded";
37385                 }else{
37386                     cls += "-plus";
37387                     c1 = "x-tree-node-expanded";
37388                     c2 = "x-tree-node-collapsed";
37389                 }
37390                 if(this.wasLeaf){
37391                     this.removeClass("x-tree-node-leaf");
37392                     this.wasLeaf = false;
37393                 }
37394                 if(this.c1 != c1 || this.c2 != c2){
37395                     Roo.fly(this.elNode).replaceClass(c1, c2);
37396                     this.c1 = c1; this.c2 = c2;
37397                 }
37398             }else{
37399                 // this changes non-leafs into leafs if they have no children.
37400                 // it's not very rational behaviour..
37401                 
37402                 if(!this.wasLeaf && this.node.leaf){
37403                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37404                     delete this.c1;
37405                     delete this.c2;
37406                     this.wasLeaf = true;
37407                 }
37408             }
37409             var ecc = "x-tree-ec-icon "+cls;
37410             if(this.ecc != ecc){
37411                 this.ecNode.className = ecc;
37412                 this.ecc = ecc;
37413             }
37414         }
37415     },
37416
37417     getChildIndent : function(){
37418         if(!this.childIndent){
37419             var buf = [];
37420             var p = this.node;
37421             while(p){
37422                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37423                     if(!p.isLast()) {
37424                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37425                     } else {
37426                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37427                     }
37428                 }
37429                 p = p.parentNode;
37430             }
37431             this.childIndent = buf.join("");
37432         }
37433         return this.childIndent;
37434     },
37435
37436     renderIndent : function(){
37437         if(this.rendered){
37438             var indent = "";
37439             var p = this.node.parentNode;
37440             if(p){
37441                 indent = p.ui.getChildIndent();
37442             }
37443             if(this.indentMarkup != indent){ // don't rerender if not required
37444                 this.indentNode.innerHTML = indent;
37445                 this.indentMarkup = indent;
37446             }
37447             this.updateExpandIcon();
37448         }
37449     }
37450 };
37451
37452 Roo.tree.RootTreeNodeUI = function(){
37453     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37454 };
37455 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37456     render : function(){
37457         if(!this.rendered){
37458             var targetNode = this.node.ownerTree.innerCt.dom;
37459             this.node.expanded = true;
37460             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37461             this.wrap = this.ctNode = targetNode.firstChild;
37462         }
37463     },
37464     collapse : function(){
37465     },
37466     expand : function(){
37467     }
37468 });/*
37469  * Based on:
37470  * Ext JS Library 1.1.1
37471  * Copyright(c) 2006-2007, Ext JS, LLC.
37472  *
37473  * Originally Released Under LGPL - original licence link has changed is not relivant.
37474  *
37475  * Fork - LGPL
37476  * <script type="text/javascript">
37477  */
37478 /**
37479  * @class Roo.tree.TreeLoader
37480  * @extends Roo.util.Observable
37481  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37482  * nodes from a specified URL. The response must be a javascript Array definition
37483  * who's elements are node definition objects. eg:
37484  * <pre><code>
37485 {  success : true,
37486    data :      [
37487    
37488     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37489     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37490     ]
37491 }
37492
37493
37494 </code></pre>
37495  * <br><br>
37496  * The old style respose with just an array is still supported, but not recommended.
37497  * <br><br>
37498  *
37499  * A server request is sent, and child nodes are loaded only when a node is expanded.
37500  * The loading node's id is passed to the server under the parameter name "node" to
37501  * enable the server to produce the correct child nodes.
37502  * <br><br>
37503  * To pass extra parameters, an event handler may be attached to the "beforeload"
37504  * event, and the parameters specified in the TreeLoader's baseParams property:
37505  * <pre><code>
37506     myTreeLoader.on("beforeload", function(treeLoader, node) {
37507         this.baseParams.category = node.attributes.category;
37508     }, this);
37509     
37510 </code></pre>
37511  *
37512  * This would pass an HTTP parameter called "category" to the server containing
37513  * the value of the Node's "category" attribute.
37514  * @constructor
37515  * Creates a new Treeloader.
37516  * @param {Object} config A config object containing config properties.
37517  */
37518 Roo.tree.TreeLoader = function(config){
37519     this.baseParams = {};
37520     this.requestMethod = "POST";
37521     Roo.apply(this, config);
37522
37523     this.addEvents({
37524     
37525         /**
37526          * @event beforeload
37527          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37528          * @param {Object} This TreeLoader object.
37529          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37530          * @param {Object} callback The callback function specified in the {@link #load} call.
37531          */
37532         beforeload : true,
37533         /**
37534          * @event load
37535          * Fires when the node has been successfuly loaded.
37536          * @param {Object} This TreeLoader object.
37537          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37538          * @param {Object} response The response object containing the data from the server.
37539          */
37540         load : true,
37541         /**
37542          * @event loadexception
37543          * Fires if the network request failed.
37544          * @param {Object} This TreeLoader object.
37545          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37546          * @param {Object} response The response object containing the data from the server.
37547          */
37548         loadexception : true,
37549         /**
37550          * @event create
37551          * Fires before a node is created, enabling you to return custom Node types 
37552          * @param {Object} This TreeLoader object.
37553          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37554          */
37555         create : true
37556     });
37557
37558     Roo.tree.TreeLoader.superclass.constructor.call(this);
37559 };
37560
37561 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37562     /**
37563     * @cfg {String} dataUrl The URL from which to request a Json string which
37564     * specifies an array of node definition object representing the child nodes
37565     * to be loaded.
37566     */
37567     /**
37568     * @cfg {String} requestMethod either GET or POST
37569     * defaults to POST (due to BC)
37570     * to be loaded.
37571     */
37572     /**
37573     * @cfg {Object} baseParams (optional) An object containing properties which
37574     * specify HTTP parameters to be passed to each request for child nodes.
37575     */
37576     /**
37577     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37578     * created by this loader. If the attributes sent by the server have an attribute in this object,
37579     * they take priority.
37580     */
37581     /**
37582     * @cfg {Object} uiProviders (optional) An object containing properties which
37583     * 
37584     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37585     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37586     * <i>uiProvider</i> attribute of a returned child node is a string rather
37587     * than a reference to a TreeNodeUI implementation, this that string value
37588     * is used as a property name in the uiProviders object. You can define the provider named
37589     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37590     */
37591     uiProviders : {},
37592
37593     /**
37594     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37595     * child nodes before loading.
37596     */
37597     clearOnLoad : true,
37598
37599     /**
37600     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37601     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37602     * Grid query { data : [ .....] }
37603     */
37604     
37605     root : false,
37606      /**
37607     * @cfg {String} queryParam (optional) 
37608     * Name of the query as it will be passed on the querystring (defaults to 'node')
37609     * eg. the request will be ?node=[id]
37610     */
37611     
37612     
37613     queryParam: false,
37614     
37615     /**
37616      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37617      * This is called automatically when a node is expanded, but may be used to reload
37618      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37619      * @param {Roo.tree.TreeNode} node
37620      * @param {Function} callback
37621      */
37622     load : function(node, callback){
37623         if(this.clearOnLoad){
37624             while(node.firstChild){
37625                 node.removeChild(node.firstChild);
37626             }
37627         }
37628         if(node.attributes.children){ // preloaded json children
37629             var cs = node.attributes.children;
37630             for(var i = 0, len = cs.length; i < len; i++){
37631                 node.appendChild(this.createNode(cs[i]));
37632             }
37633             if(typeof callback == "function"){
37634                 callback();
37635             }
37636         }else if(this.dataUrl){
37637             this.requestData(node, callback);
37638         }
37639     },
37640
37641     getParams: function(node){
37642         var buf = [], bp = this.baseParams;
37643         for(var key in bp){
37644             if(typeof bp[key] != "function"){
37645                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37646             }
37647         }
37648         var n = this.queryParam === false ? 'node' : this.queryParam;
37649         buf.push(n + "=", encodeURIComponent(node.id));
37650         return buf.join("");
37651     },
37652
37653     requestData : function(node, callback){
37654         if(this.fireEvent("beforeload", this, node, callback) !== false){
37655             this.transId = Roo.Ajax.request({
37656                 method:this.requestMethod,
37657                 url: this.dataUrl||this.url,
37658                 success: this.handleResponse,
37659                 failure: this.handleFailure,
37660                 scope: this,
37661                 argument: {callback: callback, node: node},
37662                 params: this.getParams(node)
37663             });
37664         }else{
37665             // if the load is cancelled, make sure we notify
37666             // the node that we are done
37667             if(typeof callback == "function"){
37668                 callback();
37669             }
37670         }
37671     },
37672
37673     isLoading : function(){
37674         return this.transId ? true : false;
37675     },
37676
37677     abort : function(){
37678         if(this.isLoading()){
37679             Roo.Ajax.abort(this.transId);
37680         }
37681     },
37682
37683     // private
37684     createNode : function(attr)
37685     {
37686         // apply baseAttrs, nice idea Corey!
37687         if(this.baseAttrs){
37688             Roo.applyIf(attr, this.baseAttrs);
37689         }
37690         if(this.applyLoader !== false){
37691             attr.loader = this;
37692         }
37693         // uiProvider = depreciated..
37694         
37695         if(typeof(attr.uiProvider) == 'string'){
37696            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37697                 /**  eval:var:attr */ eval(attr.uiProvider);
37698         }
37699         if(typeof(this.uiProviders['default']) != 'undefined') {
37700             attr.uiProvider = this.uiProviders['default'];
37701         }
37702         
37703         this.fireEvent('create', this, attr);
37704         
37705         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37706         return(attr.leaf ?
37707                         new Roo.tree.TreeNode(attr) :
37708                         new Roo.tree.AsyncTreeNode(attr));
37709     },
37710
37711     processResponse : function(response, node, callback)
37712     {
37713         var json = response.responseText;
37714         try {
37715             
37716             var o = Roo.decode(json);
37717             
37718             if (this.root === false && typeof(o.success) != undefined) {
37719                 this.root = 'data'; // the default behaviour for list like data..
37720                 }
37721                 
37722             if (this.root !== false &&  !o.success) {
37723                 // it's a failure condition.
37724                 var a = response.argument;
37725                 this.fireEvent("loadexception", this, a.node, response);
37726                 Roo.log("Load failed - should have a handler really");
37727                 return;
37728             }
37729             
37730             
37731             
37732             if (this.root !== false) {
37733                  o = o[this.root];
37734             }
37735             
37736             for(var i = 0, len = o.length; i < len; i++){
37737                 var n = this.createNode(o[i]);
37738                 if(n){
37739                     node.appendChild(n);
37740                 }
37741             }
37742             if(typeof callback == "function"){
37743                 callback(this, node);
37744             }
37745         }catch(e){
37746             this.handleFailure(response);
37747         }
37748     },
37749
37750     handleResponse : function(response){
37751         this.transId = false;
37752         var a = response.argument;
37753         this.processResponse(response, a.node, a.callback);
37754         this.fireEvent("load", this, a.node, response);
37755     },
37756
37757     handleFailure : function(response)
37758     {
37759         // should handle failure better..
37760         this.transId = false;
37761         var a = response.argument;
37762         this.fireEvent("loadexception", this, a.node, response);
37763         if(typeof a.callback == "function"){
37764             a.callback(this, a.node);
37765         }
37766     }
37767 });/*
37768  * Based on:
37769  * Ext JS Library 1.1.1
37770  * Copyright(c) 2006-2007, Ext JS, LLC.
37771  *
37772  * Originally Released Under LGPL - original licence link has changed is not relivant.
37773  *
37774  * Fork - LGPL
37775  * <script type="text/javascript">
37776  */
37777
37778 /**
37779 * @class Roo.tree.TreeFilter
37780 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37781 * @param {TreePanel} tree
37782 * @param {Object} config (optional)
37783  */
37784 Roo.tree.TreeFilter = function(tree, config){
37785     this.tree = tree;
37786     this.filtered = {};
37787     Roo.apply(this, config);
37788 };
37789
37790 Roo.tree.TreeFilter.prototype = {
37791     clearBlank:false,
37792     reverse:false,
37793     autoClear:false,
37794     remove:false,
37795
37796      /**
37797      * Filter the data by a specific attribute.
37798      * @param {String/RegExp} value Either string that the attribute value
37799      * should start with or a RegExp to test against the attribute
37800      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37801      * @param {TreeNode} startNode (optional) The node to start the filter at.
37802      */
37803     filter : function(value, attr, startNode){
37804         attr = attr || "text";
37805         var f;
37806         if(typeof value == "string"){
37807             var vlen = value.length;
37808             // auto clear empty filter
37809             if(vlen == 0 && this.clearBlank){
37810                 this.clear();
37811                 return;
37812             }
37813             value = value.toLowerCase();
37814             f = function(n){
37815                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37816             };
37817         }else if(value.exec){ // regex?
37818             f = function(n){
37819                 return value.test(n.attributes[attr]);
37820             };
37821         }else{
37822             throw 'Illegal filter type, must be string or regex';
37823         }
37824         this.filterBy(f, null, startNode);
37825         },
37826
37827     /**
37828      * Filter by a function. The passed function will be called with each
37829      * node in the tree (or from the startNode). If the function returns true, the node is kept
37830      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37831      * @param {Function} fn The filter function
37832      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37833      */
37834     filterBy : function(fn, scope, startNode){
37835         startNode = startNode || this.tree.root;
37836         if(this.autoClear){
37837             this.clear();
37838         }
37839         var af = this.filtered, rv = this.reverse;
37840         var f = function(n){
37841             if(n == startNode){
37842                 return true;
37843             }
37844             if(af[n.id]){
37845                 return false;
37846             }
37847             var m = fn.call(scope || n, n);
37848             if(!m || rv){
37849                 af[n.id] = n;
37850                 n.ui.hide();
37851                 return false;
37852             }
37853             return true;
37854         };
37855         startNode.cascade(f);
37856         if(this.remove){
37857            for(var id in af){
37858                if(typeof id != "function"){
37859                    var n = af[id];
37860                    if(n && n.parentNode){
37861                        n.parentNode.removeChild(n);
37862                    }
37863                }
37864            }
37865         }
37866     },
37867
37868     /**
37869      * Clears the current filter. Note: with the "remove" option
37870      * set a filter cannot be cleared.
37871      */
37872     clear : function(){
37873         var t = this.tree;
37874         var af = this.filtered;
37875         for(var id in af){
37876             if(typeof id != "function"){
37877                 var n = af[id];
37878                 if(n){
37879                     n.ui.show();
37880                 }
37881             }
37882         }
37883         this.filtered = {};
37884     }
37885 };
37886 /*
37887  * Based on:
37888  * Ext JS Library 1.1.1
37889  * Copyright(c) 2006-2007, Ext JS, LLC.
37890  *
37891  * Originally Released Under LGPL - original licence link has changed is not relivant.
37892  *
37893  * Fork - LGPL
37894  * <script type="text/javascript">
37895  */
37896  
37897
37898 /**
37899  * @class Roo.tree.TreeSorter
37900  * Provides sorting of nodes in a TreePanel
37901  * 
37902  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37903  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37904  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37905  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37906  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37907  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37908  * @constructor
37909  * @param {TreePanel} tree
37910  * @param {Object} config
37911  */
37912 Roo.tree.TreeSorter = function(tree, config){
37913     Roo.apply(this, config);
37914     tree.on("beforechildrenrendered", this.doSort, this);
37915     tree.on("append", this.updateSort, this);
37916     tree.on("insert", this.updateSort, this);
37917     
37918     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37919     var p = this.property || "text";
37920     var sortType = this.sortType;
37921     var fs = this.folderSort;
37922     var cs = this.caseSensitive === true;
37923     var leafAttr = this.leafAttr || 'leaf';
37924
37925     this.sortFn = function(n1, n2){
37926         if(fs){
37927             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37928                 return 1;
37929             }
37930             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37931                 return -1;
37932             }
37933         }
37934         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37935         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37936         if(v1 < v2){
37937                         return dsc ? +1 : -1;
37938                 }else if(v1 > v2){
37939                         return dsc ? -1 : +1;
37940         }else{
37941                 return 0;
37942         }
37943     };
37944 };
37945
37946 Roo.tree.TreeSorter.prototype = {
37947     doSort : function(node){
37948         node.sort(this.sortFn);
37949     },
37950     
37951     compareNodes : function(n1, n2){
37952         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37953     },
37954     
37955     updateSort : function(tree, node){
37956         if(node.childrenRendered){
37957             this.doSort.defer(1, this, [node]);
37958         }
37959     }
37960 };/*
37961  * Based on:
37962  * Ext JS Library 1.1.1
37963  * Copyright(c) 2006-2007, Ext JS, LLC.
37964  *
37965  * Originally Released Under LGPL - original licence link has changed is not relivant.
37966  *
37967  * Fork - LGPL
37968  * <script type="text/javascript">
37969  */
37970
37971 if(Roo.dd.DropZone){
37972     
37973 Roo.tree.TreeDropZone = function(tree, config){
37974     this.allowParentInsert = false;
37975     this.allowContainerDrop = false;
37976     this.appendOnly = false;
37977     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37978     this.tree = tree;
37979     this.lastInsertClass = "x-tree-no-status";
37980     this.dragOverData = {};
37981 };
37982
37983 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37984     ddGroup : "TreeDD",
37985     scroll:  true,
37986     
37987     expandDelay : 1000,
37988     
37989     expandNode : function(node){
37990         if(node.hasChildNodes() && !node.isExpanded()){
37991             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37992         }
37993     },
37994     
37995     queueExpand : function(node){
37996         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37997     },
37998     
37999     cancelExpand : function(){
38000         if(this.expandProcId){
38001             clearTimeout(this.expandProcId);
38002             this.expandProcId = false;
38003         }
38004     },
38005     
38006     isValidDropPoint : function(n, pt, dd, e, data){
38007         if(!n || !data){ return false; }
38008         var targetNode = n.node;
38009         var dropNode = data.node;
38010         // default drop rules
38011         if(!(targetNode && targetNode.isTarget && pt)){
38012             return false;
38013         }
38014         if(pt == "append" && targetNode.allowChildren === false){
38015             return false;
38016         }
38017         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38018             return false;
38019         }
38020         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38021             return false;
38022         }
38023         // reuse the object
38024         var overEvent = this.dragOverData;
38025         overEvent.tree = this.tree;
38026         overEvent.target = targetNode;
38027         overEvent.data = data;
38028         overEvent.point = pt;
38029         overEvent.source = dd;
38030         overEvent.rawEvent = e;
38031         overEvent.dropNode = dropNode;
38032         overEvent.cancel = false;  
38033         var result = this.tree.fireEvent("nodedragover", overEvent);
38034         return overEvent.cancel === false && result !== false;
38035     },
38036     
38037     getDropPoint : function(e, n, dd)
38038     {
38039         var tn = n.node;
38040         if(tn.isRoot){
38041             return tn.allowChildren !== false ? "append" : false; // always append for root
38042         }
38043         var dragEl = n.ddel;
38044         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38045         var y = Roo.lib.Event.getPageY(e);
38046         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38047         
38048         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38049         var noAppend = tn.allowChildren === false;
38050         if(this.appendOnly || tn.parentNode.allowChildren === false){
38051             return noAppend ? false : "append";
38052         }
38053         var noBelow = false;
38054         if(!this.allowParentInsert){
38055             noBelow = tn.hasChildNodes() && tn.isExpanded();
38056         }
38057         var q = (b - t) / (noAppend ? 2 : 3);
38058         if(y >= t && y < (t + q)){
38059             return "above";
38060         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38061             return "below";
38062         }else{
38063             return "append";
38064         }
38065     },
38066     
38067     onNodeEnter : function(n, dd, e, data)
38068     {
38069         this.cancelExpand();
38070     },
38071     
38072     onNodeOver : function(n, dd, e, data)
38073     {
38074        
38075         var pt = this.getDropPoint(e, n, dd);
38076         var node = n.node;
38077         
38078         // auto node expand check
38079         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38080             this.queueExpand(node);
38081         }else if(pt != "append"){
38082             this.cancelExpand();
38083         }
38084         
38085         // set the insert point style on the target node
38086         var returnCls = this.dropNotAllowed;
38087         if(this.isValidDropPoint(n, pt, dd, e, data)){
38088            if(pt){
38089                var el = n.ddel;
38090                var cls;
38091                if(pt == "above"){
38092                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38093                    cls = "x-tree-drag-insert-above";
38094                }else if(pt == "below"){
38095                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38096                    cls = "x-tree-drag-insert-below";
38097                }else{
38098                    returnCls = "x-tree-drop-ok-append";
38099                    cls = "x-tree-drag-append";
38100                }
38101                if(this.lastInsertClass != cls){
38102                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38103                    this.lastInsertClass = cls;
38104                }
38105            }
38106        }
38107        return returnCls;
38108     },
38109     
38110     onNodeOut : function(n, dd, e, data){
38111         
38112         this.cancelExpand();
38113         this.removeDropIndicators(n);
38114     },
38115     
38116     onNodeDrop : function(n, dd, e, data){
38117         var point = this.getDropPoint(e, n, dd);
38118         var targetNode = n.node;
38119         targetNode.ui.startDrop();
38120         if(!this.isValidDropPoint(n, point, dd, e, data)){
38121             targetNode.ui.endDrop();
38122             return false;
38123         }
38124         // first try to find the drop node
38125         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38126         var dropEvent = {
38127             tree : this.tree,
38128             target: targetNode,
38129             data: data,
38130             point: point,
38131             source: dd,
38132             rawEvent: e,
38133             dropNode: dropNode,
38134             cancel: !dropNode   
38135         };
38136         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38137         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38138             targetNode.ui.endDrop();
38139             return false;
38140         }
38141         // allow target changing
38142         targetNode = dropEvent.target;
38143         if(point == "append" && !targetNode.isExpanded()){
38144             targetNode.expand(false, null, function(){
38145                 this.completeDrop(dropEvent);
38146             }.createDelegate(this));
38147         }else{
38148             this.completeDrop(dropEvent);
38149         }
38150         return true;
38151     },
38152     
38153     completeDrop : function(de){
38154         var ns = de.dropNode, p = de.point, t = de.target;
38155         if(!(ns instanceof Array)){
38156             ns = [ns];
38157         }
38158         var n;
38159         for(var i = 0, len = ns.length; i < len; i++){
38160             n = ns[i];
38161             if(p == "above"){
38162                 t.parentNode.insertBefore(n, t);
38163             }else if(p == "below"){
38164                 t.parentNode.insertBefore(n, t.nextSibling);
38165             }else{
38166                 t.appendChild(n);
38167             }
38168         }
38169         n.ui.focus();
38170         if(this.tree.hlDrop){
38171             n.ui.highlight();
38172         }
38173         t.ui.endDrop();
38174         this.tree.fireEvent("nodedrop", de);
38175     },
38176     
38177     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38178         if(this.tree.hlDrop){
38179             dropNode.ui.focus();
38180             dropNode.ui.highlight();
38181         }
38182         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38183     },
38184     
38185     getTree : function(){
38186         return this.tree;
38187     },
38188     
38189     removeDropIndicators : function(n){
38190         if(n && n.ddel){
38191             var el = n.ddel;
38192             Roo.fly(el).removeClass([
38193                     "x-tree-drag-insert-above",
38194                     "x-tree-drag-insert-below",
38195                     "x-tree-drag-append"]);
38196             this.lastInsertClass = "_noclass";
38197         }
38198     },
38199     
38200     beforeDragDrop : function(target, e, id){
38201         this.cancelExpand();
38202         return true;
38203     },
38204     
38205     afterRepair : function(data){
38206         if(data && Roo.enableFx){
38207             data.node.ui.highlight();
38208         }
38209         this.hideProxy();
38210     } 
38211     
38212 });
38213
38214 }
38215 /*
38216  * Based on:
38217  * Ext JS Library 1.1.1
38218  * Copyright(c) 2006-2007, Ext JS, LLC.
38219  *
38220  * Originally Released Under LGPL - original licence link has changed is not relivant.
38221  *
38222  * Fork - LGPL
38223  * <script type="text/javascript">
38224  */
38225  
38226
38227 if(Roo.dd.DragZone){
38228 Roo.tree.TreeDragZone = function(tree, config){
38229     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38230     this.tree = tree;
38231 };
38232
38233 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38234     ddGroup : "TreeDD",
38235    
38236     onBeforeDrag : function(data, e){
38237         var n = data.node;
38238         return n && n.draggable && !n.disabled;
38239     },
38240      
38241     
38242     onInitDrag : function(e){
38243         var data = this.dragData;
38244         this.tree.getSelectionModel().select(data.node);
38245         this.proxy.update("");
38246         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38247         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38248     },
38249     
38250     getRepairXY : function(e, data){
38251         return data.node.ui.getDDRepairXY();
38252     },
38253     
38254     onEndDrag : function(data, e){
38255         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38256         
38257         
38258     },
38259     
38260     onValidDrop : function(dd, e, id){
38261         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38262         this.hideProxy();
38263     },
38264     
38265     beforeInvalidDrop : function(e, id){
38266         // this scrolls the original position back into view
38267         var sm = this.tree.getSelectionModel();
38268         sm.clearSelections();
38269         sm.select(this.dragData.node);
38270     }
38271 });
38272 }/*
38273  * Based on:
38274  * Ext JS Library 1.1.1
38275  * Copyright(c) 2006-2007, Ext JS, LLC.
38276  *
38277  * Originally Released Under LGPL - original licence link has changed is not relivant.
38278  *
38279  * Fork - LGPL
38280  * <script type="text/javascript">
38281  */
38282 /**
38283  * @class Roo.tree.TreeEditor
38284  * @extends Roo.Editor
38285  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38286  * as the editor field.
38287  * @constructor
38288  * @param {Object} config (used to be the tree panel.)
38289  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38290  * 
38291  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38292  * @cfg {Roo.form.TextField} field [required] The field configuration
38293  *
38294  * 
38295  */
38296 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38297     var tree = config;
38298     var field;
38299     if (oldconfig) { // old style..
38300         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38301     } else {
38302         // new style..
38303         tree = config.tree;
38304         config.field = config.field  || {};
38305         config.field.xtype = 'TextField';
38306         field = Roo.factory(config.field, Roo.form);
38307     }
38308     config = config || {};
38309     
38310     
38311     this.addEvents({
38312         /**
38313          * @event beforenodeedit
38314          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38315          * false from the handler of this event.
38316          * @param {Editor} this
38317          * @param {Roo.tree.Node} node 
38318          */
38319         "beforenodeedit" : true
38320     });
38321     
38322     //Roo.log(config);
38323     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38324
38325     this.tree = tree;
38326
38327     tree.on('beforeclick', this.beforeNodeClick, this);
38328     tree.getTreeEl().on('mousedown', this.hide, this);
38329     this.on('complete', this.updateNode, this);
38330     this.on('beforestartedit', this.fitToTree, this);
38331     this.on('startedit', this.bindScroll, this, {delay:10});
38332     this.on('specialkey', this.onSpecialKey, this);
38333 };
38334
38335 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38336     /**
38337      * @cfg {String} alignment
38338      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38339      */
38340     alignment: "l-l",
38341     // inherit
38342     autoSize: false,
38343     /**
38344      * @cfg {Boolean} hideEl
38345      * True to hide the bound element while the editor is displayed (defaults to false)
38346      */
38347     hideEl : false,
38348     /**
38349      * @cfg {String} cls
38350      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38351      */
38352     cls: "x-small-editor x-tree-editor",
38353     /**
38354      * @cfg {Boolean} shim
38355      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38356      */
38357     shim:false,
38358     // inherit
38359     shadow:"frame",
38360     /**
38361      * @cfg {Number} maxWidth
38362      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38363      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38364      * scroll and client offsets into account prior to each edit.
38365      */
38366     maxWidth: 250,
38367
38368     editDelay : 350,
38369
38370     // private
38371     fitToTree : function(ed, el){
38372         var td = this.tree.getTreeEl().dom, nd = el.dom;
38373         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38374             td.scrollLeft = nd.offsetLeft;
38375         }
38376         var w = Math.min(
38377                 this.maxWidth,
38378                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38379         this.setSize(w, '');
38380         
38381         return this.fireEvent('beforenodeedit', this, this.editNode);
38382         
38383     },
38384
38385     // private
38386     triggerEdit : function(node){
38387         this.completeEdit();
38388         this.editNode = node;
38389         this.startEdit(node.ui.textNode, node.text);
38390     },
38391
38392     // private
38393     bindScroll : function(){
38394         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38395     },
38396
38397     // private
38398     beforeNodeClick : function(node, e){
38399         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38400         this.lastClick = new Date();
38401         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38402             e.stopEvent();
38403             this.triggerEdit(node);
38404             return false;
38405         }
38406         return true;
38407     },
38408
38409     // private
38410     updateNode : function(ed, value){
38411         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38412         this.editNode.setText(value);
38413     },
38414
38415     // private
38416     onHide : function(){
38417         Roo.tree.TreeEditor.superclass.onHide.call(this);
38418         if(this.editNode){
38419             this.editNode.ui.focus();
38420         }
38421     },
38422
38423     // private
38424     onSpecialKey : function(field, e){
38425         var k = e.getKey();
38426         if(k == e.ESC){
38427             e.stopEvent();
38428             this.cancelEdit();
38429         }else if(k == e.ENTER && !e.hasModifier()){
38430             e.stopEvent();
38431             this.completeEdit();
38432         }
38433     }
38434 });//<Script type="text/javascript">
38435 /*
38436  * Based on:
38437  * Ext JS Library 1.1.1
38438  * Copyright(c) 2006-2007, Ext JS, LLC.
38439  *
38440  * Originally Released Under LGPL - original licence link has changed is not relivant.
38441  *
38442  * Fork - LGPL
38443  * <script type="text/javascript">
38444  */
38445  
38446 /**
38447  * Not documented??? - probably should be...
38448  */
38449
38450 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38451     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38452     
38453     renderElements : function(n, a, targetNode, bulkRender){
38454         //consel.log("renderElements?");
38455         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38456
38457         var t = n.getOwnerTree();
38458         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38459         
38460         var cols = t.columns;
38461         var bw = t.borderWidth;
38462         var c = cols[0];
38463         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38464          var cb = typeof a.checked == "boolean";
38465         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38466         var colcls = 'x-t-' + tid + '-c0';
38467         var buf = [
38468             '<li class="x-tree-node">',
38469             
38470                 
38471                 '<div class="x-tree-node-el ', a.cls,'">',
38472                     // extran...
38473                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38474                 
38475                 
38476                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38477                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38478                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38479                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38480                            (a.iconCls ? ' '+a.iconCls : ''),
38481                            '" unselectable="on" />',
38482                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38483                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38484                              
38485                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38486                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38487                             '<span unselectable="on" qtip="' + tx + '">',
38488                              tx,
38489                              '</span></a>' ,
38490                     '</div>',
38491                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38492                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38493                  ];
38494         for(var i = 1, len = cols.length; i < len; i++){
38495             c = cols[i];
38496             colcls = 'x-t-' + tid + '-c' +i;
38497             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38498             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38499                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38500                       "</div>");
38501          }
38502          
38503          buf.push(
38504             '</a>',
38505             '<div class="x-clear"></div></div>',
38506             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38507             "</li>");
38508         
38509         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38510             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38511                                 n.nextSibling.ui.getEl(), buf.join(""));
38512         }else{
38513             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38514         }
38515         var el = this.wrap.firstChild;
38516         this.elRow = el;
38517         this.elNode = el.firstChild;
38518         this.ranchor = el.childNodes[1];
38519         this.ctNode = this.wrap.childNodes[1];
38520         var cs = el.firstChild.childNodes;
38521         this.indentNode = cs[0];
38522         this.ecNode = cs[1];
38523         this.iconNode = cs[2];
38524         var index = 3;
38525         if(cb){
38526             this.checkbox = cs[3];
38527             index++;
38528         }
38529         this.anchor = cs[index];
38530         
38531         this.textNode = cs[index].firstChild;
38532         
38533         //el.on("click", this.onClick, this);
38534         //el.on("dblclick", this.onDblClick, this);
38535         
38536         
38537        // console.log(this);
38538     },
38539     initEvents : function(){
38540         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38541         
38542             
38543         var a = this.ranchor;
38544
38545         var el = Roo.get(a);
38546
38547         if(Roo.isOpera){ // opera render bug ignores the CSS
38548             el.setStyle("text-decoration", "none");
38549         }
38550
38551         el.on("click", this.onClick, this);
38552         el.on("dblclick", this.onDblClick, this);
38553         el.on("contextmenu", this.onContextMenu, this);
38554         
38555     },
38556     
38557     /*onSelectedChange : function(state){
38558         if(state){
38559             this.focus();
38560             this.addClass("x-tree-selected");
38561         }else{
38562             //this.blur();
38563             this.removeClass("x-tree-selected");
38564         }
38565     },*/
38566     addClass : function(cls){
38567         if(this.elRow){
38568             Roo.fly(this.elRow).addClass(cls);
38569         }
38570         
38571     },
38572     
38573     
38574     removeClass : function(cls){
38575         if(this.elRow){
38576             Roo.fly(this.elRow).removeClass(cls);
38577         }
38578     }
38579
38580     
38581     
38582 });//<Script type="text/javascript">
38583
38584 /*
38585  * Based on:
38586  * Ext JS Library 1.1.1
38587  * Copyright(c) 2006-2007, Ext JS, LLC.
38588  *
38589  * Originally Released Under LGPL - original licence link has changed is not relivant.
38590  *
38591  * Fork - LGPL
38592  * <script type="text/javascript">
38593  */
38594  
38595
38596 /**
38597  * @class Roo.tree.ColumnTree
38598  * @extends Roo.tree.TreePanel
38599  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38600  * @cfg {int} borderWidth  compined right/left border allowance
38601  * @constructor
38602  * @param {String/HTMLElement/Element} el The container element
38603  * @param {Object} config
38604  */
38605 Roo.tree.ColumnTree =  function(el, config)
38606 {
38607    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38608    this.addEvents({
38609         /**
38610         * @event resize
38611         * Fire this event on a container when it resizes
38612         * @param {int} w Width
38613         * @param {int} h Height
38614         */
38615        "resize" : true
38616     });
38617     this.on('resize', this.onResize, this);
38618 };
38619
38620 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38621     //lines:false,
38622     
38623     
38624     borderWidth: Roo.isBorderBox ? 0 : 2, 
38625     headEls : false,
38626     
38627     render : function(){
38628         // add the header.....
38629        
38630         Roo.tree.ColumnTree.superclass.render.apply(this);
38631         
38632         this.el.addClass('x-column-tree');
38633         
38634         this.headers = this.el.createChild(
38635             {cls:'x-tree-headers'},this.innerCt.dom);
38636    
38637         var cols = this.columns, c;
38638         var totalWidth = 0;
38639         this.headEls = [];
38640         var  len = cols.length;
38641         for(var i = 0; i < len; i++){
38642              c = cols[i];
38643              totalWidth += c.width;
38644             this.headEls.push(this.headers.createChild({
38645                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38646                  cn: {
38647                      cls:'x-tree-hd-text',
38648                      html: c.header
38649                  },
38650                  style:'width:'+(c.width-this.borderWidth)+'px;'
38651              }));
38652         }
38653         this.headers.createChild({cls:'x-clear'});
38654         // prevent floats from wrapping when clipped
38655         this.headers.setWidth(totalWidth);
38656         //this.innerCt.setWidth(totalWidth);
38657         this.innerCt.setStyle({ overflow: 'auto' });
38658         this.onResize(this.width, this.height);
38659              
38660         
38661     },
38662     onResize : function(w,h)
38663     {
38664         this.height = h;
38665         this.width = w;
38666         // resize cols..
38667         this.innerCt.setWidth(this.width);
38668         this.innerCt.setHeight(this.height-20);
38669         
38670         // headers...
38671         var cols = this.columns, c;
38672         var totalWidth = 0;
38673         var expEl = false;
38674         var len = cols.length;
38675         for(var i = 0; i < len; i++){
38676             c = cols[i];
38677             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38678                 // it's the expander..
38679                 expEl  = this.headEls[i];
38680                 continue;
38681             }
38682             totalWidth += c.width;
38683             
38684         }
38685         if (expEl) {
38686             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38687         }
38688         this.headers.setWidth(w-20);
38689
38690         
38691         
38692         
38693     }
38694 });
38695 /*
38696  * Based on:
38697  * Ext JS Library 1.1.1
38698  * Copyright(c) 2006-2007, Ext JS, LLC.
38699  *
38700  * Originally Released Under LGPL - original licence link has changed is not relivant.
38701  *
38702  * Fork - LGPL
38703  * <script type="text/javascript">
38704  */
38705  
38706 /**
38707  * @class Roo.menu.Menu
38708  * @extends Roo.util.Observable
38709  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38710  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38711  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38712  * @constructor
38713  * Creates a new Menu
38714  * @param {Object} config Configuration options
38715  */
38716 Roo.menu.Menu = function(config){
38717     
38718     Roo.menu.Menu.superclass.constructor.call(this, config);
38719     
38720     this.id = this.id || Roo.id();
38721     this.addEvents({
38722         /**
38723          * @event beforeshow
38724          * Fires before this menu is displayed
38725          * @param {Roo.menu.Menu} this
38726          */
38727         beforeshow : true,
38728         /**
38729          * @event beforehide
38730          * Fires before this menu is hidden
38731          * @param {Roo.menu.Menu} this
38732          */
38733         beforehide : true,
38734         /**
38735          * @event show
38736          * Fires after this menu is displayed
38737          * @param {Roo.menu.Menu} this
38738          */
38739         show : true,
38740         /**
38741          * @event hide
38742          * Fires after this menu is hidden
38743          * @param {Roo.menu.Menu} this
38744          */
38745         hide : true,
38746         /**
38747          * @event click
38748          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38749          * @param {Roo.menu.Menu} this
38750          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38751          * @param {Roo.EventObject} e
38752          */
38753         click : true,
38754         /**
38755          * @event mouseover
38756          * Fires when the mouse is hovering over this menu
38757          * @param {Roo.menu.Menu} this
38758          * @param {Roo.EventObject} e
38759          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38760          */
38761         mouseover : true,
38762         /**
38763          * @event mouseout
38764          * Fires when the mouse exits this menu
38765          * @param {Roo.menu.Menu} this
38766          * @param {Roo.EventObject} e
38767          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38768          */
38769         mouseout : true,
38770         /**
38771          * @event itemclick
38772          * Fires when a menu item contained in this menu is clicked
38773          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38774          * @param {Roo.EventObject} e
38775          */
38776         itemclick: true
38777     });
38778     if (this.registerMenu) {
38779         Roo.menu.MenuMgr.register(this);
38780     }
38781     
38782     var mis = this.items;
38783     this.items = new Roo.util.MixedCollection();
38784     if(mis){
38785         this.add.apply(this, mis);
38786     }
38787 };
38788
38789 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38790     /**
38791      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38792      */
38793     minWidth : 120,
38794     /**
38795      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38796      * for bottom-right shadow (defaults to "sides")
38797      */
38798     shadow : "sides",
38799     /**
38800      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38801      * this menu (defaults to "tl-tr?")
38802      */
38803     subMenuAlign : "tl-tr?",
38804     /**
38805      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38806      * relative to its element of origin (defaults to "tl-bl?")
38807      */
38808     defaultAlign : "tl-bl?",
38809     /**
38810      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38811      */
38812     allowOtherMenus : false,
38813     /**
38814      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38815      */
38816     registerMenu : true,
38817
38818     hidden:true,
38819
38820     // private
38821     render : function(){
38822         if(this.el){
38823             return;
38824         }
38825         var el = this.el = new Roo.Layer({
38826             cls: "x-menu",
38827             shadow:this.shadow,
38828             constrain: false,
38829             parentEl: this.parentEl || document.body,
38830             zindex:15000
38831         });
38832
38833         this.keyNav = new Roo.menu.MenuNav(this);
38834
38835         if(this.plain){
38836             el.addClass("x-menu-plain");
38837         }
38838         if(this.cls){
38839             el.addClass(this.cls);
38840         }
38841         // generic focus element
38842         this.focusEl = el.createChild({
38843             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38844         });
38845         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38846         //disabling touch- as it's causing issues ..
38847         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38848         ul.on('click'   , this.onClick, this);
38849         
38850         
38851         ul.on("mouseover", this.onMouseOver, this);
38852         ul.on("mouseout", this.onMouseOut, this);
38853         this.items.each(function(item){
38854             if (item.hidden) {
38855                 return;
38856             }
38857             
38858             var li = document.createElement("li");
38859             li.className = "x-menu-list-item";
38860             ul.dom.appendChild(li);
38861             item.render(li, this);
38862         }, this);
38863         this.ul = ul;
38864         this.autoWidth();
38865     },
38866
38867     // private
38868     autoWidth : function(){
38869         var el = this.el, ul = this.ul;
38870         if(!el){
38871             return;
38872         }
38873         var w = this.width;
38874         if(w){
38875             el.setWidth(w);
38876         }else if(Roo.isIE){
38877             el.setWidth(this.minWidth);
38878             var t = el.dom.offsetWidth; // force recalc
38879             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38880         }
38881     },
38882
38883     // private
38884     delayAutoWidth : function(){
38885         if(this.rendered){
38886             if(!this.awTask){
38887                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38888             }
38889             this.awTask.delay(20);
38890         }
38891     },
38892
38893     // private
38894     findTargetItem : function(e){
38895         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38896         if(t && t.menuItemId){
38897             return this.items.get(t.menuItemId);
38898         }
38899     },
38900
38901     // private
38902     onClick : function(e){
38903         Roo.log("menu.onClick");
38904         var t = this.findTargetItem(e);
38905         if(!t){
38906             return;
38907         }
38908         Roo.log(e);
38909         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38910             if(t == this.activeItem && t.shouldDeactivate(e)){
38911                 this.activeItem.deactivate();
38912                 delete this.activeItem;
38913                 return;
38914             }
38915             if(t.canActivate){
38916                 this.setActiveItem(t, true);
38917             }
38918             return;
38919             
38920             
38921         }
38922         
38923         t.onClick(e);
38924         this.fireEvent("click", this, t, e);
38925     },
38926
38927     // private
38928     setActiveItem : function(item, autoExpand){
38929         if(item != this.activeItem){
38930             if(this.activeItem){
38931                 this.activeItem.deactivate();
38932             }
38933             this.activeItem = item;
38934             item.activate(autoExpand);
38935         }else if(autoExpand){
38936             item.expandMenu();
38937         }
38938     },
38939
38940     // private
38941     tryActivate : function(start, step){
38942         var items = this.items;
38943         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38944             var item = items.get(i);
38945             if(!item.disabled && item.canActivate){
38946                 this.setActiveItem(item, false);
38947                 return item;
38948             }
38949         }
38950         return false;
38951     },
38952
38953     // private
38954     onMouseOver : function(e){
38955         var t;
38956         if(t = this.findTargetItem(e)){
38957             if(t.canActivate && !t.disabled){
38958                 this.setActiveItem(t, true);
38959             }
38960         }
38961         this.fireEvent("mouseover", this, e, t);
38962     },
38963
38964     // private
38965     onMouseOut : function(e){
38966         var t;
38967         if(t = this.findTargetItem(e)){
38968             if(t == this.activeItem && t.shouldDeactivate(e)){
38969                 this.activeItem.deactivate();
38970                 delete this.activeItem;
38971             }
38972         }
38973         this.fireEvent("mouseout", this, e, t);
38974     },
38975
38976     /**
38977      * Read-only.  Returns true if the menu is currently displayed, else false.
38978      * @type Boolean
38979      */
38980     isVisible : function(){
38981         return this.el && !this.hidden;
38982     },
38983
38984     /**
38985      * Displays this menu relative to another element
38986      * @param {String/HTMLElement/Roo.Element} element The element to align to
38987      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38988      * the element (defaults to this.defaultAlign)
38989      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38990      */
38991     show : function(el, pos, parentMenu){
38992         this.parentMenu = parentMenu;
38993         if(!this.el){
38994             this.render();
38995         }
38996         this.fireEvent("beforeshow", this);
38997         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38998     },
38999
39000     /**
39001      * Displays this menu at a specific xy position
39002      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39003      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39004      */
39005     showAt : function(xy, parentMenu, /* private: */_e){
39006         this.parentMenu = parentMenu;
39007         if(!this.el){
39008             this.render();
39009         }
39010         if(_e !== false){
39011             this.fireEvent("beforeshow", this);
39012             xy = this.el.adjustForConstraints(xy);
39013         }
39014         this.el.setXY(xy);
39015         this.el.show();
39016         this.hidden = false;
39017         this.focus();
39018         this.fireEvent("show", this);
39019     },
39020
39021     focus : function(){
39022         if(!this.hidden){
39023             this.doFocus.defer(50, this);
39024         }
39025     },
39026
39027     doFocus : function(){
39028         if(!this.hidden){
39029             this.focusEl.focus();
39030         }
39031     },
39032
39033     /**
39034      * Hides this menu and optionally all parent menus
39035      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39036      */
39037     hide : function(deep){
39038         if(this.el && this.isVisible()){
39039             this.fireEvent("beforehide", this);
39040             if(this.activeItem){
39041                 this.activeItem.deactivate();
39042                 this.activeItem = null;
39043             }
39044             this.el.hide();
39045             this.hidden = true;
39046             this.fireEvent("hide", this);
39047         }
39048         if(deep === true && this.parentMenu){
39049             this.parentMenu.hide(true);
39050         }
39051     },
39052
39053     /**
39054      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39055      * Any of the following are valid:
39056      * <ul>
39057      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39058      * <li>An HTMLElement object which will be converted to a menu item</li>
39059      * <li>A menu item config object that will be created as a new menu item</li>
39060      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39061      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39062      * </ul>
39063      * Usage:
39064      * <pre><code>
39065 // Create the menu
39066 var menu = new Roo.menu.Menu();
39067
39068 // Create a menu item to add by reference
39069 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39070
39071 // Add a bunch of items at once using different methods.
39072 // Only the last item added will be returned.
39073 var item = menu.add(
39074     menuItem,                // add existing item by ref
39075     'Dynamic Item',          // new TextItem
39076     '-',                     // new separator
39077     { text: 'Config Item' }  // new item by config
39078 );
39079 </code></pre>
39080      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39081      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39082      */
39083     add : function(){
39084         var a = arguments, l = a.length, item;
39085         for(var i = 0; i < l; i++){
39086             var el = a[i];
39087             if ((typeof(el) == "object") && el.xtype && el.xns) {
39088                 el = Roo.factory(el, Roo.menu);
39089             }
39090             
39091             if(el.render){ // some kind of Item
39092                 item = this.addItem(el);
39093             }else if(typeof el == "string"){ // string
39094                 if(el == "separator" || el == "-"){
39095                     item = this.addSeparator();
39096                 }else{
39097                     item = this.addText(el);
39098                 }
39099             }else if(el.tagName || el.el){ // element
39100                 item = this.addElement(el);
39101             }else if(typeof el == "object"){ // must be menu item config?
39102                 item = this.addMenuItem(el);
39103             }
39104         }
39105         return item;
39106     },
39107
39108     /**
39109      * Returns this menu's underlying {@link Roo.Element} object
39110      * @return {Roo.Element} The element
39111      */
39112     getEl : function(){
39113         if(!this.el){
39114             this.render();
39115         }
39116         return this.el;
39117     },
39118
39119     /**
39120      * Adds a separator bar to the menu
39121      * @return {Roo.menu.Item} The menu item that was added
39122      */
39123     addSeparator : function(){
39124         return this.addItem(new Roo.menu.Separator());
39125     },
39126
39127     /**
39128      * Adds an {@link Roo.Element} object to the menu
39129      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39130      * @return {Roo.menu.Item} The menu item that was added
39131      */
39132     addElement : function(el){
39133         return this.addItem(new Roo.menu.BaseItem(el));
39134     },
39135
39136     /**
39137      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39138      * @param {Roo.menu.Item} item The menu item to add
39139      * @return {Roo.menu.Item} The menu item that was added
39140      */
39141     addItem : function(item){
39142         this.items.add(item);
39143         if(this.ul){
39144             var li = document.createElement("li");
39145             li.className = "x-menu-list-item";
39146             this.ul.dom.appendChild(li);
39147             item.render(li, this);
39148             this.delayAutoWidth();
39149         }
39150         return item;
39151     },
39152
39153     /**
39154      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39155      * @param {Object} config A MenuItem config object
39156      * @return {Roo.menu.Item} The menu item that was added
39157      */
39158     addMenuItem : function(config){
39159         if(!(config instanceof Roo.menu.Item)){
39160             if(typeof config.checked == "boolean"){ // must be check menu item config?
39161                 config = new Roo.menu.CheckItem(config);
39162             }else{
39163                 config = new Roo.menu.Item(config);
39164             }
39165         }
39166         return this.addItem(config);
39167     },
39168
39169     /**
39170      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39171      * @param {String} text The text to display in the menu item
39172      * @return {Roo.menu.Item} The menu item that was added
39173      */
39174     addText : function(text){
39175         return this.addItem(new Roo.menu.TextItem({ text : text }));
39176     },
39177
39178     /**
39179      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39180      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39181      * @param {Roo.menu.Item} item The menu item to add
39182      * @return {Roo.menu.Item} The menu item that was added
39183      */
39184     insert : function(index, item){
39185         this.items.insert(index, item);
39186         if(this.ul){
39187             var li = document.createElement("li");
39188             li.className = "x-menu-list-item";
39189             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39190             item.render(li, this);
39191             this.delayAutoWidth();
39192         }
39193         return item;
39194     },
39195
39196     /**
39197      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39198      * @param {Roo.menu.Item} item The menu item to remove
39199      */
39200     remove : function(item){
39201         this.items.removeKey(item.id);
39202         item.destroy();
39203     },
39204
39205     /**
39206      * Removes and destroys all items in the menu
39207      */
39208     removeAll : function(){
39209         var f;
39210         while(f = this.items.first()){
39211             this.remove(f);
39212         }
39213     }
39214 });
39215
39216 // MenuNav is a private utility class used internally by the Menu
39217 Roo.menu.MenuNav = function(menu){
39218     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39219     this.scope = this.menu = menu;
39220 };
39221
39222 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39223     doRelay : function(e, h){
39224         var k = e.getKey();
39225         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39226             this.menu.tryActivate(0, 1);
39227             return false;
39228         }
39229         return h.call(this.scope || this, e, this.menu);
39230     },
39231
39232     up : function(e, m){
39233         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39234             m.tryActivate(m.items.length-1, -1);
39235         }
39236     },
39237
39238     down : function(e, m){
39239         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39240             m.tryActivate(0, 1);
39241         }
39242     },
39243
39244     right : function(e, m){
39245         if(m.activeItem){
39246             m.activeItem.expandMenu(true);
39247         }
39248     },
39249
39250     left : function(e, m){
39251         m.hide();
39252         if(m.parentMenu && m.parentMenu.activeItem){
39253             m.parentMenu.activeItem.activate();
39254         }
39255     },
39256
39257     enter : function(e, m){
39258         if(m.activeItem){
39259             e.stopPropagation();
39260             m.activeItem.onClick(e);
39261             m.fireEvent("click", this, m.activeItem);
39262             return true;
39263         }
39264     }
39265 });/*
39266  * Based on:
39267  * Ext JS Library 1.1.1
39268  * Copyright(c) 2006-2007, Ext JS, LLC.
39269  *
39270  * Originally Released Under LGPL - original licence link has changed is not relivant.
39271  *
39272  * Fork - LGPL
39273  * <script type="text/javascript">
39274  */
39275  
39276 /**
39277  * @class Roo.menu.MenuMgr
39278  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39279  * @static
39280  */
39281 Roo.menu.MenuMgr = function(){
39282    var menus, active, groups = {}, attached = false, lastShow = new Date();
39283
39284    // private - called when first menu is created
39285    function init(){
39286        menus = {};
39287        active = new Roo.util.MixedCollection();
39288        Roo.get(document).addKeyListener(27, function(){
39289            if(active.length > 0){
39290                hideAll();
39291            }
39292        });
39293    }
39294
39295    // private
39296    function hideAll(){
39297        if(active && active.length > 0){
39298            var c = active.clone();
39299            c.each(function(m){
39300                m.hide();
39301            });
39302        }
39303    }
39304
39305    // private
39306    function onHide(m){
39307        active.remove(m);
39308        if(active.length < 1){
39309            Roo.get(document).un("mousedown", onMouseDown);
39310            attached = false;
39311        }
39312    }
39313
39314    // private
39315    function onShow(m){
39316        var last = active.last();
39317        lastShow = new Date();
39318        active.add(m);
39319        if(!attached){
39320            Roo.get(document).on("mousedown", onMouseDown);
39321            attached = true;
39322        }
39323        if(m.parentMenu){
39324           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39325           m.parentMenu.activeChild = m;
39326        }else if(last && last.isVisible()){
39327           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39328        }
39329    }
39330
39331    // private
39332    function onBeforeHide(m){
39333        if(m.activeChild){
39334            m.activeChild.hide();
39335        }
39336        if(m.autoHideTimer){
39337            clearTimeout(m.autoHideTimer);
39338            delete m.autoHideTimer;
39339        }
39340    }
39341
39342    // private
39343    function onBeforeShow(m){
39344        var pm = m.parentMenu;
39345        if(!pm && !m.allowOtherMenus){
39346            hideAll();
39347        }else if(pm && pm.activeChild && active != m){
39348            pm.activeChild.hide();
39349        }
39350    }
39351
39352    // private
39353    function onMouseDown(e){
39354        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39355            hideAll();
39356        }
39357    }
39358
39359    // private
39360    function onBeforeCheck(mi, state){
39361        if(state){
39362            var g = groups[mi.group];
39363            for(var i = 0, l = g.length; i < l; i++){
39364                if(g[i] != mi){
39365                    g[i].setChecked(false);
39366                }
39367            }
39368        }
39369    }
39370
39371    return {
39372
39373        /**
39374         * Hides all menus that are currently visible
39375         */
39376        hideAll : function(){
39377             hideAll();  
39378        },
39379
39380        // private
39381        register : function(menu){
39382            if(!menus){
39383                init();
39384            }
39385            menus[menu.id] = menu;
39386            menu.on("beforehide", onBeforeHide);
39387            menu.on("hide", onHide);
39388            menu.on("beforeshow", onBeforeShow);
39389            menu.on("show", onShow);
39390            var g = menu.group;
39391            if(g && menu.events["checkchange"]){
39392                if(!groups[g]){
39393                    groups[g] = [];
39394                }
39395                groups[g].push(menu);
39396                menu.on("checkchange", onCheck);
39397            }
39398        },
39399
39400         /**
39401          * Returns a {@link Roo.menu.Menu} object
39402          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39403          * be used to generate and return a new Menu instance.
39404          */
39405        get : function(menu){
39406            if(typeof menu == "string"){ // menu id
39407                return menus[menu];
39408            }else if(menu.events){  // menu instance
39409                return menu;
39410            }else if(typeof menu.length == 'number'){ // array of menu items?
39411                return new Roo.menu.Menu({items:menu});
39412            }else{ // otherwise, must be a config
39413                return new Roo.menu.Menu(menu);
39414            }
39415        },
39416
39417        // private
39418        unregister : function(menu){
39419            delete menus[menu.id];
39420            menu.un("beforehide", onBeforeHide);
39421            menu.un("hide", onHide);
39422            menu.un("beforeshow", onBeforeShow);
39423            menu.un("show", onShow);
39424            var g = menu.group;
39425            if(g && menu.events["checkchange"]){
39426                groups[g].remove(menu);
39427                menu.un("checkchange", onCheck);
39428            }
39429        },
39430
39431        // private
39432        registerCheckable : function(menuItem){
39433            var g = menuItem.group;
39434            if(g){
39435                if(!groups[g]){
39436                    groups[g] = [];
39437                }
39438                groups[g].push(menuItem);
39439                menuItem.on("beforecheckchange", onBeforeCheck);
39440            }
39441        },
39442
39443        // private
39444        unregisterCheckable : function(menuItem){
39445            var g = menuItem.group;
39446            if(g){
39447                groups[g].remove(menuItem);
39448                menuItem.un("beforecheckchange", onBeforeCheck);
39449            }
39450        }
39451    };
39452 }();/*
39453  * Based on:
39454  * Ext JS Library 1.1.1
39455  * Copyright(c) 2006-2007, Ext JS, LLC.
39456  *
39457  * Originally Released Under LGPL - original licence link has changed is not relivant.
39458  *
39459  * Fork - LGPL
39460  * <script type="text/javascript">
39461  */
39462  
39463
39464 /**
39465  * @class Roo.menu.BaseItem
39466  * @extends Roo.Component
39467  * @abstract
39468  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39469  * management and base configuration options shared by all menu components.
39470  * @constructor
39471  * Creates a new BaseItem
39472  * @param {Object} config Configuration options
39473  */
39474 Roo.menu.BaseItem = function(config){
39475     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39476
39477     this.addEvents({
39478         /**
39479          * @event click
39480          * Fires when this item is clicked
39481          * @param {Roo.menu.BaseItem} this
39482          * @param {Roo.EventObject} e
39483          */
39484         click: true,
39485         /**
39486          * @event activate
39487          * Fires when this item is activated
39488          * @param {Roo.menu.BaseItem} this
39489          */
39490         activate : true,
39491         /**
39492          * @event deactivate
39493          * Fires when this item is deactivated
39494          * @param {Roo.menu.BaseItem} this
39495          */
39496         deactivate : true
39497     });
39498
39499     if(this.handler){
39500         this.on("click", this.handler, this.scope, true);
39501     }
39502 };
39503
39504 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39505     /**
39506      * @cfg {Function} handler
39507      * A function that will handle the click event of this menu item (defaults to undefined)
39508      */
39509     /**
39510      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39511      */
39512     canActivate : false,
39513     
39514      /**
39515      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39516      */
39517     hidden: false,
39518     
39519     /**
39520      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39521      */
39522     activeClass : "x-menu-item-active",
39523     /**
39524      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39525      */
39526     hideOnClick : true,
39527     /**
39528      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39529      */
39530     hideDelay : 100,
39531
39532     // private
39533     ctype: "Roo.menu.BaseItem",
39534
39535     // private
39536     actionMode : "container",
39537
39538     // private
39539     render : function(container, parentMenu){
39540         this.parentMenu = parentMenu;
39541         Roo.menu.BaseItem.superclass.render.call(this, container);
39542         this.container.menuItemId = this.id;
39543     },
39544
39545     // private
39546     onRender : function(container, position){
39547         this.el = Roo.get(this.el);
39548         container.dom.appendChild(this.el.dom);
39549     },
39550
39551     // private
39552     onClick : function(e){
39553         if(!this.disabled && this.fireEvent("click", this, e) !== false
39554                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39555             this.handleClick(e);
39556         }else{
39557             e.stopEvent();
39558         }
39559     },
39560
39561     // private
39562     activate : function(){
39563         if(this.disabled){
39564             return false;
39565         }
39566         var li = this.container;
39567         li.addClass(this.activeClass);
39568         this.region = li.getRegion().adjust(2, 2, -2, -2);
39569         this.fireEvent("activate", this);
39570         return true;
39571     },
39572
39573     // private
39574     deactivate : function(){
39575         this.container.removeClass(this.activeClass);
39576         this.fireEvent("deactivate", this);
39577     },
39578
39579     // private
39580     shouldDeactivate : function(e){
39581         return !this.region || !this.region.contains(e.getPoint());
39582     },
39583
39584     // private
39585     handleClick : function(e){
39586         if(this.hideOnClick){
39587             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39588         }
39589     },
39590
39591     // private
39592     expandMenu : function(autoActivate){
39593         // do nothing
39594     },
39595
39596     // private
39597     hideMenu : function(){
39598         // do nothing
39599     }
39600 });/*
39601  * Based on:
39602  * Ext JS Library 1.1.1
39603  * Copyright(c) 2006-2007, Ext JS, LLC.
39604  *
39605  * Originally Released Under LGPL - original licence link has changed is not relivant.
39606  *
39607  * Fork - LGPL
39608  * <script type="text/javascript">
39609  */
39610  
39611 /**
39612  * @class Roo.menu.Adapter
39613  * @extends Roo.menu.BaseItem
39614  * @abstract
39615  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
39616  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39617  * @constructor
39618  * Creates a new Adapter
39619  * @param {Object} config Configuration options
39620  */
39621 Roo.menu.Adapter = function(component, config){
39622     Roo.menu.Adapter.superclass.constructor.call(this, config);
39623     this.component = component;
39624 };
39625 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39626     // private
39627     canActivate : true,
39628
39629     // private
39630     onRender : function(container, position){
39631         this.component.render(container);
39632         this.el = this.component.getEl();
39633     },
39634
39635     // private
39636     activate : function(){
39637         if(this.disabled){
39638             return false;
39639         }
39640         this.component.focus();
39641         this.fireEvent("activate", this);
39642         return true;
39643     },
39644
39645     // private
39646     deactivate : function(){
39647         this.fireEvent("deactivate", this);
39648     },
39649
39650     // private
39651     disable : function(){
39652         this.component.disable();
39653         Roo.menu.Adapter.superclass.disable.call(this);
39654     },
39655
39656     // private
39657     enable : function(){
39658         this.component.enable();
39659         Roo.menu.Adapter.superclass.enable.call(this);
39660     }
39661 });/*
39662  * Based on:
39663  * Ext JS Library 1.1.1
39664  * Copyright(c) 2006-2007, Ext JS, LLC.
39665  *
39666  * Originally Released Under LGPL - original licence link has changed is not relivant.
39667  *
39668  * Fork - LGPL
39669  * <script type="text/javascript">
39670  */
39671
39672 /**
39673  * @class Roo.menu.TextItem
39674  * @extends Roo.menu.BaseItem
39675  * Adds a static text string to a menu, usually used as either a heading or group separator.
39676  * Note: old style constructor with text is still supported.
39677  * 
39678  * @constructor
39679  * Creates a new TextItem
39680  * @param {Object} cfg Configuration
39681  */
39682 Roo.menu.TextItem = function(cfg){
39683     if (typeof(cfg) == 'string') {
39684         this.text = cfg;
39685     } else {
39686         Roo.apply(this,cfg);
39687     }
39688     
39689     Roo.menu.TextItem.superclass.constructor.call(this);
39690 };
39691
39692 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39693     /**
39694      * @cfg {String} text Text to show on item.
39695      */
39696     text : '',
39697     
39698     /**
39699      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39700      */
39701     hideOnClick : false,
39702     /**
39703      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39704      */
39705     itemCls : "x-menu-text",
39706
39707     // private
39708     onRender : function(){
39709         var s = document.createElement("span");
39710         s.className = this.itemCls;
39711         s.innerHTML = this.text;
39712         this.el = s;
39713         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39714     }
39715 });/*
39716  * Based on:
39717  * Ext JS Library 1.1.1
39718  * Copyright(c) 2006-2007, Ext JS, LLC.
39719  *
39720  * Originally Released Under LGPL - original licence link has changed is not relivant.
39721  *
39722  * Fork - LGPL
39723  * <script type="text/javascript">
39724  */
39725
39726 /**
39727  * @class Roo.menu.Separator
39728  * @extends Roo.menu.BaseItem
39729  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39730  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39731  * @constructor
39732  * @param {Object} config Configuration options
39733  */
39734 Roo.menu.Separator = function(config){
39735     Roo.menu.Separator.superclass.constructor.call(this, config);
39736 };
39737
39738 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39739     /**
39740      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39741      */
39742     itemCls : "x-menu-sep",
39743     /**
39744      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39745      */
39746     hideOnClick : false,
39747
39748     // private
39749     onRender : function(li){
39750         var s = document.createElement("span");
39751         s.className = this.itemCls;
39752         s.innerHTML = "&#160;";
39753         this.el = s;
39754         li.addClass("x-menu-sep-li");
39755         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39756     }
39757 });/*
39758  * Based on:
39759  * Ext JS Library 1.1.1
39760  * Copyright(c) 2006-2007, Ext JS, LLC.
39761  *
39762  * Originally Released Under LGPL - original licence link has changed is not relivant.
39763  *
39764  * Fork - LGPL
39765  * <script type="text/javascript">
39766  */
39767 /**
39768  * @class Roo.menu.Item
39769  * @extends Roo.menu.BaseItem
39770  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39771  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39772  * activation and click handling.
39773  * @constructor
39774  * Creates a new Item
39775  * @param {Object} config Configuration options
39776  */
39777 Roo.menu.Item = function(config){
39778     Roo.menu.Item.superclass.constructor.call(this, config);
39779     if(this.menu){
39780         this.menu = Roo.menu.MenuMgr.get(this.menu);
39781     }
39782 };
39783 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39784     /**
39785      * @cfg {Roo.menu.Menu} menu
39786      * A Sub menu
39787      */
39788     /**
39789      * @cfg {String} text
39790      * The text to show on the menu item.
39791      */
39792     text: '',
39793      /**
39794      * @cfg {String} HTML to render in menu
39795      * The text to show on the menu item (HTML version).
39796      */
39797     html: '',
39798     /**
39799      * @cfg {String} icon
39800      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39801      */
39802     icon: undefined,
39803     /**
39804      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39805      */
39806     itemCls : "x-menu-item",
39807     /**
39808      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39809      */
39810     canActivate : true,
39811     /**
39812      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39813      */
39814     showDelay: 200,
39815     // doc'd in BaseItem
39816     hideDelay: 200,
39817
39818     // private
39819     ctype: "Roo.menu.Item",
39820     
39821     // private
39822     onRender : function(container, position){
39823         var el = document.createElement("a");
39824         el.hideFocus = true;
39825         el.unselectable = "on";
39826         el.href = this.href || "#";
39827         if(this.hrefTarget){
39828             el.target = this.hrefTarget;
39829         }
39830         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39831         
39832         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39833         
39834         el.innerHTML = String.format(
39835                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39836                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39837         this.el = el;
39838         Roo.menu.Item.superclass.onRender.call(this, container, position);
39839     },
39840
39841     /**
39842      * Sets the text to display in this menu item
39843      * @param {String} text The text to display
39844      * @param {Boolean} isHTML true to indicate text is pure html.
39845      */
39846     setText : function(text, isHTML){
39847         if (isHTML) {
39848             this.html = text;
39849         } else {
39850             this.text = text;
39851             this.html = '';
39852         }
39853         if(this.rendered){
39854             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39855      
39856             this.el.update(String.format(
39857                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39858                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39859             this.parentMenu.autoWidth();
39860         }
39861     },
39862
39863     // private
39864     handleClick : function(e){
39865         if(!this.href){ // if no link defined, stop the event automatically
39866             e.stopEvent();
39867         }
39868         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39869     },
39870
39871     // private
39872     activate : function(autoExpand){
39873         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39874             this.focus();
39875             if(autoExpand){
39876                 this.expandMenu();
39877             }
39878         }
39879         return true;
39880     },
39881
39882     // private
39883     shouldDeactivate : function(e){
39884         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39885             if(this.menu && this.menu.isVisible()){
39886                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39887             }
39888             return true;
39889         }
39890         return false;
39891     },
39892
39893     // private
39894     deactivate : function(){
39895         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39896         this.hideMenu();
39897     },
39898
39899     // private
39900     expandMenu : function(autoActivate){
39901         if(!this.disabled && this.menu){
39902             clearTimeout(this.hideTimer);
39903             delete this.hideTimer;
39904             if(!this.menu.isVisible() && !this.showTimer){
39905                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39906             }else if (this.menu.isVisible() && autoActivate){
39907                 this.menu.tryActivate(0, 1);
39908             }
39909         }
39910     },
39911
39912     // private
39913     deferExpand : function(autoActivate){
39914         delete this.showTimer;
39915         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39916         if(autoActivate){
39917             this.menu.tryActivate(0, 1);
39918         }
39919     },
39920
39921     // private
39922     hideMenu : function(){
39923         clearTimeout(this.showTimer);
39924         delete this.showTimer;
39925         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39926             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39927         }
39928     },
39929
39930     // private
39931     deferHide : function(){
39932         delete this.hideTimer;
39933         this.menu.hide();
39934     }
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.menu.CheckItem
39948  * @extends Roo.menu.Item
39949  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39950  * @constructor
39951  * Creates a new CheckItem
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.menu.CheckItem = function(config){
39955     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39956     this.addEvents({
39957         /**
39958          * @event beforecheckchange
39959          * Fires before the checked value is set, providing an opportunity to cancel if needed
39960          * @param {Roo.menu.CheckItem} this
39961          * @param {Boolean} checked The new checked value that will be set
39962          */
39963         "beforecheckchange" : true,
39964         /**
39965          * @event checkchange
39966          * Fires after the checked value has been set
39967          * @param {Roo.menu.CheckItem} this
39968          * @param {Boolean} checked The checked value that was set
39969          */
39970         "checkchange" : true
39971     });
39972     if(this.checkHandler){
39973         this.on('checkchange', this.checkHandler, this.scope);
39974     }
39975 };
39976 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39977     /**
39978      * @cfg {String} group
39979      * All check items with the same group name will automatically be grouped into a single-select
39980      * radio button group (defaults to '')
39981      */
39982     /**
39983      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39984      */
39985     itemCls : "x-menu-item x-menu-check-item",
39986     /**
39987      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39988      */
39989     groupClass : "x-menu-group-item",
39990
39991     /**
39992      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39993      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39994      * initialized with checked = true will be rendered as checked.
39995      */
39996     checked: false,
39997
39998     // private
39999     ctype: "Roo.menu.CheckItem",
40000
40001     // private
40002     onRender : function(c){
40003         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40004         if(this.group){
40005             this.el.addClass(this.groupClass);
40006         }
40007         Roo.menu.MenuMgr.registerCheckable(this);
40008         if(this.checked){
40009             this.checked = false;
40010             this.setChecked(true, true);
40011         }
40012     },
40013
40014     // private
40015     destroy : function(){
40016         if(this.rendered){
40017             Roo.menu.MenuMgr.unregisterCheckable(this);
40018         }
40019         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40020     },
40021
40022     /**
40023      * Set the checked state of this item
40024      * @param {Boolean} checked The new checked value
40025      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40026      */
40027     setChecked : function(state, suppressEvent){
40028         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40029             if(this.container){
40030                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40031             }
40032             this.checked = state;
40033             if(suppressEvent !== true){
40034                 this.fireEvent("checkchange", this, state);
40035             }
40036         }
40037     },
40038
40039     // private
40040     handleClick : function(e){
40041        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40042            this.setChecked(!this.checked);
40043        }
40044        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40045     }
40046 });/*
40047  * Based on:
40048  * Ext JS Library 1.1.1
40049  * Copyright(c) 2006-2007, Ext JS, LLC.
40050  *
40051  * Originally Released Under LGPL - original licence link has changed is not relivant.
40052  *
40053  * Fork - LGPL
40054  * <script type="text/javascript">
40055  */
40056  
40057 /**
40058  * @class Roo.menu.DateItem
40059  * @extends Roo.menu.Adapter
40060  * A menu item that wraps the {@link Roo.DatPicker} component.
40061  * @constructor
40062  * Creates a new DateItem
40063  * @param {Object} config Configuration options
40064  */
40065 Roo.menu.DateItem = function(config){
40066     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40067     /** The Roo.DatePicker object @type Roo.DatePicker */
40068     this.picker = this.component;
40069     this.addEvents({select: true});
40070     
40071     this.picker.on("render", function(picker){
40072         picker.getEl().swallowEvent("click");
40073         picker.container.addClass("x-menu-date-item");
40074     });
40075
40076     this.picker.on("select", this.onSelect, this);
40077 };
40078
40079 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40080     // private
40081     onSelect : function(picker, date){
40082         this.fireEvent("select", this, date, picker);
40083         Roo.menu.DateItem.superclass.handleClick.call(this);
40084     }
40085 });/*
40086  * Based on:
40087  * Ext JS Library 1.1.1
40088  * Copyright(c) 2006-2007, Ext JS, LLC.
40089  *
40090  * Originally Released Under LGPL - original licence link has changed is not relivant.
40091  *
40092  * Fork - LGPL
40093  * <script type="text/javascript">
40094  */
40095  
40096 /**
40097  * @class Roo.menu.ColorItem
40098  * @extends Roo.menu.Adapter
40099  * A menu item that wraps the {@link Roo.ColorPalette} component.
40100  * @constructor
40101  * Creates a new ColorItem
40102  * @param {Object} config Configuration options
40103  */
40104 Roo.menu.ColorItem = function(config){
40105     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40106     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40107     this.palette = this.component;
40108     this.relayEvents(this.palette, ["select"]);
40109     if(this.selectHandler){
40110         this.on('select', this.selectHandler, this.scope);
40111     }
40112 };
40113 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40114  * Based on:
40115  * Ext JS Library 1.1.1
40116  * Copyright(c) 2006-2007, Ext JS, LLC.
40117  *
40118  * Originally Released Under LGPL - original licence link has changed is not relivant.
40119  *
40120  * Fork - LGPL
40121  * <script type="text/javascript">
40122  */
40123  
40124
40125 /**
40126  * @class Roo.menu.DateMenu
40127  * @extends Roo.menu.Menu
40128  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40129  * @constructor
40130  * Creates a new DateMenu
40131  * @param {Object} config Configuration options
40132  */
40133 Roo.menu.DateMenu = function(config){
40134     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40135     this.plain = true;
40136     var di = new Roo.menu.DateItem(config);
40137     this.add(di);
40138     /**
40139      * The {@link Roo.DatePicker} instance for this DateMenu
40140      * @type DatePicker
40141      */
40142     this.picker = di.picker;
40143     /**
40144      * @event select
40145      * @param {DatePicker} picker
40146      * @param {Date} date
40147      */
40148     this.relayEvents(di, ["select"]);
40149     this.on('beforeshow', function(){
40150         if(this.picker){
40151             this.picker.hideMonthPicker(false);
40152         }
40153     }, this);
40154 };
40155 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40156     cls:'x-date-menu'
40157 });/*
40158  * Based on:
40159  * Ext JS Library 1.1.1
40160  * Copyright(c) 2006-2007, Ext JS, LLC.
40161  *
40162  * Originally Released Under LGPL - original licence link has changed is not relivant.
40163  *
40164  * Fork - LGPL
40165  * <script type="text/javascript">
40166  */
40167  
40168
40169 /**
40170  * @class Roo.menu.ColorMenu
40171  * @extends Roo.menu.Menu
40172  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40173  * @constructor
40174  * Creates a new ColorMenu
40175  * @param {Object} config Configuration options
40176  */
40177 Roo.menu.ColorMenu = function(config){
40178     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40179     this.plain = true;
40180     var ci = new Roo.menu.ColorItem(config);
40181     this.add(ci);
40182     /**
40183      * The {@link Roo.ColorPalette} instance for this ColorMenu
40184      * @type ColorPalette
40185      */
40186     this.palette = ci.palette;
40187     /**
40188      * @event select
40189      * @param {ColorPalette} palette
40190      * @param {String} color
40191      */
40192     this.relayEvents(ci, ["select"]);
40193 };
40194 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40195  * Based on:
40196  * Ext JS Library 1.1.1
40197  * Copyright(c) 2006-2007, Ext JS, LLC.
40198  *
40199  * Originally Released Under LGPL - original licence link has changed is not relivant.
40200  *
40201  * Fork - LGPL
40202  * <script type="text/javascript">
40203  */
40204  
40205 /**
40206  * @class Roo.form.TextItem
40207  * @extends Roo.BoxComponent
40208  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40209  * @constructor
40210  * Creates a new TextItem
40211  * @param {Object} config Configuration options
40212  */
40213 Roo.form.TextItem = function(config){
40214     Roo.form.TextItem.superclass.constructor.call(this, config);
40215 };
40216
40217 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40218     
40219     /**
40220      * @cfg {String} tag the tag for this item (default div)
40221      */
40222     tag : 'div',
40223     /**
40224      * @cfg {String} html the content for this item
40225      */
40226     html : '',
40227     
40228     getAutoCreate : function()
40229     {
40230         var cfg = {
40231             id: this.id,
40232             tag: this.tag,
40233             html: this.html,
40234             cls: 'x-form-item'
40235         };
40236         
40237         return cfg;
40238         
40239     },
40240     
40241     onRender : function(ct, position)
40242     {
40243         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40244         
40245         if(!this.el){
40246             var cfg = this.getAutoCreate();
40247             if(!cfg.name){
40248                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40249             }
40250             if (!cfg.name.length) {
40251                 delete cfg.name;
40252             }
40253             this.el = ct.createChild(cfg, position);
40254         }
40255     },
40256     /*
40257      * setHTML
40258      * @param {String} html update the Contents of the element.
40259      */
40260     setHTML : function(html)
40261     {
40262         this.fieldEl.dom.innerHTML = html;
40263     }
40264     
40265 });/*
40266  * Based on:
40267  * Ext JS Library 1.1.1
40268  * Copyright(c) 2006-2007, Ext JS, LLC.
40269  *
40270  * Originally Released Under LGPL - original licence link has changed is not relivant.
40271  *
40272  * Fork - LGPL
40273  * <script type="text/javascript">
40274  */
40275  
40276 /**
40277  * @class Roo.form.Field
40278  * @extends Roo.BoxComponent
40279  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40280  * @constructor
40281  * Creates a new Field
40282  * @param {Object} config Configuration options
40283  */
40284 Roo.form.Field = function(config){
40285     Roo.form.Field.superclass.constructor.call(this, config);
40286 };
40287
40288 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40289     /**
40290      * @cfg {String} fieldLabel Label to use when rendering a form.
40291      */
40292        /**
40293      * @cfg {String} qtip Mouse over tip
40294      */
40295      
40296     /**
40297      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40298      */
40299     invalidClass : "x-form-invalid",
40300     /**
40301      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
40302      */
40303     invalidText : "The value in this field is invalid",
40304     /**
40305      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40306      */
40307     focusClass : "x-form-focus",
40308     /**
40309      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40310       automatic validation (defaults to "keyup").
40311      */
40312     validationEvent : "keyup",
40313     /**
40314      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40315      */
40316     validateOnBlur : true,
40317     /**
40318      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40319      */
40320     validationDelay : 250,
40321     /**
40322      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40323      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40324      */
40325     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40326     /**
40327      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40328      */
40329     fieldClass : "x-form-field",
40330     /**
40331      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40332      *<pre>
40333 Value         Description
40334 -----------   ----------------------------------------------------------------------
40335 qtip          Display a quick tip when the user hovers over the field
40336 title         Display a default browser title attribute popup
40337 under         Add a block div beneath the field containing the error text
40338 side          Add an error icon to the right of the field with a popup on hover
40339 [element id]  Add the error text directly to the innerHTML of the specified element
40340 </pre>
40341      */
40342     msgTarget : 'qtip',
40343     /**
40344      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40345      */
40346     msgFx : 'normal',
40347
40348     /**
40349      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
40350      */
40351     readOnly : false,
40352
40353     /**
40354      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40355      */
40356     disabled : false,
40357
40358     /**
40359      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40360      */
40361     inputType : undefined,
40362     
40363     /**
40364      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
40365          */
40366         tabIndex : undefined,
40367         
40368     // private
40369     isFormField : true,
40370
40371     // private
40372     hasFocus : false,
40373     /**
40374      * @property {Roo.Element} fieldEl
40375      * Element Containing the rendered Field (with label etc.)
40376      */
40377     /**
40378      * @cfg {Mixed} value A value to initialize this field with.
40379      */
40380     value : undefined,
40381
40382     /**
40383      * @cfg {String} name The field's HTML name attribute.
40384      */
40385     /**
40386      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40387      */
40388     // private
40389     loadedValue : false,
40390      
40391      
40392         // private ??
40393         initComponent : function(){
40394         Roo.form.Field.superclass.initComponent.call(this);
40395         this.addEvents({
40396             /**
40397              * @event focus
40398              * Fires when this field receives input focus.
40399              * @param {Roo.form.Field} this
40400              */
40401             focus : true,
40402             /**
40403              * @event blur
40404              * Fires when this field loses input focus.
40405              * @param {Roo.form.Field} this
40406              */
40407             blur : true,
40408             /**
40409              * @event specialkey
40410              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40411              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40412              * @param {Roo.form.Field} this
40413              * @param {Roo.EventObject} e The event object
40414              */
40415             specialkey : true,
40416             /**
40417              * @event change
40418              * Fires just before the field blurs if the field value has changed.
40419              * @param {Roo.form.Field} this
40420              * @param {Mixed} newValue The new value
40421              * @param {Mixed} oldValue The original value
40422              */
40423             change : true,
40424             /**
40425              * @event invalid
40426              * Fires after the field has been marked as invalid.
40427              * @param {Roo.form.Field} this
40428              * @param {String} msg The validation message
40429              */
40430             invalid : true,
40431             /**
40432              * @event valid
40433              * Fires after the field has been validated with no errors.
40434              * @param {Roo.form.Field} this
40435              */
40436             valid : true,
40437              /**
40438              * @event keyup
40439              * Fires after the key up
40440              * @param {Roo.form.Field} this
40441              * @param {Roo.EventObject}  e The event Object
40442              */
40443             keyup : true
40444         });
40445     },
40446
40447     /**
40448      * Returns the name attribute of the field if available
40449      * @return {String} name The field name
40450      */
40451     getName: function(){
40452          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40453     },
40454
40455     // private
40456     onRender : function(ct, position){
40457         Roo.form.Field.superclass.onRender.call(this, ct, position);
40458         if(!this.el){
40459             var cfg = this.getAutoCreate();
40460             if(!cfg.name){
40461                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40462             }
40463             if (!cfg.name.length) {
40464                 delete cfg.name;
40465             }
40466             if(this.inputType){
40467                 cfg.type = this.inputType;
40468             }
40469             this.el = ct.createChild(cfg, position);
40470         }
40471         var type = this.el.dom.type;
40472         if(type){
40473             if(type == 'password'){
40474                 type = 'text';
40475             }
40476             this.el.addClass('x-form-'+type);
40477         }
40478         if(this.readOnly){
40479             this.el.dom.readOnly = true;
40480         }
40481         if(this.tabIndex !== undefined){
40482             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40483         }
40484
40485         this.el.addClass([this.fieldClass, this.cls]);
40486         this.initValue();
40487     },
40488
40489     /**
40490      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40491      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40492      * @return {Roo.form.Field} this
40493      */
40494     applyTo : function(target){
40495         this.allowDomMove = false;
40496         this.el = Roo.get(target);
40497         this.render(this.el.dom.parentNode);
40498         return this;
40499     },
40500
40501     // private
40502     initValue : function(){
40503         if(this.value !== undefined){
40504             this.setValue(this.value);
40505         }else if(this.el.dom.value.length > 0){
40506             this.setValue(this.el.dom.value);
40507         }
40508     },
40509
40510     /**
40511      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40512      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40513      */
40514     isDirty : function() {
40515         if(this.disabled) {
40516             return false;
40517         }
40518         return String(this.getValue()) !== String(this.originalValue);
40519     },
40520
40521     /**
40522      * stores the current value in loadedValue
40523      */
40524     resetHasChanged : function()
40525     {
40526         this.loadedValue = String(this.getValue());
40527     },
40528     /**
40529      * checks the current value against the 'loaded' value.
40530      * Note - will return false if 'resetHasChanged' has not been called first.
40531      */
40532     hasChanged : function()
40533     {
40534         if(this.disabled || this.readOnly) {
40535             return false;
40536         }
40537         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40538     },
40539     
40540     
40541     
40542     // private
40543     afterRender : function(){
40544         Roo.form.Field.superclass.afterRender.call(this);
40545         this.initEvents();
40546     },
40547
40548     // private
40549     fireKey : function(e){
40550         //Roo.log('field ' + e.getKey());
40551         if(e.isNavKeyPress()){
40552             this.fireEvent("specialkey", this, e);
40553         }
40554     },
40555
40556     /**
40557      * Resets the current field value to the originally loaded value and clears any validation messages
40558      */
40559     reset : function(){
40560         this.setValue(this.resetValue);
40561         this.originalValue = this.getValue();
40562         this.clearInvalid();
40563     },
40564
40565     // private
40566     initEvents : function(){
40567         // safari killled keypress - so keydown is now used..
40568         this.el.on("keydown" , this.fireKey,  this);
40569         this.el.on("focus", this.onFocus,  this);
40570         this.el.on("blur", this.onBlur,  this);
40571         this.el.relayEvent('keyup', this);
40572
40573         // reference to original value for reset
40574         this.originalValue = this.getValue();
40575         this.resetValue =  this.getValue();
40576     },
40577
40578     // private
40579     onFocus : function(){
40580         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40581             this.el.addClass(this.focusClass);
40582         }
40583         if(!this.hasFocus){
40584             this.hasFocus = true;
40585             this.startValue = this.getValue();
40586             this.fireEvent("focus", this);
40587         }
40588     },
40589
40590     beforeBlur : Roo.emptyFn,
40591
40592     // private
40593     onBlur : function(){
40594         this.beforeBlur();
40595         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40596             this.el.removeClass(this.focusClass);
40597         }
40598         this.hasFocus = false;
40599         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40600             this.validate();
40601         }
40602         var v = this.getValue();
40603         if(String(v) !== String(this.startValue)){
40604             this.fireEvent('change', this, v, this.startValue);
40605         }
40606         this.fireEvent("blur", this);
40607     },
40608
40609     /**
40610      * Returns whether or not the field value is currently valid
40611      * @param {Boolean} preventMark True to disable marking the field invalid
40612      * @return {Boolean} True if the value is valid, else false
40613      */
40614     isValid : function(preventMark){
40615         if(this.disabled){
40616             return true;
40617         }
40618         var restore = this.preventMark;
40619         this.preventMark = preventMark === true;
40620         var v = this.validateValue(this.processValue(this.getRawValue()));
40621         this.preventMark = restore;
40622         return v;
40623     },
40624
40625     /**
40626      * Validates the field value
40627      * @return {Boolean} True if the value is valid, else false
40628      */
40629     validate : function(){
40630         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40631             this.clearInvalid();
40632             return true;
40633         }
40634         return false;
40635     },
40636
40637     processValue : function(value){
40638         return value;
40639     },
40640
40641     // private
40642     // Subclasses should provide the validation implementation by overriding this
40643     validateValue : function(value){
40644         return true;
40645     },
40646
40647     /**
40648      * Mark this field as invalid
40649      * @param {String} msg The validation message
40650      */
40651     markInvalid : function(msg){
40652         if(!this.rendered || this.preventMark){ // not rendered
40653             return;
40654         }
40655         
40656         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40657         
40658         obj.el.addClass(this.invalidClass);
40659         msg = msg || this.invalidText;
40660         switch(this.msgTarget){
40661             case 'qtip':
40662                 obj.el.dom.qtip = msg;
40663                 obj.el.dom.qclass = 'x-form-invalid-tip';
40664                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40665                     Roo.QuickTips.enable();
40666                 }
40667                 break;
40668             case 'title':
40669                 this.el.dom.title = msg;
40670                 break;
40671             case 'under':
40672                 if(!this.errorEl){
40673                     var elp = this.el.findParent('.x-form-element', 5, true);
40674                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40675                     this.errorEl.setWidth(elp.getWidth(true)-20);
40676                 }
40677                 this.errorEl.update(msg);
40678                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40679                 break;
40680             case 'side':
40681                 if(!this.errorIcon){
40682                     var elp = this.el.findParent('.x-form-element', 5, true);
40683                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40684                 }
40685                 this.alignErrorIcon();
40686                 this.errorIcon.dom.qtip = msg;
40687                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40688                 this.errorIcon.show();
40689                 this.on('resize', this.alignErrorIcon, this);
40690                 break;
40691             default:
40692                 var t = Roo.getDom(this.msgTarget);
40693                 t.innerHTML = msg;
40694                 t.style.display = this.msgDisplay;
40695                 break;
40696         }
40697         this.fireEvent('invalid', this, msg);
40698     },
40699
40700     // private
40701     alignErrorIcon : function(){
40702         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40703     },
40704
40705     /**
40706      * Clear any invalid styles/messages for this field
40707      */
40708     clearInvalid : function(){
40709         if(!this.rendered || this.preventMark){ // not rendered
40710             return;
40711         }
40712         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40713         
40714         obj.el.removeClass(this.invalidClass);
40715         switch(this.msgTarget){
40716             case 'qtip':
40717                 obj.el.dom.qtip = '';
40718                 break;
40719             case 'title':
40720                 this.el.dom.title = '';
40721                 break;
40722             case 'under':
40723                 if(this.errorEl){
40724                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40725                 }
40726                 break;
40727             case 'side':
40728                 if(this.errorIcon){
40729                     this.errorIcon.dom.qtip = '';
40730                     this.errorIcon.hide();
40731                     this.un('resize', this.alignErrorIcon, this);
40732                 }
40733                 break;
40734             default:
40735                 var t = Roo.getDom(this.msgTarget);
40736                 t.innerHTML = '';
40737                 t.style.display = 'none';
40738                 break;
40739         }
40740         this.fireEvent('valid', this);
40741     },
40742
40743     /**
40744      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40745      * @return {Mixed} value The field value
40746      */
40747     getRawValue : function(){
40748         var v = this.el.getValue();
40749         
40750         return v;
40751     },
40752
40753     /**
40754      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40755      * @return {Mixed} value The field value
40756      */
40757     getValue : function(){
40758         var v = this.el.getValue();
40759          
40760         return v;
40761     },
40762
40763     /**
40764      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40765      * @param {Mixed} value The value to set
40766      */
40767     setRawValue : function(v){
40768         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40769     },
40770
40771     /**
40772      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40773      * @param {Mixed} value The value to set
40774      */
40775     setValue : function(v){
40776         this.value = v;
40777         if(this.rendered){
40778             this.el.dom.value = (v === null || v === undefined ? '' : v);
40779              this.validate();
40780         }
40781     },
40782
40783     adjustSize : function(w, h){
40784         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40785         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40786         return s;
40787     },
40788
40789     adjustWidth : function(tag, w){
40790         tag = tag.toLowerCase();
40791         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40792             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40793                 if(tag == 'input'){
40794                     return w + 2;
40795                 }
40796                 if(tag == 'textarea'){
40797                     return w-2;
40798                 }
40799             }else if(Roo.isOpera){
40800                 if(tag == 'input'){
40801                     return w + 2;
40802                 }
40803                 if(tag == 'textarea'){
40804                     return w-2;
40805                 }
40806             }
40807         }
40808         return w;
40809     }
40810 });
40811
40812
40813 // anything other than normal should be considered experimental
40814 Roo.form.Field.msgFx = {
40815     normal : {
40816         show: function(msgEl, f){
40817             msgEl.setDisplayed('block');
40818         },
40819
40820         hide : function(msgEl, f){
40821             msgEl.setDisplayed(false).update('');
40822         }
40823     },
40824
40825     slide : {
40826         show: function(msgEl, f){
40827             msgEl.slideIn('t', {stopFx:true});
40828         },
40829
40830         hide : function(msgEl, f){
40831             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40832         }
40833     },
40834
40835     slideRight : {
40836         show: function(msgEl, f){
40837             msgEl.fixDisplay();
40838             msgEl.alignTo(f.el, 'tl-tr');
40839             msgEl.slideIn('l', {stopFx:true});
40840         },
40841
40842         hide : function(msgEl, f){
40843             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40844         }
40845     }
40846 };/*
40847  * Based on:
40848  * Ext JS Library 1.1.1
40849  * Copyright(c) 2006-2007, Ext JS, LLC.
40850  *
40851  * Originally Released Under LGPL - original licence link has changed is not relivant.
40852  *
40853  * Fork - LGPL
40854  * <script type="text/javascript">
40855  */
40856  
40857
40858 /**
40859  * @class Roo.form.TextField
40860  * @extends Roo.form.Field
40861  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40862  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40863  * @constructor
40864  * Creates a new TextField
40865  * @param {Object} config Configuration options
40866  */
40867 Roo.form.TextField = function(config){
40868     Roo.form.TextField.superclass.constructor.call(this, config);
40869     this.addEvents({
40870         /**
40871          * @event autosize
40872          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40873          * according to the default logic, but this event provides a hook for the developer to apply additional
40874          * logic at runtime to resize the field if needed.
40875              * @param {Roo.form.Field} this This text field
40876              * @param {Number} width The new field width
40877              */
40878         autosize : true
40879     });
40880 };
40881
40882 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40883     /**
40884      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40885      */
40886     grow : false,
40887     /**
40888      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40889      */
40890     growMin : 30,
40891     /**
40892      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40893      */
40894     growMax : 800,
40895     /**
40896      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40897      */
40898     vtype : null,
40899     /**
40900      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40901      */
40902     maskRe : null,
40903     /**
40904      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40905      */
40906     disableKeyFilter : false,
40907     /**
40908      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40909      */
40910     allowBlank : true,
40911     /**
40912      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40913      */
40914     minLength : 0,
40915     /**
40916      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40917      */
40918     maxLength : Number.MAX_VALUE,
40919     /**
40920      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40921      */
40922     minLengthText : "The minimum length for this field is {0}",
40923     /**
40924      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40925      */
40926     maxLengthText : "The maximum length for this field is {0}",
40927     /**
40928      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40929      */
40930     selectOnFocus : false,
40931     /**
40932      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40933      */    
40934     allowLeadingSpace : false,
40935     /**
40936      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40937      */
40938     blankText : "This field is required",
40939     /**
40940      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40941      * If available, this function will be called only after the basic validators all return true, and will be passed the
40942      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40943      */
40944     validator : null,
40945     /**
40946      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40947      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40948      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40949      */
40950     regex : null,
40951     /**
40952      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40953      */
40954     regexText : "",
40955     /**
40956      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40957      */
40958     emptyText : null,
40959    
40960
40961     // private
40962     initEvents : function()
40963     {
40964         if (this.emptyText) {
40965             this.el.attr('placeholder', this.emptyText);
40966         }
40967         
40968         Roo.form.TextField.superclass.initEvents.call(this);
40969         if(this.validationEvent == 'keyup'){
40970             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40971             this.el.on('keyup', this.filterValidation, this);
40972         }
40973         else if(this.validationEvent !== false){
40974             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40975         }
40976         
40977         if(this.selectOnFocus){
40978             this.on("focus", this.preFocus, this);
40979         }
40980         if (!this.allowLeadingSpace) {
40981             this.on('blur', this.cleanLeadingSpace, this);
40982         }
40983         
40984         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40985             this.el.on("keypress", this.filterKeys, this);
40986         }
40987         if(this.grow){
40988             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40989             this.el.on("click", this.autoSize,  this);
40990         }
40991         if(this.el.is('input[type=password]') && Roo.isSafari){
40992             this.el.on('keydown', this.SafariOnKeyDown, this);
40993         }
40994     },
40995
40996     processValue : function(value){
40997         if(this.stripCharsRe){
40998             var newValue = value.replace(this.stripCharsRe, '');
40999             if(newValue !== value){
41000                 this.setRawValue(newValue);
41001                 return newValue;
41002             }
41003         }
41004         return value;
41005     },
41006
41007     filterValidation : function(e){
41008         if(!e.isNavKeyPress()){
41009             this.validationTask.delay(this.validationDelay);
41010         }
41011     },
41012
41013     // private
41014     onKeyUp : function(e){
41015         if(!e.isNavKeyPress()){
41016             this.autoSize();
41017         }
41018     },
41019     // private - clean the leading white space
41020     cleanLeadingSpace : function(e)
41021     {
41022         if ( this.inputType == 'file') {
41023             return;
41024         }
41025         
41026         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41027     },
41028     /**
41029      * Resets the current field value to the originally-loaded value and clears any validation messages.
41030      *  
41031      */
41032     reset : function(){
41033         Roo.form.TextField.superclass.reset.call(this);
41034        
41035     }, 
41036     // private
41037     preFocus : function(){
41038         
41039         if(this.selectOnFocus){
41040             this.el.dom.select();
41041         }
41042     },
41043
41044     
41045     // private
41046     filterKeys : function(e){
41047         var k = e.getKey();
41048         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41049             return;
41050         }
41051         var c = e.getCharCode(), cc = String.fromCharCode(c);
41052         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41053             return;
41054         }
41055         if(!this.maskRe.test(cc)){
41056             e.stopEvent();
41057         }
41058     },
41059
41060     setValue : function(v){
41061         
41062         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41063         
41064         this.autoSize();
41065     },
41066
41067     /**
41068      * Validates a value according to the field's validation rules and marks the field as invalid
41069      * if the validation fails
41070      * @param {Mixed} value The value to validate
41071      * @return {Boolean} True if the value is valid, else false
41072      */
41073     validateValue : function(value){
41074         if(value.length < 1)  { // if it's blank
41075              if(this.allowBlank){
41076                 this.clearInvalid();
41077                 return true;
41078              }else{
41079                 this.markInvalid(this.blankText);
41080                 return false;
41081              }
41082         }
41083         if(value.length < this.minLength){
41084             this.markInvalid(String.format(this.minLengthText, this.minLength));
41085             return false;
41086         }
41087         if(value.length > this.maxLength){
41088             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41089             return false;
41090         }
41091         if(this.vtype){
41092             var vt = Roo.form.VTypes;
41093             if(!vt[this.vtype](value, this)){
41094                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41095                 return false;
41096             }
41097         }
41098         if(typeof this.validator == "function"){
41099             var msg = this.validator(value);
41100             if(msg !== true){
41101                 this.markInvalid(msg);
41102                 return false;
41103             }
41104         }
41105         if(this.regex && !this.regex.test(value)){
41106             this.markInvalid(this.regexText);
41107             return false;
41108         }
41109         return true;
41110     },
41111
41112     /**
41113      * Selects text in this field
41114      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41115      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41116      */
41117     selectText : function(start, end){
41118         var v = this.getRawValue();
41119         if(v.length > 0){
41120             start = start === undefined ? 0 : start;
41121             end = end === undefined ? v.length : end;
41122             var d = this.el.dom;
41123             if(d.setSelectionRange){
41124                 d.setSelectionRange(start, end);
41125             }else if(d.createTextRange){
41126                 var range = d.createTextRange();
41127                 range.moveStart("character", start);
41128                 range.moveEnd("character", v.length-end);
41129                 range.select();
41130             }
41131         }
41132     },
41133
41134     /**
41135      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41136      * This only takes effect if grow = true, and fires the autosize event.
41137      */
41138     autoSize : function(){
41139         if(!this.grow || !this.rendered){
41140             return;
41141         }
41142         if(!this.metrics){
41143             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41144         }
41145         var el = this.el;
41146         var v = el.dom.value;
41147         var d = document.createElement('div');
41148         d.appendChild(document.createTextNode(v));
41149         v = d.innerHTML;
41150         d = null;
41151         v += "&#160;";
41152         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41153         this.el.setWidth(w);
41154         this.fireEvent("autosize", this, w);
41155     },
41156     
41157     // private
41158     SafariOnKeyDown : function(event)
41159     {
41160         // this is a workaround for a password hang bug on chrome/ webkit.
41161         
41162         var isSelectAll = false;
41163         
41164         if(this.el.dom.selectionEnd > 0){
41165             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41166         }
41167         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41168             event.preventDefault();
41169             this.setValue('');
41170             return;
41171         }
41172         
41173         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41174             
41175             event.preventDefault();
41176             // this is very hacky as keydown always get's upper case.
41177             
41178             var cc = String.fromCharCode(event.getCharCode());
41179             
41180             
41181             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41182             
41183         }
41184         
41185         
41186     }
41187 });/*
41188  * Based on:
41189  * Ext JS Library 1.1.1
41190  * Copyright(c) 2006-2007, Ext JS, LLC.
41191  *
41192  * Originally Released Under LGPL - original licence link has changed is not relivant.
41193  *
41194  * Fork - LGPL
41195  * <script type="text/javascript">
41196  */
41197  
41198 /**
41199  * @class Roo.form.Hidden
41200  * @extends Roo.form.TextField
41201  * Simple Hidden element used on forms 
41202  * 
41203  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41204  * 
41205  * @constructor
41206  * Creates a new Hidden form element.
41207  * @param {Object} config Configuration options
41208  */
41209
41210
41211
41212 // easy hidden field...
41213 Roo.form.Hidden = function(config){
41214     Roo.form.Hidden.superclass.constructor.call(this, config);
41215 };
41216   
41217 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41218     fieldLabel:      '',
41219     inputType:      'hidden',
41220     width:          50,
41221     allowBlank:     true,
41222     labelSeparator: '',
41223     hidden:         true,
41224     itemCls :       'x-form-item-display-none'
41225
41226
41227 });
41228
41229
41230 /*
41231  * Based on:
41232  * Ext JS Library 1.1.1
41233  * Copyright(c) 2006-2007, Ext JS, LLC.
41234  *
41235  * Originally Released Under LGPL - original licence link has changed is not relivant.
41236  *
41237  * Fork - LGPL
41238  * <script type="text/javascript">
41239  */
41240  
41241 /**
41242  * @class Roo.form.TriggerField
41243  * @extends Roo.form.TextField
41244  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41245  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41246  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41247  * for which you can provide a custom implementation.  For example:
41248  * <pre><code>
41249 var trigger = new Roo.form.TriggerField();
41250 trigger.onTriggerClick = myTriggerFn;
41251 trigger.applyTo('my-field');
41252 </code></pre>
41253  *
41254  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41255  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41256  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41257  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41258  * @constructor
41259  * Create a new TriggerField.
41260  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41261  * to the base TextField)
41262  */
41263 Roo.form.TriggerField = function(config){
41264     this.mimicing = false;
41265     Roo.form.TriggerField.superclass.constructor.call(this, config);
41266 };
41267
41268 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41269     /**
41270      * @cfg {String} triggerClass A CSS class to apply to the trigger
41271      */
41272     /**
41273      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41274      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41275      */
41276     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41277     /**
41278      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41279      */
41280     hideTrigger:false,
41281
41282     /** @cfg {Boolean} grow @hide */
41283     /** @cfg {Number} growMin @hide */
41284     /** @cfg {Number} growMax @hide */
41285
41286     /**
41287      * @hide 
41288      * @method
41289      */
41290     autoSize: Roo.emptyFn,
41291     // private
41292     monitorTab : true,
41293     // private
41294     deferHeight : true,
41295
41296     
41297     actionMode : 'wrap',
41298     // private
41299     onResize : function(w, h){
41300         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41301         if(typeof w == 'number'){
41302             var x = w - this.trigger.getWidth();
41303             this.el.setWidth(this.adjustWidth('input', x));
41304             this.trigger.setStyle('left', x+'px');
41305         }
41306     },
41307
41308     // private
41309     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41310
41311     // private
41312     getResizeEl : function(){
41313         return this.wrap;
41314     },
41315
41316     // private
41317     getPositionEl : function(){
41318         return this.wrap;
41319     },
41320
41321     // private
41322     alignErrorIcon : function(){
41323         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41324     },
41325
41326     // private
41327     onRender : function(ct, position){
41328         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41329         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41330         this.trigger = this.wrap.createChild(this.triggerConfig ||
41331                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41332         if(this.hideTrigger){
41333             this.trigger.setDisplayed(false);
41334         }
41335         this.initTrigger();
41336         if(!this.width){
41337             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41338         }
41339     },
41340
41341     // private
41342     initTrigger : function(){
41343         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41344         this.trigger.addClassOnOver('x-form-trigger-over');
41345         this.trigger.addClassOnClick('x-form-trigger-click');
41346     },
41347
41348     // private
41349     onDestroy : function(){
41350         if(this.trigger){
41351             this.trigger.removeAllListeners();
41352             this.trigger.remove();
41353         }
41354         if(this.wrap){
41355             this.wrap.remove();
41356         }
41357         Roo.form.TriggerField.superclass.onDestroy.call(this);
41358     },
41359
41360     // private
41361     onFocus : function(){
41362         Roo.form.TriggerField.superclass.onFocus.call(this);
41363         if(!this.mimicing){
41364             this.wrap.addClass('x-trigger-wrap-focus');
41365             this.mimicing = true;
41366             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41367             if(this.monitorTab){
41368                 this.el.on("keydown", this.checkTab, this);
41369             }
41370         }
41371     },
41372
41373     // private
41374     checkTab : function(e){
41375         if(e.getKey() == e.TAB){
41376             this.triggerBlur();
41377         }
41378     },
41379
41380     // private
41381     onBlur : function(){
41382         // do nothing
41383     },
41384
41385     // private
41386     mimicBlur : function(e, t){
41387         if(!this.wrap.contains(t) && this.validateBlur()){
41388             this.triggerBlur();
41389         }
41390     },
41391
41392     // private
41393     triggerBlur : function(){
41394         this.mimicing = false;
41395         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41396         if(this.monitorTab){
41397             this.el.un("keydown", this.checkTab, this);
41398         }
41399         this.wrap.removeClass('x-trigger-wrap-focus');
41400         Roo.form.TriggerField.superclass.onBlur.call(this);
41401     },
41402
41403     // private
41404     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41405     validateBlur : function(e, t){
41406         return true;
41407     },
41408
41409     // private
41410     onDisable : function(){
41411         Roo.form.TriggerField.superclass.onDisable.call(this);
41412         if(this.wrap){
41413             this.wrap.addClass('x-item-disabled');
41414         }
41415     },
41416
41417     // private
41418     onEnable : function(){
41419         Roo.form.TriggerField.superclass.onEnable.call(this);
41420         if(this.wrap){
41421             this.wrap.removeClass('x-item-disabled');
41422         }
41423     },
41424
41425     // private
41426     onShow : function(){
41427         var ae = this.getActionEl();
41428         
41429         if(ae){
41430             ae.dom.style.display = '';
41431             ae.dom.style.visibility = 'visible';
41432         }
41433     },
41434
41435     // private
41436     
41437     onHide : function(){
41438         var ae = this.getActionEl();
41439         ae.dom.style.display = 'none';
41440     },
41441
41442     /**
41443      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41444      * by an implementing function.
41445      * @method
41446      * @param {EventObject} e
41447      */
41448     onTriggerClick : Roo.emptyFn
41449 });
41450
41451 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41452 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41453 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41454 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41455     initComponent : function(){
41456         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41457
41458         this.triggerConfig = {
41459             tag:'span', cls:'x-form-twin-triggers', cn:[
41460             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41461             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41462         ]};
41463     },
41464
41465     getTrigger : function(index){
41466         return this.triggers[index];
41467     },
41468
41469     initTrigger : function(){
41470         var ts = this.trigger.select('.x-form-trigger', true);
41471         this.wrap.setStyle('overflow', 'hidden');
41472         var triggerField = this;
41473         ts.each(function(t, all, index){
41474             t.hide = function(){
41475                 var w = triggerField.wrap.getWidth();
41476                 this.dom.style.display = 'none';
41477                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41478             };
41479             t.show = function(){
41480                 var w = triggerField.wrap.getWidth();
41481                 this.dom.style.display = '';
41482                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41483             };
41484             var triggerIndex = 'Trigger'+(index+1);
41485
41486             if(this['hide'+triggerIndex]){
41487                 t.dom.style.display = 'none';
41488             }
41489             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41490             t.addClassOnOver('x-form-trigger-over');
41491             t.addClassOnClick('x-form-trigger-click');
41492         }, this);
41493         this.triggers = ts.elements;
41494     },
41495
41496     onTrigger1Click : Roo.emptyFn,
41497     onTrigger2Click : Roo.emptyFn
41498 });/*
41499  * Based on:
41500  * Ext JS Library 1.1.1
41501  * Copyright(c) 2006-2007, Ext JS, LLC.
41502  *
41503  * Originally Released Under LGPL - original licence link has changed is not relivant.
41504  *
41505  * Fork - LGPL
41506  * <script type="text/javascript">
41507  */
41508  
41509 /**
41510  * @class Roo.form.TextArea
41511  * @extends Roo.form.TextField
41512  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41513  * support for auto-sizing.
41514  * @constructor
41515  * Creates a new TextArea
41516  * @param {Object} config Configuration options
41517  */
41518 Roo.form.TextArea = function(config){
41519     Roo.form.TextArea.superclass.constructor.call(this, config);
41520     // these are provided exchanges for backwards compat
41521     // minHeight/maxHeight were replaced by growMin/growMax to be
41522     // compatible with TextField growing config values
41523     if(this.minHeight !== undefined){
41524         this.growMin = this.minHeight;
41525     }
41526     if(this.maxHeight !== undefined){
41527         this.growMax = this.maxHeight;
41528     }
41529 };
41530
41531 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41532     /**
41533      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41534      */
41535     growMin : 60,
41536     /**
41537      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41538      */
41539     growMax: 1000,
41540     /**
41541      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41542      * in the field (equivalent to setting overflow: hidden, defaults to false)
41543      */
41544     preventScrollbars: false,
41545     /**
41546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41547      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41548      */
41549
41550     // private
41551     onRender : function(ct, position){
41552         if(!this.el){
41553             this.defaultAutoCreate = {
41554                 tag: "textarea",
41555                 style:"width:300px;height:60px;",
41556                 autocomplete: "new-password"
41557             };
41558         }
41559         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41560         if(this.grow){
41561             this.textSizeEl = Roo.DomHelper.append(document.body, {
41562                 tag: "pre", cls: "x-form-grow-sizer"
41563             });
41564             if(this.preventScrollbars){
41565                 this.el.setStyle("overflow", "hidden");
41566             }
41567             this.el.setHeight(this.growMin);
41568         }
41569     },
41570
41571     onDestroy : function(){
41572         if(this.textSizeEl){
41573             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41574         }
41575         Roo.form.TextArea.superclass.onDestroy.call(this);
41576     },
41577
41578     // private
41579     onKeyUp : function(e){
41580         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41581             this.autoSize();
41582         }
41583     },
41584
41585     /**
41586      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41587      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41588      */
41589     autoSize : function(){
41590         if(!this.grow || !this.textSizeEl){
41591             return;
41592         }
41593         var el = this.el;
41594         var v = el.dom.value;
41595         var ts = this.textSizeEl;
41596
41597         ts.innerHTML = '';
41598         ts.appendChild(document.createTextNode(v));
41599         v = ts.innerHTML;
41600
41601         Roo.fly(ts).setWidth(this.el.getWidth());
41602         if(v.length < 1){
41603             v = "&#160;&#160;";
41604         }else{
41605             if(Roo.isIE){
41606                 v = v.replace(/\n/g, '<p>&#160;</p>');
41607             }
41608             v += "&#160;\n&#160;";
41609         }
41610         ts.innerHTML = v;
41611         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41612         if(h != this.lastHeight){
41613             this.lastHeight = h;
41614             this.el.setHeight(h);
41615             this.fireEvent("autosize", this, h);
41616         }
41617     }
41618 });/*
41619  * Based on:
41620  * Ext JS Library 1.1.1
41621  * Copyright(c) 2006-2007, Ext JS, LLC.
41622  *
41623  * Originally Released Under LGPL - original licence link has changed is not relivant.
41624  *
41625  * Fork - LGPL
41626  * <script type="text/javascript">
41627  */
41628  
41629
41630 /**
41631  * @class Roo.form.NumberField
41632  * @extends Roo.form.TextField
41633  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41634  * @constructor
41635  * Creates a new NumberField
41636  * @param {Object} config Configuration options
41637  */
41638 Roo.form.NumberField = function(config){
41639     Roo.form.NumberField.superclass.constructor.call(this, config);
41640 };
41641
41642 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41643     /**
41644      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41645      */
41646     fieldClass: "x-form-field x-form-num-field",
41647     /**
41648      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41649      */
41650     allowDecimals : true,
41651     /**
41652      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41653      */
41654     decimalSeparator : ".",
41655     /**
41656      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41657      */
41658     decimalPrecision : 2,
41659     /**
41660      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41661      */
41662     allowNegative : true,
41663     /**
41664      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41665      */
41666     minValue : Number.NEGATIVE_INFINITY,
41667     /**
41668      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41669      */
41670     maxValue : Number.MAX_VALUE,
41671     /**
41672      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41673      */
41674     minText : "The minimum value for this field is {0}",
41675     /**
41676      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41677      */
41678     maxText : "The maximum value for this field is {0}",
41679     /**
41680      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41681      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41682      */
41683     nanText : "{0} is not a valid number",
41684
41685     // private
41686     initEvents : function(){
41687         Roo.form.NumberField.superclass.initEvents.call(this);
41688         var allowed = "0123456789";
41689         if(this.allowDecimals){
41690             allowed += this.decimalSeparator;
41691         }
41692         if(this.allowNegative){
41693             allowed += "-";
41694         }
41695         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41696         var keyPress = function(e){
41697             var k = e.getKey();
41698             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41699                 return;
41700             }
41701             var c = e.getCharCode();
41702             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41703                 e.stopEvent();
41704             }
41705         };
41706         this.el.on("keypress", keyPress, this);
41707     },
41708
41709     // private
41710     validateValue : function(value){
41711         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41712             return false;
41713         }
41714         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41715              return true;
41716         }
41717         var num = this.parseValue(value);
41718         if(isNaN(num)){
41719             this.markInvalid(String.format(this.nanText, value));
41720             return false;
41721         }
41722         if(num < this.minValue){
41723             this.markInvalid(String.format(this.minText, this.minValue));
41724             return false;
41725         }
41726         if(num > this.maxValue){
41727             this.markInvalid(String.format(this.maxText, this.maxValue));
41728             return false;
41729         }
41730         return true;
41731     },
41732
41733     getValue : function(){
41734         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41735     },
41736
41737     // private
41738     parseValue : function(value){
41739         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41740         return isNaN(value) ? '' : value;
41741     },
41742
41743     // private
41744     fixPrecision : function(value){
41745         var nan = isNaN(value);
41746         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41747             return nan ? '' : value;
41748         }
41749         return parseFloat(value).toFixed(this.decimalPrecision);
41750     },
41751
41752     setValue : function(v){
41753         v = this.fixPrecision(v);
41754         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41755     },
41756
41757     // private
41758     decimalPrecisionFcn : function(v){
41759         return Math.floor(v);
41760     },
41761
41762     beforeBlur : function(){
41763         var v = this.parseValue(this.getRawValue());
41764         if(v){
41765             this.setValue(v);
41766         }
41767     }
41768 });/*
41769  * Based on:
41770  * Ext JS Library 1.1.1
41771  * Copyright(c) 2006-2007, Ext JS, LLC.
41772  *
41773  * Originally Released Under LGPL - original licence link has changed is not relivant.
41774  *
41775  * Fork - LGPL
41776  * <script type="text/javascript">
41777  */
41778  
41779 /**
41780  * @class Roo.form.DateField
41781  * @extends Roo.form.TriggerField
41782  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41783 * @constructor
41784 * Create a new DateField
41785 * @param {Object} config
41786  */
41787 Roo.form.DateField = function(config)
41788 {
41789     Roo.form.DateField.superclass.constructor.call(this, config);
41790     
41791       this.addEvents({
41792          
41793         /**
41794          * @event select
41795          * Fires when a date is selected
41796              * @param {Roo.form.DateField} combo This combo box
41797              * @param {Date} date The date selected
41798              */
41799         'select' : true
41800          
41801     });
41802     
41803     
41804     if(typeof this.minValue == "string") {
41805         this.minValue = this.parseDate(this.minValue);
41806     }
41807     if(typeof this.maxValue == "string") {
41808         this.maxValue = this.parseDate(this.maxValue);
41809     }
41810     this.ddMatch = null;
41811     if(this.disabledDates){
41812         var dd = this.disabledDates;
41813         var re = "(?:";
41814         for(var i = 0; i < dd.length; i++){
41815             re += dd[i];
41816             if(i != dd.length-1) {
41817                 re += "|";
41818             }
41819         }
41820         this.ddMatch = new RegExp(re + ")");
41821     }
41822 };
41823
41824 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41825     /**
41826      * @cfg {String} format
41827      * The default date format string which can be overriden for localization support.  The format must be
41828      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41829      */
41830     format : "m/d/y",
41831     /**
41832      * @cfg {String} altFormats
41833      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41834      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41835      */
41836     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41837     /**
41838      * @cfg {Array} disabledDays
41839      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41840      */
41841     disabledDays : null,
41842     /**
41843      * @cfg {String} disabledDaysText
41844      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41845      */
41846     disabledDaysText : "Disabled",
41847     /**
41848      * @cfg {Array} disabledDates
41849      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41850      * expression so they are very powerful. Some examples:
41851      * <ul>
41852      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41853      * <li>["03/08", "09/16"] would disable those days for every year</li>
41854      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41855      * <li>["03/../2006"] would disable every day in March 2006</li>
41856      * <li>["^03"] would disable every day in every March</li>
41857      * </ul>
41858      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41859      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41860      */
41861     disabledDates : null,
41862     /**
41863      * @cfg {String} disabledDatesText
41864      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41865      */
41866     disabledDatesText : "Disabled",
41867         
41868         
41869         /**
41870      * @cfg {Date/String} zeroValue
41871      * if the date is less that this number, then the field is rendered as empty
41872      * default is 1800
41873      */
41874         zeroValue : '1800-01-01',
41875         
41876         
41877     /**
41878      * @cfg {Date/String} minValue
41879      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41880      * valid format (defaults to null).
41881      */
41882     minValue : null,
41883     /**
41884      * @cfg {Date/String} maxValue
41885      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41886      * valid format (defaults to null).
41887      */
41888     maxValue : null,
41889     /**
41890      * @cfg {String} minText
41891      * The error text to display when the date in the cell is before minValue (defaults to
41892      * 'The date in this field must be after {minValue}').
41893      */
41894     minText : "The date in this field must be equal to or after {0}",
41895     /**
41896      * @cfg {String} maxText
41897      * The error text to display when the date in the cell is after maxValue (defaults to
41898      * 'The date in this field must be before {maxValue}').
41899      */
41900     maxText : "The date in this field must be equal to or before {0}",
41901     /**
41902      * @cfg {String} invalidText
41903      * The error text to display when the date in the field is invalid (defaults to
41904      * '{value} is not a valid date - it must be in the format {format}').
41905      */
41906     invalidText : "{0} is not a valid date - it must be in the format {1}",
41907     /**
41908      * @cfg {String} triggerClass
41909      * An additional CSS class used to style the trigger button.  The trigger will always get the
41910      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41911      * which displays a calendar icon).
41912      */
41913     triggerClass : 'x-form-date-trigger',
41914     
41915
41916     /**
41917      * @cfg {Boolean} useIso
41918      * if enabled, then the date field will use a hidden field to store the 
41919      * real value as iso formated date. default (false)
41920      */ 
41921     useIso : false,
41922     /**
41923      * @cfg {String/Object} autoCreate
41924      * A DomHelper element spec, or true for a default element spec (defaults to
41925      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41926      */ 
41927     // private
41928     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41929     
41930     // private
41931     hiddenField: false,
41932     
41933     onRender : function(ct, position)
41934     {
41935         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41936         if (this.useIso) {
41937             //this.el.dom.removeAttribute('name'); 
41938             Roo.log("Changing name?");
41939             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41940             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41941                     'before', true);
41942             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41943             // prevent input submission
41944             this.hiddenName = this.name;
41945         }
41946             
41947             
41948     },
41949     
41950     // private
41951     validateValue : function(value)
41952     {
41953         value = this.formatDate(value);
41954         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41955             Roo.log('super failed');
41956             return false;
41957         }
41958         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41959              return true;
41960         }
41961         var svalue = value;
41962         value = this.parseDate(value);
41963         if(!value){
41964             Roo.log('parse date failed' + svalue);
41965             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41966             return false;
41967         }
41968         var time = value.getTime();
41969         if(this.minValue && time < this.minValue.getTime()){
41970             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41971             return false;
41972         }
41973         if(this.maxValue && time > this.maxValue.getTime()){
41974             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41975             return false;
41976         }
41977         if(this.disabledDays){
41978             var day = value.getDay();
41979             for(var i = 0; i < this.disabledDays.length; i++) {
41980                 if(day === this.disabledDays[i]){
41981                     this.markInvalid(this.disabledDaysText);
41982                     return false;
41983                 }
41984             }
41985         }
41986         var fvalue = this.formatDate(value);
41987         if(this.ddMatch && this.ddMatch.test(fvalue)){
41988             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41989             return false;
41990         }
41991         return true;
41992     },
41993
41994     // private
41995     // Provides logic to override the default TriggerField.validateBlur which just returns true
41996     validateBlur : function(){
41997         return !this.menu || !this.menu.isVisible();
41998     },
41999     
42000     getName: function()
42001     {
42002         // returns hidden if it's set..
42003         if (!this.rendered) {return ''};
42004         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42005         
42006     },
42007
42008     /**
42009      * Returns the current date value of the date field.
42010      * @return {Date} The date value
42011      */
42012     getValue : function(){
42013         
42014         return  this.hiddenField ?
42015                 this.hiddenField.value :
42016                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42017     },
42018
42019     /**
42020      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42021      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42022      * (the default format used is "m/d/y").
42023      * <br />Usage:
42024      * <pre><code>
42025 //All of these calls set the same date value (May 4, 2006)
42026
42027 //Pass a date object:
42028 var dt = new Date('5/4/06');
42029 dateField.setValue(dt);
42030
42031 //Pass a date string (default format):
42032 dateField.setValue('5/4/06');
42033
42034 //Pass a date string (custom format):
42035 dateField.format = 'Y-m-d';
42036 dateField.setValue('2006-5-4');
42037 </code></pre>
42038      * @param {String/Date} date The date or valid date string
42039      */
42040     setValue : function(date){
42041         if (this.hiddenField) {
42042             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42043         }
42044         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42045         // make sure the value field is always stored as a date..
42046         this.value = this.parseDate(date);
42047         
42048         
42049     },
42050
42051     // private
42052     parseDate : function(value){
42053                 
42054                 if (value instanceof Date) {
42055                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42056                                 return  '';
42057                         }
42058                         return value;
42059                 }
42060                 
42061                 
42062         if(!value || value instanceof Date){
42063             return value;
42064         }
42065         var v = Date.parseDate(value, this.format);
42066          if (!v && this.useIso) {
42067             v = Date.parseDate(value, 'Y-m-d');
42068         }
42069         if(!v && this.altFormats){
42070             if(!this.altFormatsArray){
42071                 this.altFormatsArray = this.altFormats.split("|");
42072             }
42073             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42074                 v = Date.parseDate(value, this.altFormatsArray[i]);
42075             }
42076         }
42077                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42078                         v = '';
42079                 }
42080         return v;
42081     },
42082
42083     // private
42084     formatDate : function(date, fmt){
42085         return (!date || !(date instanceof Date)) ?
42086                date : date.dateFormat(fmt || this.format);
42087     },
42088
42089     // private
42090     menuListeners : {
42091         select: function(m, d){
42092             
42093             this.setValue(d);
42094             this.fireEvent('select', this, d);
42095         },
42096         show : function(){ // retain focus styling
42097             this.onFocus();
42098         },
42099         hide : function(){
42100             this.focus.defer(10, this);
42101             var ml = this.menuListeners;
42102             this.menu.un("select", ml.select,  this);
42103             this.menu.un("show", ml.show,  this);
42104             this.menu.un("hide", ml.hide,  this);
42105         }
42106     },
42107
42108     // private
42109     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42110     onTriggerClick : function(){
42111         if(this.disabled){
42112             return;
42113         }
42114         if(this.menu == null){
42115             this.menu = new Roo.menu.DateMenu();
42116         }
42117         Roo.apply(this.menu.picker,  {
42118             showClear: this.allowBlank,
42119             minDate : this.minValue,
42120             maxDate : this.maxValue,
42121             disabledDatesRE : this.ddMatch,
42122             disabledDatesText : this.disabledDatesText,
42123             disabledDays : this.disabledDays,
42124             disabledDaysText : this.disabledDaysText,
42125             format : this.useIso ? 'Y-m-d' : this.format,
42126             minText : String.format(this.minText, this.formatDate(this.minValue)),
42127             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42128         });
42129         this.menu.on(Roo.apply({}, this.menuListeners, {
42130             scope:this
42131         }));
42132         this.menu.picker.setValue(this.getValue() || new Date());
42133         this.menu.show(this.el, "tl-bl?");
42134     },
42135
42136     beforeBlur : function(){
42137         var v = this.parseDate(this.getRawValue());
42138         if(v){
42139             this.setValue(v);
42140         }
42141     },
42142
42143     /*@
42144      * overide
42145      * 
42146      */
42147     isDirty : function() {
42148         if(this.disabled) {
42149             return false;
42150         }
42151         
42152         if(typeof(this.startValue) === 'undefined'){
42153             return false;
42154         }
42155         
42156         return String(this.getValue()) !== String(this.startValue);
42157         
42158     },
42159     // @overide
42160     cleanLeadingSpace : function(e)
42161     {
42162        return;
42163     }
42164     
42165 });/*
42166  * Based on:
42167  * Ext JS Library 1.1.1
42168  * Copyright(c) 2006-2007, Ext JS, LLC.
42169  *
42170  * Originally Released Under LGPL - original licence link has changed is not relivant.
42171  *
42172  * Fork - LGPL
42173  * <script type="text/javascript">
42174  */
42175  
42176 /**
42177  * @class Roo.form.MonthField
42178  * @extends Roo.form.TriggerField
42179  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42180 * @constructor
42181 * Create a new MonthField
42182 * @param {Object} config
42183  */
42184 Roo.form.MonthField = function(config){
42185     
42186     Roo.form.MonthField.superclass.constructor.call(this, config);
42187     
42188       this.addEvents({
42189          
42190         /**
42191          * @event select
42192          * Fires when a date is selected
42193              * @param {Roo.form.MonthFieeld} combo This combo box
42194              * @param {Date} date The date selected
42195              */
42196         'select' : true
42197          
42198     });
42199     
42200     
42201     if(typeof this.minValue == "string") {
42202         this.minValue = this.parseDate(this.minValue);
42203     }
42204     if(typeof this.maxValue == "string") {
42205         this.maxValue = this.parseDate(this.maxValue);
42206     }
42207     this.ddMatch = null;
42208     if(this.disabledDates){
42209         var dd = this.disabledDates;
42210         var re = "(?:";
42211         for(var i = 0; i < dd.length; i++){
42212             re += dd[i];
42213             if(i != dd.length-1) {
42214                 re += "|";
42215             }
42216         }
42217         this.ddMatch = new RegExp(re + ")");
42218     }
42219 };
42220
42221 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42222     /**
42223      * @cfg {String} format
42224      * The default date format string which can be overriden for localization support.  The format must be
42225      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42226      */
42227     format : "M Y",
42228     /**
42229      * @cfg {String} altFormats
42230      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42231      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42232      */
42233     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42234     /**
42235      * @cfg {Array} disabledDays
42236      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42237      */
42238     disabledDays : [0,1,2,3,4,5,6],
42239     /**
42240      * @cfg {String} disabledDaysText
42241      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42242      */
42243     disabledDaysText : "Disabled",
42244     /**
42245      * @cfg {Array} disabledDates
42246      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42247      * expression so they are very powerful. Some examples:
42248      * <ul>
42249      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42250      * <li>["03/08", "09/16"] would disable those days for every year</li>
42251      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42252      * <li>["03/../2006"] would disable every day in March 2006</li>
42253      * <li>["^03"] would disable every day in every March</li>
42254      * </ul>
42255      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42256      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42257      */
42258     disabledDates : null,
42259     /**
42260      * @cfg {String} disabledDatesText
42261      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42262      */
42263     disabledDatesText : "Disabled",
42264     /**
42265      * @cfg {Date/String} minValue
42266      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42267      * valid format (defaults to null).
42268      */
42269     minValue : null,
42270     /**
42271      * @cfg {Date/String} maxValue
42272      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42273      * valid format (defaults to null).
42274      */
42275     maxValue : null,
42276     /**
42277      * @cfg {String} minText
42278      * The error text to display when the date in the cell is before minValue (defaults to
42279      * 'The date in this field must be after {minValue}').
42280      */
42281     minText : "The date in this field must be equal to or after {0}",
42282     /**
42283      * @cfg {String} maxTextf
42284      * The error text to display when the date in the cell is after maxValue (defaults to
42285      * 'The date in this field must be before {maxValue}').
42286      */
42287     maxText : "The date in this field must be equal to or before {0}",
42288     /**
42289      * @cfg {String} invalidText
42290      * The error text to display when the date in the field is invalid (defaults to
42291      * '{value} is not a valid date - it must be in the format {format}').
42292      */
42293     invalidText : "{0} is not a valid date - it must be in the format {1}",
42294     /**
42295      * @cfg {String} triggerClass
42296      * An additional CSS class used to style the trigger button.  The trigger will always get the
42297      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42298      * which displays a calendar icon).
42299      */
42300     triggerClass : 'x-form-date-trigger',
42301     
42302
42303     /**
42304      * @cfg {Boolean} useIso
42305      * if enabled, then the date field will use a hidden field to store the 
42306      * real value as iso formated date. default (true)
42307      */ 
42308     useIso : true,
42309     /**
42310      * @cfg {String/Object} autoCreate
42311      * A DomHelper element spec, or true for a default element spec (defaults to
42312      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42313      */ 
42314     // private
42315     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42316     
42317     // private
42318     hiddenField: false,
42319     
42320     hideMonthPicker : false,
42321     
42322     onRender : function(ct, position)
42323     {
42324         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42325         if (this.useIso) {
42326             this.el.dom.removeAttribute('name'); 
42327             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42328                     'before', true);
42329             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42330             // prevent input submission
42331             this.hiddenName = this.name;
42332         }
42333             
42334             
42335     },
42336     
42337     // private
42338     validateValue : function(value)
42339     {
42340         value = this.formatDate(value);
42341         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42342             return false;
42343         }
42344         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42345              return true;
42346         }
42347         var svalue = value;
42348         value = this.parseDate(value);
42349         if(!value){
42350             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42351             return false;
42352         }
42353         var time = value.getTime();
42354         if(this.minValue && time < this.minValue.getTime()){
42355             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42356             return false;
42357         }
42358         if(this.maxValue && time > this.maxValue.getTime()){
42359             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42360             return false;
42361         }
42362         /*if(this.disabledDays){
42363             var day = value.getDay();
42364             for(var i = 0; i < this.disabledDays.length; i++) {
42365                 if(day === this.disabledDays[i]){
42366                     this.markInvalid(this.disabledDaysText);
42367                     return false;
42368                 }
42369             }
42370         }
42371         */
42372         var fvalue = this.formatDate(value);
42373         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42374             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42375             return false;
42376         }
42377         */
42378         return true;
42379     },
42380
42381     // private
42382     // Provides logic to override the default TriggerField.validateBlur which just returns true
42383     validateBlur : function(){
42384         return !this.menu || !this.menu.isVisible();
42385     },
42386
42387     /**
42388      * Returns the current date value of the date field.
42389      * @return {Date} The date value
42390      */
42391     getValue : function(){
42392         
42393         
42394         
42395         return  this.hiddenField ?
42396                 this.hiddenField.value :
42397                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42398     },
42399
42400     /**
42401      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42402      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42403      * (the default format used is "m/d/y").
42404      * <br />Usage:
42405      * <pre><code>
42406 //All of these calls set the same date value (May 4, 2006)
42407
42408 //Pass a date object:
42409 var dt = new Date('5/4/06');
42410 monthField.setValue(dt);
42411
42412 //Pass a date string (default format):
42413 monthField.setValue('5/4/06');
42414
42415 //Pass a date string (custom format):
42416 monthField.format = 'Y-m-d';
42417 monthField.setValue('2006-5-4');
42418 </code></pre>
42419      * @param {String/Date} date The date or valid date string
42420      */
42421     setValue : function(date){
42422         Roo.log('month setValue' + date);
42423         // can only be first of month..
42424         
42425         var val = this.parseDate(date);
42426         
42427         if (this.hiddenField) {
42428             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42429         }
42430         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42431         this.value = this.parseDate(date);
42432     },
42433
42434     // private
42435     parseDate : function(value){
42436         if(!value || value instanceof Date){
42437             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42438             return value;
42439         }
42440         var v = Date.parseDate(value, this.format);
42441         if (!v && this.useIso) {
42442             v = Date.parseDate(value, 'Y-m-d');
42443         }
42444         if (v) {
42445             // 
42446             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42447         }
42448         
42449         
42450         if(!v && this.altFormats){
42451             if(!this.altFormatsArray){
42452                 this.altFormatsArray = this.altFormats.split("|");
42453             }
42454             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42455                 v = Date.parseDate(value, this.altFormatsArray[i]);
42456             }
42457         }
42458         return v;
42459     },
42460
42461     // private
42462     formatDate : function(date, fmt){
42463         return (!date || !(date instanceof Date)) ?
42464                date : date.dateFormat(fmt || this.format);
42465     },
42466
42467     // private
42468     menuListeners : {
42469         select: function(m, d){
42470             this.setValue(d);
42471             this.fireEvent('select', this, d);
42472         },
42473         show : function(){ // retain focus styling
42474             this.onFocus();
42475         },
42476         hide : function(){
42477             this.focus.defer(10, this);
42478             var ml = this.menuListeners;
42479             this.menu.un("select", ml.select,  this);
42480             this.menu.un("show", ml.show,  this);
42481             this.menu.un("hide", ml.hide,  this);
42482         }
42483     },
42484     // private
42485     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42486     onTriggerClick : function(){
42487         if(this.disabled){
42488             return;
42489         }
42490         if(this.menu == null){
42491             this.menu = new Roo.menu.DateMenu();
42492            
42493         }
42494         
42495         Roo.apply(this.menu.picker,  {
42496             
42497             showClear: this.allowBlank,
42498             minDate : this.minValue,
42499             maxDate : this.maxValue,
42500             disabledDatesRE : this.ddMatch,
42501             disabledDatesText : this.disabledDatesText,
42502             
42503             format : this.useIso ? 'Y-m-d' : this.format,
42504             minText : String.format(this.minText, this.formatDate(this.minValue)),
42505             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42506             
42507         });
42508          this.menu.on(Roo.apply({}, this.menuListeners, {
42509             scope:this
42510         }));
42511        
42512         
42513         var m = this.menu;
42514         var p = m.picker;
42515         
42516         // hide month picker get's called when we called by 'before hide';
42517         
42518         var ignorehide = true;
42519         p.hideMonthPicker  = function(disableAnim){
42520             if (ignorehide) {
42521                 return;
42522             }
42523              if(this.monthPicker){
42524                 Roo.log("hideMonthPicker called");
42525                 if(disableAnim === true){
42526                     this.monthPicker.hide();
42527                 }else{
42528                     this.monthPicker.slideOut('t', {duration:.2});
42529                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42530                     p.fireEvent("select", this, this.value);
42531                     m.hide();
42532                 }
42533             }
42534         }
42535         
42536         Roo.log('picker set value');
42537         Roo.log(this.getValue());
42538         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42539         m.show(this.el, 'tl-bl?');
42540         ignorehide  = false;
42541         // this will trigger hideMonthPicker..
42542         
42543         
42544         // hidden the day picker
42545         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42546         
42547         
42548         
42549       
42550         
42551         p.showMonthPicker.defer(100, p);
42552     
42553         
42554        
42555     },
42556
42557     beforeBlur : function(){
42558         var v = this.parseDate(this.getRawValue());
42559         if(v){
42560             this.setValue(v);
42561         }
42562     }
42563
42564     /** @cfg {Boolean} grow @hide */
42565     /** @cfg {Number} growMin @hide */
42566     /** @cfg {Number} growMax @hide */
42567     /**
42568      * @hide
42569      * @method autoSize
42570      */
42571 });/*
42572  * Based on:
42573  * Ext JS Library 1.1.1
42574  * Copyright(c) 2006-2007, Ext JS, LLC.
42575  *
42576  * Originally Released Under LGPL - original licence link has changed is not relivant.
42577  *
42578  * Fork - LGPL
42579  * <script type="text/javascript">
42580  */
42581  
42582
42583 /**
42584  * @class Roo.form.ComboBox
42585  * @extends Roo.form.TriggerField
42586  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42587  * @constructor
42588  * Create a new ComboBox.
42589  * @param {Object} config Configuration options
42590  */
42591 Roo.form.ComboBox = function(config){
42592     Roo.form.ComboBox.superclass.constructor.call(this, config);
42593     this.addEvents({
42594         /**
42595          * @event expand
42596          * Fires when the dropdown list is expanded
42597              * @param {Roo.form.ComboBox} combo This combo box
42598              */
42599         'expand' : true,
42600         /**
42601          * @event collapse
42602          * Fires when the dropdown list is collapsed
42603              * @param {Roo.form.ComboBox} combo This combo box
42604              */
42605         'collapse' : true,
42606         /**
42607          * @event beforeselect
42608          * Fires before a list item is selected. Return false to cancel the selection.
42609              * @param {Roo.form.ComboBox} combo This combo box
42610              * @param {Roo.data.Record} record The data record returned from the underlying store
42611              * @param {Number} index The index of the selected item in the dropdown list
42612              */
42613         'beforeselect' : true,
42614         /**
42615          * @event select
42616          * Fires when a list item is selected
42617              * @param {Roo.form.ComboBox} combo This combo box
42618              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42619              * @param {Number} index The index of the selected item in the dropdown list
42620              */
42621         'select' : true,
42622         /**
42623          * @event beforequery
42624          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42625          * The event object passed has these properties:
42626              * @param {Roo.form.ComboBox} combo This combo box
42627              * @param {String} query The query
42628              * @param {Boolean} forceAll true to force "all" query
42629              * @param {Boolean} cancel true to cancel the query
42630              * @param {Object} e The query event object
42631              */
42632         'beforequery': true,
42633          /**
42634          * @event add
42635          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42636              * @param {Roo.form.ComboBox} combo This combo box
42637              */
42638         'add' : true,
42639         /**
42640          * @event edit
42641          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42642              * @param {Roo.form.ComboBox} combo This combo box
42643              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42644              */
42645         'edit' : true
42646         
42647         
42648     });
42649     if(this.transform){
42650         this.allowDomMove = false;
42651         var s = Roo.getDom(this.transform);
42652         if(!this.hiddenName){
42653             this.hiddenName = s.name;
42654         }
42655         if(!this.store){
42656             this.mode = 'local';
42657             var d = [], opts = s.options;
42658             for(var i = 0, len = opts.length;i < len; i++){
42659                 var o = opts[i];
42660                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42661                 if(o.selected) {
42662                     this.value = value;
42663                 }
42664                 d.push([value, o.text]);
42665             }
42666             this.store = new Roo.data.SimpleStore({
42667                 'id': 0,
42668                 fields: ['value', 'text'],
42669                 data : d
42670             });
42671             this.valueField = 'value';
42672             this.displayField = 'text';
42673         }
42674         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42675         if(!this.lazyRender){
42676             this.target = true;
42677             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42678             s.parentNode.removeChild(s); // remove it
42679             this.render(this.el.parentNode);
42680         }else{
42681             s.parentNode.removeChild(s); // remove it
42682         }
42683
42684     }
42685     if (this.store) {
42686         this.store = Roo.factory(this.store, Roo.data);
42687     }
42688     
42689     this.selectedIndex = -1;
42690     if(this.mode == 'local'){
42691         if(config.queryDelay === undefined){
42692             this.queryDelay = 10;
42693         }
42694         if(config.minChars === undefined){
42695             this.minChars = 0;
42696         }
42697     }
42698 };
42699
42700 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42701     /**
42702      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42703      */
42704     /**
42705      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42706      * rendering into an Roo.Editor, defaults to false)
42707      */
42708     /**
42709      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42710      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42711      */
42712     /**
42713      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42714      */
42715     /**
42716      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42717      * the dropdown list (defaults to undefined, with no header element)
42718      */
42719
42720      /**
42721      * @cfg {String/Roo.Template} tpl The template to use to render the output
42722      */
42723      
42724     // private
42725     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42726     /**
42727      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42728      */
42729     listWidth: undefined,
42730     /**
42731      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42732      * mode = 'remote' or 'text' if mode = 'local')
42733      */
42734     displayField: undefined,
42735     /**
42736      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42737      * mode = 'remote' or 'value' if mode = 'local'). 
42738      * Note: use of a valueField requires the user make a selection
42739      * in order for a value to be mapped.
42740      */
42741     valueField: undefined,
42742     
42743     
42744     /**
42745      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42746      * field's data value (defaults to the underlying DOM element's name)
42747      */
42748     hiddenName: undefined,
42749     /**
42750      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42751      */
42752     listClass: '',
42753     /**
42754      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42755      */
42756     selectedClass: 'x-combo-selected',
42757     /**
42758      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42759      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42760      * which displays a downward arrow icon).
42761      */
42762     triggerClass : 'x-form-arrow-trigger',
42763     /**
42764      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42765      */
42766     shadow:'sides',
42767     /**
42768      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42769      * anchor positions (defaults to 'tl-bl')
42770      */
42771     listAlign: 'tl-bl?',
42772     /**
42773      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42774      */
42775     maxHeight: 300,
42776     /**
42777      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42778      * query specified by the allQuery config option (defaults to 'query')
42779      */
42780     triggerAction: 'query',
42781     /**
42782      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42783      * (defaults to 4, does not apply if editable = false)
42784      */
42785     minChars : 4,
42786     /**
42787      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42788      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42789      */
42790     typeAhead: false,
42791     /**
42792      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42793      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42794      */
42795     queryDelay: 500,
42796     /**
42797      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42798      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42799      */
42800     pageSize: 0,
42801     /**
42802      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42803      * when editable = true (defaults to false)
42804      */
42805     selectOnFocus:false,
42806     /**
42807      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42808      */
42809     queryParam: 'query',
42810     /**
42811      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42812      * when mode = 'remote' (defaults to 'Loading...')
42813      */
42814     loadingText: 'Loading...',
42815     /**
42816      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42817      */
42818     resizable: false,
42819     /**
42820      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42821      */
42822     handleHeight : 8,
42823     /**
42824      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42825      * traditional select (defaults to true)
42826      */
42827     editable: true,
42828     /**
42829      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42830      */
42831     allQuery: '',
42832     /**
42833      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42834      */
42835     mode: 'remote',
42836     /**
42837      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42838      * listWidth has a higher value)
42839      */
42840     minListWidth : 70,
42841     /**
42842      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42843      * allow the user to set arbitrary text into the field (defaults to false)
42844      */
42845     forceSelection:false,
42846     /**
42847      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42848      * if typeAhead = true (defaults to 250)
42849      */
42850     typeAheadDelay : 250,
42851     /**
42852      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42853      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42854      */
42855     valueNotFoundText : undefined,
42856     /**
42857      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42858      */
42859     blockFocus : false,
42860     
42861     /**
42862      * @cfg {Boolean} disableClear Disable showing of clear button.
42863      */
42864     disableClear : false,
42865     /**
42866      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42867      */
42868     alwaysQuery : false,
42869     
42870     //private
42871     addicon : false,
42872     editicon: false,
42873     
42874     // element that contains real text value.. (when hidden is used..)
42875      
42876     // private
42877     onRender : function(ct, position)
42878     {
42879         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42880         
42881         if(this.hiddenName){
42882             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42883                     'before', true);
42884             this.hiddenField.value =
42885                 this.hiddenValue !== undefined ? this.hiddenValue :
42886                 this.value !== undefined ? this.value : '';
42887
42888             // prevent input submission
42889             this.el.dom.removeAttribute('name');
42890              
42891              
42892         }
42893         
42894         if(Roo.isGecko){
42895             this.el.dom.setAttribute('autocomplete', 'off');
42896         }
42897
42898         var cls = 'x-combo-list';
42899
42900         this.list = new Roo.Layer({
42901             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42902         });
42903
42904         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42905         this.list.setWidth(lw);
42906         this.list.swallowEvent('mousewheel');
42907         this.assetHeight = 0;
42908
42909         if(this.title){
42910             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42911             this.assetHeight += this.header.getHeight();
42912         }
42913
42914         this.innerList = this.list.createChild({cls:cls+'-inner'});
42915         this.innerList.on('mouseover', this.onViewOver, this);
42916         this.innerList.on('mousemove', this.onViewMove, this);
42917         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42918         
42919         if(this.allowBlank && !this.pageSize && !this.disableClear){
42920             this.footer = this.list.createChild({cls:cls+'-ft'});
42921             this.pageTb = new Roo.Toolbar(this.footer);
42922            
42923         }
42924         if(this.pageSize){
42925             this.footer = this.list.createChild({cls:cls+'-ft'});
42926             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42927                     {pageSize: this.pageSize});
42928             
42929         }
42930         
42931         if (this.pageTb && this.allowBlank && !this.disableClear) {
42932             var _this = this;
42933             this.pageTb.add(new Roo.Toolbar.Fill(), {
42934                 cls: 'x-btn-icon x-btn-clear',
42935                 text: '&#160;',
42936                 handler: function()
42937                 {
42938                     _this.collapse();
42939                     _this.clearValue();
42940                     _this.onSelect(false, -1);
42941                 }
42942             });
42943         }
42944         if (this.footer) {
42945             this.assetHeight += this.footer.getHeight();
42946         }
42947         
42948
42949         if(!this.tpl){
42950             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42951         }
42952
42953         this.view = new Roo.View(this.innerList, this.tpl, {
42954             singleSelect:true,
42955             store: this.store,
42956             selectedClass: this.selectedClass
42957         });
42958
42959         this.view.on('click', this.onViewClick, this);
42960
42961         this.store.on('beforeload', this.onBeforeLoad, this);
42962         this.store.on('load', this.onLoad, this);
42963         this.store.on('loadexception', this.onLoadException, this);
42964
42965         if(this.resizable){
42966             this.resizer = new Roo.Resizable(this.list,  {
42967                pinned:true, handles:'se'
42968             });
42969             this.resizer.on('resize', function(r, w, h){
42970                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42971                 this.listWidth = w;
42972                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42973                 this.restrictHeight();
42974             }, this);
42975             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42976         }
42977         if(!this.editable){
42978             this.editable = true;
42979             this.setEditable(false);
42980         }  
42981         
42982         
42983         if (typeof(this.events.add.listeners) != 'undefined') {
42984             
42985             this.addicon = this.wrap.createChild(
42986                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42987        
42988             this.addicon.on('click', function(e) {
42989                 this.fireEvent('add', this);
42990             }, this);
42991         }
42992         if (typeof(this.events.edit.listeners) != 'undefined') {
42993             
42994             this.editicon = this.wrap.createChild(
42995                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42996             if (this.addicon) {
42997                 this.editicon.setStyle('margin-left', '40px');
42998             }
42999             this.editicon.on('click', function(e) {
43000                 
43001                 // we fire even  if inothing is selected..
43002                 this.fireEvent('edit', this, this.lastData );
43003                 
43004             }, this);
43005         }
43006         
43007         
43008         
43009     },
43010
43011     // private
43012     initEvents : function(){
43013         Roo.form.ComboBox.superclass.initEvents.call(this);
43014
43015         this.keyNav = new Roo.KeyNav(this.el, {
43016             "up" : function(e){
43017                 this.inKeyMode = true;
43018                 this.selectPrev();
43019             },
43020
43021             "down" : function(e){
43022                 if(!this.isExpanded()){
43023                     this.onTriggerClick();
43024                 }else{
43025                     this.inKeyMode = true;
43026                     this.selectNext();
43027                 }
43028             },
43029
43030             "enter" : function(e){
43031                 this.onViewClick();
43032                 //return true;
43033             },
43034
43035             "esc" : function(e){
43036                 this.collapse();
43037             },
43038
43039             "tab" : function(e){
43040                 this.onViewClick(false);
43041                 this.fireEvent("specialkey", this, e);
43042                 return true;
43043             },
43044
43045             scope : this,
43046
43047             doRelay : function(foo, bar, hname){
43048                 if(hname == 'down' || this.scope.isExpanded()){
43049                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43050                 }
43051                 return true;
43052             },
43053
43054             forceKeyDown: true
43055         });
43056         this.queryDelay = Math.max(this.queryDelay || 10,
43057                 this.mode == 'local' ? 10 : 250);
43058         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43059         if(this.typeAhead){
43060             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43061         }
43062         if(this.editable !== false){
43063             this.el.on("keyup", this.onKeyUp, this);
43064         }
43065         if(this.forceSelection){
43066             this.on('blur', this.doForce, this);
43067         }
43068     },
43069
43070     onDestroy : function(){
43071         if(this.view){
43072             this.view.setStore(null);
43073             this.view.el.removeAllListeners();
43074             this.view.el.remove();
43075             this.view.purgeListeners();
43076         }
43077         if(this.list){
43078             this.list.destroy();
43079         }
43080         if(this.store){
43081             this.store.un('beforeload', this.onBeforeLoad, this);
43082             this.store.un('load', this.onLoad, this);
43083             this.store.un('loadexception', this.onLoadException, this);
43084         }
43085         Roo.form.ComboBox.superclass.onDestroy.call(this);
43086     },
43087
43088     // private
43089     fireKey : function(e){
43090         if(e.isNavKeyPress() && !this.list.isVisible()){
43091             this.fireEvent("specialkey", this, e);
43092         }
43093     },
43094
43095     // private
43096     onResize: function(w, h){
43097         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43098         
43099         if(typeof w != 'number'){
43100             // we do not handle it!?!?
43101             return;
43102         }
43103         var tw = this.trigger.getWidth();
43104         tw += this.addicon ? this.addicon.getWidth() : 0;
43105         tw += this.editicon ? this.editicon.getWidth() : 0;
43106         var x = w - tw;
43107         this.el.setWidth( this.adjustWidth('input', x));
43108             
43109         this.trigger.setStyle('left', x+'px');
43110         
43111         if(this.list && this.listWidth === undefined){
43112             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43113             this.list.setWidth(lw);
43114             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43115         }
43116         
43117     
43118         
43119     },
43120
43121     /**
43122      * Allow or prevent the user from directly editing the field text.  If false is passed,
43123      * the user will only be able to select from the items defined in the dropdown list.  This method
43124      * is the runtime equivalent of setting the 'editable' config option at config time.
43125      * @param {Boolean} value True to allow the user to directly edit the field text
43126      */
43127     setEditable : function(value){
43128         if(value == this.editable){
43129             return;
43130         }
43131         this.editable = value;
43132         if(!value){
43133             this.el.dom.setAttribute('readOnly', true);
43134             this.el.on('mousedown', this.onTriggerClick,  this);
43135             this.el.addClass('x-combo-noedit');
43136         }else{
43137             this.el.dom.setAttribute('readOnly', false);
43138             this.el.un('mousedown', this.onTriggerClick,  this);
43139             this.el.removeClass('x-combo-noedit');
43140         }
43141     },
43142
43143     // private
43144     onBeforeLoad : function(){
43145         if(!this.hasFocus){
43146             return;
43147         }
43148         this.innerList.update(this.loadingText ?
43149                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43150         this.restrictHeight();
43151         this.selectedIndex = -1;
43152     },
43153
43154     // private
43155     onLoad : function(){
43156         if(!this.hasFocus){
43157             return;
43158         }
43159         if(this.store.getCount() > 0){
43160             this.expand();
43161             this.restrictHeight();
43162             if(this.lastQuery == this.allQuery){
43163                 if(this.editable){
43164                     this.el.dom.select();
43165                 }
43166                 if(!this.selectByValue(this.value, true)){
43167                     this.select(0, true);
43168                 }
43169             }else{
43170                 this.selectNext();
43171                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43172                     this.taTask.delay(this.typeAheadDelay);
43173                 }
43174             }
43175         }else{
43176             this.onEmptyResults();
43177         }
43178         //this.el.focus();
43179     },
43180     // private
43181     onLoadException : function()
43182     {
43183         this.collapse();
43184         Roo.log(this.store.reader.jsonData);
43185         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43186             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43187         }
43188         
43189         
43190     },
43191     // private
43192     onTypeAhead : function(){
43193         if(this.store.getCount() > 0){
43194             var r = this.store.getAt(0);
43195             var newValue = r.data[this.displayField];
43196             var len = newValue.length;
43197             var selStart = this.getRawValue().length;
43198             if(selStart != len){
43199                 this.setRawValue(newValue);
43200                 this.selectText(selStart, newValue.length);
43201             }
43202         }
43203     },
43204
43205     // private
43206     onSelect : function(record, index){
43207         if(this.fireEvent('beforeselect', this, record, index) !== false){
43208             this.setFromData(index > -1 ? record.data : false);
43209             this.collapse();
43210             this.fireEvent('select', this, record, index);
43211         }
43212     },
43213
43214     /**
43215      * Returns the currently selected field value or empty string if no value is set.
43216      * @return {String} value The selected value
43217      */
43218     getValue : function(){
43219         if(this.valueField){
43220             return typeof this.value != 'undefined' ? this.value : '';
43221         }
43222         return Roo.form.ComboBox.superclass.getValue.call(this);
43223     },
43224
43225     /**
43226      * Clears any text/value currently set in the field
43227      */
43228     clearValue : function(){
43229         if(this.hiddenField){
43230             this.hiddenField.value = '';
43231         }
43232         this.value = '';
43233         this.setRawValue('');
43234         this.lastSelectionText = '';
43235         
43236     },
43237
43238     /**
43239      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43240      * will be displayed in the field.  If the value does not match the data value of an existing item,
43241      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43242      * Otherwise the field will be blank (although the value will still be set).
43243      * @param {String} value The value to match
43244      */
43245     setValue : function(v){
43246         var text = v;
43247         if(this.valueField){
43248             var r = this.findRecord(this.valueField, v);
43249             if(r){
43250                 text = r.data[this.displayField];
43251             }else if(this.valueNotFoundText !== undefined){
43252                 text = this.valueNotFoundText;
43253             }
43254         }
43255         this.lastSelectionText = text;
43256         if(this.hiddenField){
43257             this.hiddenField.value = v;
43258         }
43259         Roo.form.ComboBox.superclass.setValue.call(this, text);
43260         this.value = v;
43261     },
43262     /**
43263      * @property {Object} the last set data for the element
43264      */
43265     
43266     lastData : false,
43267     /**
43268      * Sets the value of the field based on a object which is related to the record format for the store.
43269      * @param {Object} value the value to set as. or false on reset?
43270      */
43271     setFromData : function(o){
43272         var dv = ''; // display value
43273         var vv = ''; // value value..
43274         this.lastData = o;
43275         if (this.displayField) {
43276             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43277         } else {
43278             // this is an error condition!!!
43279             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43280         }
43281         
43282         if(this.valueField){
43283             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43284         }
43285         if(this.hiddenField){
43286             this.hiddenField.value = vv;
43287             
43288             this.lastSelectionText = dv;
43289             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43290             this.value = vv;
43291             return;
43292         }
43293         // no hidden field.. - we store the value in 'value', but still display
43294         // display field!!!!
43295         this.lastSelectionText = dv;
43296         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43297         this.value = vv;
43298         
43299         
43300     },
43301     // private
43302     reset : function(){
43303         // overridden so that last data is reset..
43304         this.setValue(this.resetValue);
43305         this.originalValue = this.getValue();
43306         this.clearInvalid();
43307         this.lastData = false;
43308         if (this.view) {
43309             this.view.clearSelections();
43310         }
43311     },
43312     // private
43313     findRecord : function(prop, value){
43314         var record;
43315         if(this.store.getCount() > 0){
43316             this.store.each(function(r){
43317                 if(r.data[prop] == value){
43318                     record = r;
43319                     return false;
43320                 }
43321                 return true;
43322             });
43323         }
43324         return record;
43325     },
43326     
43327     getName: function()
43328     {
43329         // returns hidden if it's set..
43330         if (!this.rendered) {return ''};
43331         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43332         
43333     },
43334     // private
43335     onViewMove : function(e, t){
43336         this.inKeyMode = false;
43337     },
43338
43339     // private
43340     onViewOver : function(e, t){
43341         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43342             return;
43343         }
43344         var item = this.view.findItemFromChild(t);
43345         if(item){
43346             var index = this.view.indexOf(item);
43347             this.select(index, false);
43348         }
43349     },
43350
43351     // private
43352     onViewClick : function(doFocus)
43353     {
43354         var index = this.view.getSelectedIndexes()[0];
43355         var r = this.store.getAt(index);
43356         if(r){
43357             this.onSelect(r, index);
43358         }
43359         if(doFocus !== false && !this.blockFocus){
43360             this.el.focus();
43361         }
43362     },
43363
43364     // private
43365     restrictHeight : function(){
43366         this.innerList.dom.style.height = '';
43367         var inner = this.innerList.dom;
43368         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43369         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43370         this.list.beginUpdate();
43371         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43372         this.list.alignTo(this.el, this.listAlign);
43373         this.list.endUpdate();
43374     },
43375
43376     // private
43377     onEmptyResults : function(){
43378         this.collapse();
43379     },
43380
43381     /**
43382      * Returns true if the dropdown list is expanded, else false.
43383      */
43384     isExpanded : function(){
43385         return this.list.isVisible();
43386     },
43387
43388     /**
43389      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43390      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43391      * @param {String} value The data value of the item to select
43392      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43393      * selected item if it is not currently in view (defaults to true)
43394      * @return {Boolean} True if the value matched an item in the list, else false
43395      */
43396     selectByValue : function(v, scrollIntoView){
43397         if(v !== undefined && v !== null){
43398             var r = this.findRecord(this.valueField || this.displayField, v);
43399             if(r){
43400                 this.select(this.store.indexOf(r), scrollIntoView);
43401                 return true;
43402             }
43403         }
43404         return false;
43405     },
43406
43407     /**
43408      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43409      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43410      * @param {Number} index The zero-based index of the list item to select
43411      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43412      * selected item if it is not currently in view (defaults to true)
43413      */
43414     select : function(index, scrollIntoView){
43415         this.selectedIndex = index;
43416         this.view.select(index);
43417         if(scrollIntoView !== false){
43418             var el = this.view.getNode(index);
43419             if(el){
43420                 this.innerList.scrollChildIntoView(el, false);
43421             }
43422         }
43423     },
43424
43425     // private
43426     selectNext : function(){
43427         var ct = this.store.getCount();
43428         if(ct > 0){
43429             if(this.selectedIndex == -1){
43430                 this.select(0);
43431             }else if(this.selectedIndex < ct-1){
43432                 this.select(this.selectedIndex+1);
43433             }
43434         }
43435     },
43436
43437     // private
43438     selectPrev : function(){
43439         var ct = this.store.getCount();
43440         if(ct > 0){
43441             if(this.selectedIndex == -1){
43442                 this.select(0);
43443             }else if(this.selectedIndex != 0){
43444                 this.select(this.selectedIndex-1);
43445             }
43446         }
43447     },
43448
43449     // private
43450     onKeyUp : function(e){
43451         if(this.editable !== false && !e.isSpecialKey()){
43452             this.lastKey = e.getKey();
43453             this.dqTask.delay(this.queryDelay);
43454         }
43455     },
43456
43457     // private
43458     validateBlur : function(){
43459         return !this.list || !this.list.isVisible();   
43460     },
43461
43462     // private
43463     initQuery : function(){
43464         this.doQuery(this.getRawValue());
43465     },
43466
43467     // private
43468     doForce : function(){
43469         if(this.el.dom.value.length > 0){
43470             this.el.dom.value =
43471                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43472              
43473         }
43474     },
43475
43476     /**
43477      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43478      * query allowing the query action to be canceled if needed.
43479      * @param {String} query The SQL query to execute
43480      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43481      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43482      * saved in the current store (defaults to false)
43483      */
43484     doQuery : function(q, forceAll){
43485         if(q === undefined || q === null){
43486             q = '';
43487         }
43488         var qe = {
43489             query: q,
43490             forceAll: forceAll,
43491             combo: this,
43492             cancel:false
43493         };
43494         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43495             return false;
43496         }
43497         q = qe.query;
43498         forceAll = qe.forceAll;
43499         if(forceAll === true || (q.length >= this.minChars)){
43500             if(this.lastQuery != q || this.alwaysQuery){
43501                 this.lastQuery = q;
43502                 if(this.mode == 'local'){
43503                     this.selectedIndex = -1;
43504                     if(forceAll){
43505                         this.store.clearFilter();
43506                     }else{
43507                         this.store.filter(this.displayField, q);
43508                     }
43509                     this.onLoad();
43510                 }else{
43511                     this.store.baseParams[this.queryParam] = q;
43512                     this.store.load({
43513                         params: this.getParams(q)
43514                     });
43515                     this.expand();
43516                 }
43517             }else{
43518                 this.selectedIndex = -1;
43519                 this.onLoad();   
43520             }
43521         }
43522     },
43523
43524     // private
43525     getParams : function(q){
43526         var p = {};
43527         //p[this.queryParam] = q;
43528         if(this.pageSize){
43529             p.start = 0;
43530             p.limit = this.pageSize;
43531         }
43532         return p;
43533     },
43534
43535     /**
43536      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43537      */
43538     collapse : function(){
43539         if(!this.isExpanded()){
43540             return;
43541         }
43542         this.list.hide();
43543         Roo.get(document).un('mousedown', this.collapseIf, this);
43544         Roo.get(document).un('mousewheel', this.collapseIf, this);
43545         if (!this.editable) {
43546             Roo.get(document).un('keydown', this.listKeyPress, this);
43547         }
43548         this.fireEvent('collapse', this);
43549     },
43550
43551     // private
43552     collapseIf : function(e){
43553         if(!e.within(this.wrap) && !e.within(this.list)){
43554             this.collapse();
43555         }
43556     },
43557
43558     /**
43559      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43560      */
43561     expand : function(){
43562         if(this.isExpanded() || !this.hasFocus){
43563             return;
43564         }
43565         this.list.alignTo(this.el, this.listAlign);
43566         this.list.show();
43567         Roo.get(document).on('mousedown', this.collapseIf, this);
43568         Roo.get(document).on('mousewheel', this.collapseIf, this);
43569         if (!this.editable) {
43570             Roo.get(document).on('keydown', this.listKeyPress, this);
43571         }
43572         
43573         this.fireEvent('expand', this);
43574     },
43575
43576     // private
43577     // Implements the default empty TriggerField.onTriggerClick function
43578     onTriggerClick : function(){
43579         if(this.disabled){
43580             return;
43581         }
43582         if(this.isExpanded()){
43583             this.collapse();
43584             if (!this.blockFocus) {
43585                 this.el.focus();
43586             }
43587             
43588         }else {
43589             this.hasFocus = true;
43590             if(this.triggerAction == 'all') {
43591                 this.doQuery(this.allQuery, true);
43592             } else {
43593                 this.doQuery(this.getRawValue());
43594             }
43595             if (!this.blockFocus) {
43596                 this.el.focus();
43597             }
43598         }
43599     },
43600     listKeyPress : function(e)
43601     {
43602         //Roo.log('listkeypress');
43603         // scroll to first matching element based on key pres..
43604         if (e.isSpecialKey()) {
43605             return false;
43606         }
43607         var k = String.fromCharCode(e.getKey()).toUpperCase();
43608         //Roo.log(k);
43609         var match  = false;
43610         var csel = this.view.getSelectedNodes();
43611         var cselitem = false;
43612         if (csel.length) {
43613             var ix = this.view.indexOf(csel[0]);
43614             cselitem  = this.store.getAt(ix);
43615             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43616                 cselitem = false;
43617             }
43618             
43619         }
43620         
43621         this.store.each(function(v) { 
43622             if (cselitem) {
43623                 // start at existing selection.
43624                 if (cselitem.id == v.id) {
43625                     cselitem = false;
43626                 }
43627                 return;
43628             }
43629                 
43630             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43631                 match = this.store.indexOf(v);
43632                 return false;
43633             }
43634         }, this);
43635         
43636         if (match === false) {
43637             return true; // no more action?
43638         }
43639         // scroll to?
43640         this.view.select(match);
43641         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43642         sn.scrollIntoView(sn.dom.parentNode, false);
43643     } 
43644
43645     /** 
43646     * @cfg {Boolean} grow 
43647     * @hide 
43648     */
43649     /** 
43650     * @cfg {Number} growMin 
43651     * @hide 
43652     */
43653     /** 
43654     * @cfg {Number} growMax 
43655     * @hide 
43656     */
43657     /**
43658      * @hide
43659      * @method autoSize
43660      */
43661 });/*
43662  * Copyright(c) 2010-2012, Roo J Solutions Limited
43663  *
43664  * Licence LGPL
43665  *
43666  */
43667
43668 /**
43669  * @class Roo.form.ComboBoxArray
43670  * @extends Roo.form.TextField
43671  * A facebook style adder... for lists of email / people / countries  etc...
43672  * pick multiple items from a combo box, and shows each one.
43673  *
43674  *  Fred [x]  Brian [x]  [Pick another |v]
43675  *
43676  *
43677  *  For this to work: it needs various extra information
43678  *    - normal combo problay has
43679  *      name, hiddenName
43680  *    + displayField, valueField
43681  *
43682  *    For our purpose...
43683  *
43684  *
43685  *   If we change from 'extends' to wrapping...
43686  *   
43687  *  
43688  *
43689  
43690  
43691  * @constructor
43692  * Create a new ComboBoxArray.
43693  * @param {Object} config Configuration options
43694  */
43695  
43696
43697 Roo.form.ComboBoxArray = function(config)
43698 {
43699     this.addEvents({
43700         /**
43701          * @event beforeremove
43702          * Fires before remove the value from the list
43703              * @param {Roo.form.ComboBoxArray} _self This combo box array
43704              * @param {Roo.form.ComboBoxArray.Item} item removed item
43705              */
43706         'beforeremove' : true,
43707         /**
43708          * @event remove
43709          * Fires when remove the value from the list
43710              * @param {Roo.form.ComboBoxArray} _self This combo box array
43711              * @param {Roo.form.ComboBoxArray.Item} item removed item
43712              */
43713         'remove' : true
43714         
43715         
43716     });
43717     
43718     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43719     
43720     this.items = new Roo.util.MixedCollection(false);
43721     
43722     // construct the child combo...
43723     
43724     
43725     
43726     
43727    
43728     
43729 }
43730
43731  
43732 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43733
43734     /**
43735      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43736      */
43737     
43738     lastData : false,
43739     
43740     // behavies liek a hiddne field
43741     inputType:      'hidden',
43742     /**
43743      * @cfg {Number} width The width of the box that displays the selected element
43744      */ 
43745     width:          300,
43746
43747     
43748     
43749     /**
43750      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43751      */
43752     name : false,
43753     /**
43754      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43755      */
43756     hiddenName : false,
43757       /**
43758      * @cfg {String} seperator    The value seperator normally ',' 
43759      */
43760     seperator : ',',
43761     
43762     // private the array of items that are displayed..
43763     items  : false,
43764     // private - the hidden field el.
43765     hiddenEl : false,
43766     // private - the filed el..
43767     el : false,
43768     
43769     //validateValue : function() { return true; }, // all values are ok!
43770     //onAddClick: function() { },
43771     
43772     onRender : function(ct, position) 
43773     {
43774         
43775         // create the standard hidden element
43776         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43777         
43778         
43779         // give fake names to child combo;
43780         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43781         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43782         
43783         this.combo = Roo.factory(this.combo, Roo.form);
43784         this.combo.onRender(ct, position);
43785         if (typeof(this.combo.width) != 'undefined') {
43786             this.combo.onResize(this.combo.width,0);
43787         }
43788         
43789         this.combo.initEvents();
43790         
43791         // assigned so form know we need to do this..
43792         this.store          = this.combo.store;
43793         this.valueField     = this.combo.valueField;
43794         this.displayField   = this.combo.displayField ;
43795         
43796         
43797         this.combo.wrap.addClass('x-cbarray-grp');
43798         
43799         var cbwrap = this.combo.wrap.createChild(
43800             {tag: 'div', cls: 'x-cbarray-cb'},
43801             this.combo.el.dom
43802         );
43803         
43804              
43805         this.hiddenEl = this.combo.wrap.createChild({
43806             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43807         });
43808         this.el = this.combo.wrap.createChild({
43809             tag: 'input',  type:'hidden' , name: this.name, value : ''
43810         });
43811          //   this.el.dom.removeAttribute("name");
43812         
43813         
43814         this.outerWrap = this.combo.wrap;
43815         this.wrap = cbwrap;
43816         
43817         this.outerWrap.setWidth(this.width);
43818         this.outerWrap.dom.removeChild(this.el.dom);
43819         
43820         this.wrap.dom.appendChild(this.el.dom);
43821         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43822         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43823         
43824         this.combo.trigger.setStyle('position','relative');
43825         this.combo.trigger.setStyle('left', '0px');
43826         this.combo.trigger.setStyle('top', '2px');
43827         
43828         this.combo.el.setStyle('vertical-align', 'text-bottom');
43829         
43830         //this.trigger.setStyle('vertical-align', 'top');
43831         
43832         // this should use the code from combo really... on('add' ....)
43833         if (this.adder) {
43834             
43835         
43836             this.adder = this.outerWrap.createChild(
43837                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43838             var _t = this;
43839             this.adder.on('click', function(e) {
43840                 _t.fireEvent('adderclick', this, e);
43841             }, _t);
43842         }
43843         //var _t = this;
43844         //this.adder.on('click', this.onAddClick, _t);
43845         
43846         
43847         this.combo.on('select', function(cb, rec, ix) {
43848             this.addItem(rec.data);
43849             
43850             cb.setValue('');
43851             cb.el.dom.value = '';
43852             //cb.lastData = rec.data;
43853             // add to list
43854             
43855         }, this);
43856         
43857         
43858     },
43859     
43860     
43861     getName: function()
43862     {
43863         // returns hidden if it's set..
43864         if (!this.rendered) {return ''};
43865         return  this.hiddenName ? this.hiddenName : this.name;
43866         
43867     },
43868     
43869     
43870     onResize: function(w, h){
43871         
43872         return;
43873         // not sure if this is needed..
43874         //this.combo.onResize(w,h);
43875         
43876         if(typeof w != 'number'){
43877             // we do not handle it!?!?
43878             return;
43879         }
43880         var tw = this.combo.trigger.getWidth();
43881         tw += this.addicon ? this.addicon.getWidth() : 0;
43882         tw += this.editicon ? this.editicon.getWidth() : 0;
43883         var x = w - tw;
43884         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43885             
43886         this.combo.trigger.setStyle('left', '0px');
43887         
43888         if(this.list && this.listWidth === undefined){
43889             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43890             this.list.setWidth(lw);
43891             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43892         }
43893         
43894     
43895         
43896     },
43897     
43898     addItem: function(rec)
43899     {
43900         var valueField = this.combo.valueField;
43901         var displayField = this.combo.displayField;
43902         
43903         if (this.items.indexOfKey(rec[valueField]) > -1) {
43904             //console.log("GOT " + rec.data.id);
43905             return;
43906         }
43907         
43908         var x = new Roo.form.ComboBoxArray.Item({
43909             //id : rec[this.idField],
43910             data : rec,
43911             displayField : displayField ,
43912             tipField : displayField ,
43913             cb : this
43914         });
43915         // use the 
43916         this.items.add(rec[valueField],x);
43917         // add it before the element..
43918         this.updateHiddenEl();
43919         x.render(this.outerWrap, this.wrap.dom);
43920         // add the image handler..
43921     },
43922     
43923     updateHiddenEl : function()
43924     {
43925         this.validate();
43926         if (!this.hiddenEl) {
43927             return;
43928         }
43929         var ar = [];
43930         var idField = this.combo.valueField;
43931         
43932         this.items.each(function(f) {
43933             ar.push(f.data[idField]);
43934         });
43935         this.hiddenEl.dom.value = ar.join(this.seperator);
43936         this.validate();
43937     },
43938     
43939     reset : function()
43940     {
43941         this.items.clear();
43942         
43943         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43944            el.remove();
43945         });
43946         
43947         this.el.dom.value = '';
43948         if (this.hiddenEl) {
43949             this.hiddenEl.dom.value = '';
43950         }
43951         
43952     },
43953     getValue: function()
43954     {
43955         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43956     },
43957     setValue: function(v) // not a valid action - must use addItems..
43958     {
43959         
43960         this.reset();
43961          
43962         if (this.store.isLocal && (typeof(v) == 'string')) {
43963             // then we can use the store to find the values..
43964             // comma seperated at present.. this needs to allow JSON based encoding..
43965             this.hiddenEl.value  = v;
43966             var v_ar = [];
43967             Roo.each(v.split(this.seperator), function(k) {
43968                 Roo.log("CHECK " + this.valueField + ',' + k);
43969                 var li = this.store.query(this.valueField, k);
43970                 if (!li.length) {
43971                     return;
43972                 }
43973                 var add = {};
43974                 add[this.valueField] = k;
43975                 add[this.displayField] = li.item(0).data[this.displayField];
43976                 
43977                 this.addItem(add);
43978             }, this) 
43979              
43980         }
43981         if (typeof(v) == 'object' ) {
43982             // then let's assume it's an array of objects..
43983             Roo.each(v, function(l) {
43984                 var add = l;
43985                 if (typeof(l) == 'string') {
43986                     add = {};
43987                     add[this.valueField] = l;
43988                     add[this.displayField] = l
43989                 }
43990                 this.addItem(add);
43991             }, this);
43992              
43993         }
43994         
43995         
43996     },
43997     setFromData: function(v)
43998     {
43999         // this recieves an object, if setValues is called.
44000         this.reset();
44001         this.el.dom.value = v[this.displayField];
44002         this.hiddenEl.dom.value = v[this.valueField];
44003         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44004             return;
44005         }
44006         var kv = v[this.valueField];
44007         var dv = v[this.displayField];
44008         kv = typeof(kv) != 'string' ? '' : kv;
44009         dv = typeof(dv) != 'string' ? '' : dv;
44010         
44011         
44012         var keys = kv.split(this.seperator);
44013         var display = dv.split(this.seperator);
44014         for (var i = 0 ; i < keys.length; i++) {
44015             add = {};
44016             add[this.valueField] = keys[i];
44017             add[this.displayField] = display[i];
44018             this.addItem(add);
44019         }
44020       
44021         
44022     },
44023     
44024     /**
44025      * Validates the combox array value
44026      * @return {Boolean} True if the value is valid, else false
44027      */
44028     validate : function(){
44029         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44030             this.clearInvalid();
44031             return true;
44032         }
44033         return false;
44034     },
44035     
44036     validateValue : function(value){
44037         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44038         
44039     },
44040     
44041     /*@
44042      * overide
44043      * 
44044      */
44045     isDirty : function() {
44046         if(this.disabled) {
44047             return false;
44048         }
44049         
44050         try {
44051             var d = Roo.decode(String(this.originalValue));
44052         } catch (e) {
44053             return String(this.getValue()) !== String(this.originalValue);
44054         }
44055         
44056         var originalValue = [];
44057         
44058         for (var i = 0; i < d.length; i++){
44059             originalValue.push(d[i][this.valueField]);
44060         }
44061         
44062         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44063         
44064     }
44065     
44066 });
44067
44068
44069
44070 /**
44071  * @class Roo.form.ComboBoxArray.Item
44072  * @extends Roo.BoxComponent
44073  * A selected item in the list
44074  *  Fred [x]  Brian [x]  [Pick another |v]
44075  * 
44076  * @constructor
44077  * Create a new item.
44078  * @param {Object} config Configuration options
44079  */
44080  
44081 Roo.form.ComboBoxArray.Item = function(config) {
44082     config.id = Roo.id();
44083     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44084 }
44085
44086 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44087     data : {},
44088     cb: false,
44089     displayField : false,
44090     tipField : false,
44091     
44092     
44093     defaultAutoCreate : {
44094         tag: 'div',
44095         cls: 'x-cbarray-item',
44096         cn : [ 
44097             { tag: 'div' },
44098             {
44099                 tag: 'img',
44100                 width:16,
44101                 height : 16,
44102                 src : Roo.BLANK_IMAGE_URL ,
44103                 align: 'center'
44104             }
44105         ]
44106         
44107     },
44108     
44109  
44110     onRender : function(ct, position)
44111     {
44112         Roo.form.Field.superclass.onRender.call(this, ct, position);
44113         
44114         if(!this.el){
44115             var cfg = this.getAutoCreate();
44116             this.el = ct.createChild(cfg, position);
44117         }
44118         
44119         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44120         
44121         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44122             this.cb.renderer(this.data) :
44123             String.format('{0}',this.data[this.displayField]);
44124         
44125             
44126         this.el.child('div').dom.setAttribute('qtip',
44127                         String.format('{0}',this.data[this.tipField])
44128         );
44129         
44130         this.el.child('img').on('click', this.remove, this);
44131         
44132     },
44133    
44134     remove : function()
44135     {
44136         if(this.cb.disabled){
44137             return;
44138         }
44139         
44140         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44141             this.cb.items.remove(this);
44142             this.el.child('img').un('click', this.remove, this);
44143             this.el.remove();
44144             this.cb.updateHiddenEl();
44145
44146             this.cb.fireEvent('remove', this.cb, this);
44147         }
44148         
44149     }
44150 });/*
44151  * RooJS Library 1.1.1
44152  * Copyright(c) 2008-2011  Alan Knowles
44153  *
44154  * License - LGPL
44155  */
44156  
44157
44158 /**
44159  * @class Roo.form.ComboNested
44160  * @extends Roo.form.ComboBox
44161  * A combobox for that allows selection of nested items in a list,
44162  * eg.
44163  *
44164  *  Book
44165  *    -> red
44166  *    -> green
44167  *  Table
44168  *    -> square
44169  *      ->red
44170  *      ->green
44171  *    -> rectangle
44172  *      ->green
44173  *      
44174  * 
44175  * @constructor
44176  * Create a new ComboNested
44177  * @param {Object} config Configuration options
44178  */
44179 Roo.form.ComboNested = function(config){
44180     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44181     // should verify some data...
44182     // like
44183     // hiddenName = required..
44184     // displayField = required
44185     // valudField == required
44186     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44187     var _t = this;
44188     Roo.each(req, function(e) {
44189         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44190             throw "Roo.form.ComboNested : missing value for: " + e;
44191         }
44192     });
44193      
44194     
44195 };
44196
44197 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44198    
44199     /*
44200      * @config {Number} max Number of columns to show
44201      */
44202     
44203     maxColumns : 3,
44204    
44205     list : null, // the outermost div..
44206     innerLists : null, // the
44207     views : null,
44208     stores : null,
44209     // private
44210     loadingChildren : false,
44211     
44212     onRender : function(ct, position)
44213     {
44214         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44215         
44216         if(this.hiddenName){
44217             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44218                     'before', true);
44219             this.hiddenField.value =
44220                 this.hiddenValue !== undefined ? this.hiddenValue :
44221                 this.value !== undefined ? this.value : '';
44222
44223             // prevent input submission
44224             this.el.dom.removeAttribute('name');
44225              
44226              
44227         }
44228         
44229         if(Roo.isGecko){
44230             this.el.dom.setAttribute('autocomplete', 'off');
44231         }
44232
44233         var cls = 'x-combo-list';
44234
44235         this.list = new Roo.Layer({
44236             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44237         });
44238
44239         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44240         this.list.setWidth(lw);
44241         this.list.swallowEvent('mousewheel');
44242         this.assetHeight = 0;
44243
44244         if(this.title){
44245             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44246             this.assetHeight += this.header.getHeight();
44247         }
44248         this.innerLists = [];
44249         this.views = [];
44250         this.stores = [];
44251         for (var i =0 ; i < this.maxColumns; i++) {
44252             this.onRenderList( cls, i);
44253         }
44254         
44255         // always needs footer, as we are going to have an 'OK' button.
44256         this.footer = this.list.createChild({cls:cls+'-ft'});
44257         this.pageTb = new Roo.Toolbar(this.footer);  
44258         var _this = this;
44259         this.pageTb.add(  {
44260             
44261             text: 'Done',
44262             handler: function()
44263             {
44264                 _this.collapse();
44265             }
44266         });
44267         
44268         if ( this.allowBlank && !this.disableClear) {
44269             
44270             this.pageTb.add(new Roo.Toolbar.Fill(), {
44271                 cls: 'x-btn-icon x-btn-clear',
44272                 text: '&#160;',
44273                 handler: function()
44274                 {
44275                     _this.collapse();
44276                     _this.clearValue();
44277                     _this.onSelect(false, -1);
44278                 }
44279             });
44280         }
44281         if (this.footer) {
44282             this.assetHeight += this.footer.getHeight();
44283         }
44284         
44285     },
44286     onRenderList : function (  cls, i)
44287     {
44288         
44289         var lw = Math.floor(
44290                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44291         );
44292         
44293         this.list.setWidth(lw); // default to '1'
44294
44295         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44296         //il.on('mouseover', this.onViewOver, this, { list:  i });
44297         //il.on('mousemove', this.onViewMove, this, { list:  i });
44298         il.setWidth(lw);
44299         il.setStyle({ 'overflow-x' : 'hidden'});
44300
44301         if(!this.tpl){
44302             this.tpl = new Roo.Template({
44303                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44304                 isEmpty: function (value, allValues) {
44305                     //Roo.log(value);
44306                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44307                     return dl ? 'has-children' : 'no-children'
44308                 }
44309             });
44310         }
44311         
44312         var store  = this.store;
44313         if (i > 0) {
44314             store  = new Roo.data.SimpleStore({
44315                 //fields : this.store.reader.meta.fields,
44316                 reader : this.store.reader,
44317                 data : [ ]
44318             });
44319         }
44320         this.stores[i]  = store;
44321                   
44322         var view = this.views[i] = new Roo.View(
44323             il,
44324             this.tpl,
44325             {
44326                 singleSelect:true,
44327                 store: store,
44328                 selectedClass: this.selectedClass
44329             }
44330         );
44331         view.getEl().setWidth(lw);
44332         view.getEl().setStyle({
44333             position: i < 1 ? 'relative' : 'absolute',
44334             top: 0,
44335             left: (i * lw ) + 'px',
44336             display : i > 0 ? 'none' : 'block'
44337         });
44338         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44339         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44340         //view.on('click', this.onViewClick, this, { list : i });
44341
44342         store.on('beforeload', this.onBeforeLoad, this);
44343         store.on('load',  this.onLoad, this, { list  : i});
44344         store.on('loadexception', this.onLoadException, this);
44345
44346         // hide the other vies..
44347         
44348         
44349         
44350     },
44351       
44352     restrictHeight : function()
44353     {
44354         var mh = 0;
44355         Roo.each(this.innerLists, function(il,i) {
44356             var el = this.views[i].getEl();
44357             el.dom.style.height = '';
44358             var inner = el.dom;
44359             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44360             // only adjust heights on other ones..
44361             mh = Math.max(h, mh);
44362             if (i < 1) {
44363                 
44364                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44365                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44366                
44367             }
44368             
44369             
44370         }, this);
44371         
44372         this.list.beginUpdate();
44373         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44374         this.list.alignTo(this.el, this.listAlign);
44375         this.list.endUpdate();
44376         
44377     },
44378      
44379     
44380     // -- store handlers..
44381     // private
44382     onBeforeLoad : function()
44383     {
44384         if(!this.hasFocus){
44385             return;
44386         }
44387         this.innerLists[0].update(this.loadingText ?
44388                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44389         this.restrictHeight();
44390         this.selectedIndex = -1;
44391     },
44392     // private
44393     onLoad : function(a,b,c,d)
44394     {
44395         if (!this.loadingChildren) {
44396             // then we are loading the top level. - hide the children
44397             for (var i = 1;i < this.views.length; i++) {
44398                 this.views[i].getEl().setStyle({ display : 'none' });
44399             }
44400             var lw = Math.floor(
44401                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44402             );
44403         
44404              this.list.setWidth(lw); // default to '1'
44405
44406             
44407         }
44408         if(!this.hasFocus){
44409             return;
44410         }
44411         
44412         if(this.store.getCount() > 0) {
44413             this.expand();
44414             this.restrictHeight();   
44415         } else {
44416             this.onEmptyResults();
44417         }
44418         
44419         if (!this.loadingChildren) {
44420             this.selectActive();
44421         }
44422         /*
44423         this.stores[1].loadData([]);
44424         this.stores[2].loadData([]);
44425         this.views
44426         */    
44427     
44428         //this.el.focus();
44429     },
44430     
44431     
44432     // private
44433     onLoadException : function()
44434     {
44435         this.collapse();
44436         Roo.log(this.store.reader.jsonData);
44437         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44438             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44439         }
44440         
44441         
44442     },
44443     // no cleaning of leading spaces on blur here.
44444     cleanLeadingSpace : function(e) { },
44445     
44446
44447     onSelectChange : function (view, sels, opts )
44448     {
44449         var ix = view.getSelectedIndexes();
44450          
44451         if (opts.list > this.maxColumns - 2) {
44452             if (view.store.getCount()<  1) {
44453                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44454
44455             } else  {
44456                 if (ix.length) {
44457                     // used to clear ?? but if we are loading unselected 
44458                     this.setFromData(view.store.getAt(ix[0]).data);
44459                 }
44460                 
44461             }
44462             
44463             return;
44464         }
44465         
44466         if (!ix.length) {
44467             // this get's fired when trigger opens..
44468            // this.setFromData({});
44469             var str = this.stores[opts.list+1];
44470             str.data.clear(); // removeall wihtout the fire events..
44471             return;
44472         }
44473         
44474         var rec = view.store.getAt(ix[0]);
44475          
44476         this.setFromData(rec.data);
44477         this.fireEvent('select', this, rec, ix[0]);
44478         
44479         var lw = Math.floor(
44480              (
44481                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44482              ) / this.maxColumns
44483         );
44484         this.loadingChildren = true;
44485         this.stores[opts.list+1].loadDataFromChildren( rec );
44486         this.loadingChildren = false;
44487         var dl = this.stores[opts.list+1]. getTotalCount();
44488         
44489         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44490         
44491         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44492         for (var i = opts.list+2; i < this.views.length;i++) {
44493             this.views[i].getEl().setStyle({ display : 'none' });
44494         }
44495         
44496         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44497         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44498         
44499         if (this.isLoading) {
44500            // this.selectActive(opts.list);
44501         }
44502          
44503     },
44504     
44505     
44506     
44507     
44508     onDoubleClick : function()
44509     {
44510         this.collapse(); //??
44511     },
44512     
44513      
44514     
44515     
44516     
44517     // private
44518     recordToStack : function(store, prop, value, stack)
44519     {
44520         var cstore = new Roo.data.SimpleStore({
44521             //fields : this.store.reader.meta.fields, // we need array reader.. for
44522             reader : this.store.reader,
44523             data : [ ]
44524         });
44525         var _this = this;
44526         var record  = false;
44527         var srec = false;
44528         if(store.getCount() < 1){
44529             return false;
44530         }
44531         store.each(function(r){
44532             if(r.data[prop] == value){
44533                 record = r;
44534             srec = r;
44535                 return false;
44536             }
44537             if (r.data.cn && r.data.cn.length) {
44538                 cstore.loadDataFromChildren( r);
44539                 var cret = _this.recordToStack(cstore, prop, value, stack);
44540                 if (cret !== false) {
44541                     record = cret;
44542                     srec = r;
44543                     return false;
44544                 }
44545             }
44546              
44547             return true;
44548         });
44549         if (record == false) {
44550             return false
44551         }
44552         stack.unshift(srec);
44553         return record;
44554     },
44555     
44556     /*
44557      * find the stack of stores that match our value.
44558      *
44559      * 
44560      */
44561     
44562     selectActive : function ()
44563     {
44564         // if store is not loaded, then we will need to wait for that to happen first.
44565         var stack = [];
44566         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44567         for (var i = 0; i < stack.length; i++ ) {
44568             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44569         }
44570         
44571     }
44572         
44573          
44574     
44575     
44576     
44577     
44578 });/*
44579  * Based on:
44580  * Ext JS Library 1.1.1
44581  * Copyright(c) 2006-2007, Ext JS, LLC.
44582  *
44583  * Originally Released Under LGPL - original licence link has changed is not relivant.
44584  *
44585  * Fork - LGPL
44586  * <script type="text/javascript">
44587  */
44588 /**
44589  * @class Roo.form.Checkbox
44590  * @extends Roo.form.Field
44591  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44592  * @constructor
44593  * Creates a new Checkbox
44594  * @param {Object} config Configuration options
44595  */
44596 Roo.form.Checkbox = function(config){
44597     Roo.form.Checkbox.superclass.constructor.call(this, config);
44598     this.addEvents({
44599         /**
44600          * @event check
44601          * Fires when the checkbox is checked or unchecked.
44602              * @param {Roo.form.Checkbox} this This checkbox
44603              * @param {Boolean} checked The new checked value
44604              */
44605         check : true
44606     });
44607 };
44608
44609 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44610     /**
44611      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44612      */
44613     focusClass : undefined,
44614     /**
44615      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44616      */
44617     fieldClass: "x-form-field",
44618     /**
44619      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44620      */
44621     checked: false,
44622     /**
44623      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44624      * {tag: "input", type: "checkbox", autocomplete: "off"})
44625      */
44626     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44627     /**
44628      * @cfg {String} boxLabel The text that appears beside the checkbox
44629      */
44630     boxLabel : "",
44631     /**
44632      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44633      */  
44634     inputValue : '1',
44635     /**
44636      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44637      */
44638      valueOff: '0', // value when not checked..
44639
44640     actionMode : 'viewEl', 
44641     //
44642     // private
44643     itemCls : 'x-menu-check-item x-form-item',
44644     groupClass : 'x-menu-group-item',
44645     inputType : 'hidden',
44646     
44647     
44648     inSetChecked: false, // check that we are not calling self...
44649     
44650     inputElement: false, // real input element?
44651     basedOn: false, // ????
44652     
44653     isFormField: true, // not sure where this is needed!!!!
44654
44655     onResize : function(){
44656         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44657         if(!this.boxLabel){
44658             this.el.alignTo(this.wrap, 'c-c');
44659         }
44660     },
44661
44662     initEvents : function(){
44663         Roo.form.Checkbox.superclass.initEvents.call(this);
44664         this.el.on("click", this.onClick,  this);
44665         this.el.on("change", this.onClick,  this);
44666     },
44667
44668
44669     getResizeEl : function(){
44670         return this.wrap;
44671     },
44672
44673     getPositionEl : function(){
44674         return this.wrap;
44675     },
44676
44677     // private
44678     onRender : function(ct, position){
44679         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44680         /*
44681         if(this.inputValue !== undefined){
44682             this.el.dom.value = this.inputValue;
44683         }
44684         */
44685         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44686         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44687         var viewEl = this.wrap.createChild({ 
44688             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44689         this.viewEl = viewEl;   
44690         this.wrap.on('click', this.onClick,  this); 
44691         
44692         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44693         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44694         
44695         
44696         
44697         if(this.boxLabel){
44698             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44699         //    viewEl.on('click', this.onClick,  this); 
44700         }
44701         //if(this.checked){
44702             this.setChecked(this.checked);
44703         //}else{
44704             //this.checked = this.el.dom;
44705         //}
44706
44707     },
44708
44709     // private
44710     initValue : Roo.emptyFn,
44711
44712     /**
44713      * Returns the checked state of the checkbox.
44714      * @return {Boolean} True if checked, else false
44715      */
44716     getValue : function(){
44717         if(this.el){
44718             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44719         }
44720         return this.valueOff;
44721         
44722     },
44723
44724         // private
44725     onClick : function(){ 
44726         if (this.disabled) {
44727             return;
44728         }
44729         this.setChecked(!this.checked);
44730
44731         //if(this.el.dom.checked != this.checked){
44732         //    this.setValue(this.el.dom.checked);
44733        // }
44734     },
44735
44736     /**
44737      * Sets the checked state of the checkbox.
44738      * On is always based on a string comparison between inputValue and the param.
44739      * @param {Boolean/String} value - the value to set 
44740      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44741      */
44742     setValue : function(v,suppressEvent){
44743         
44744         
44745         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44746         //if(this.el && this.el.dom){
44747         //    this.el.dom.checked = this.checked;
44748         //    this.el.dom.defaultChecked = this.checked;
44749         //}
44750         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44751         //this.fireEvent("check", this, this.checked);
44752     },
44753     // private..
44754     setChecked : function(state,suppressEvent)
44755     {
44756         if (this.inSetChecked) {
44757             this.checked = state;
44758             return;
44759         }
44760         
44761     
44762         if(this.wrap){
44763             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44764         }
44765         this.checked = state;
44766         if(suppressEvent !== true){
44767             this.fireEvent('check', this, state);
44768         }
44769         this.inSetChecked = true;
44770         this.el.dom.value = state ? this.inputValue : this.valueOff;
44771         this.inSetChecked = false;
44772         
44773     },
44774     // handle setting of hidden value by some other method!!?!?
44775     setFromHidden: function()
44776     {
44777         if(!this.el){
44778             return;
44779         }
44780         //console.log("SET FROM HIDDEN");
44781         //alert('setFrom hidden');
44782         this.setValue(this.el.dom.value);
44783     },
44784     
44785     onDestroy : function()
44786     {
44787         if(this.viewEl){
44788             Roo.get(this.viewEl).remove();
44789         }
44790          
44791         Roo.form.Checkbox.superclass.onDestroy.call(this);
44792     },
44793     
44794     setBoxLabel : function(str)
44795     {
44796         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44797     }
44798
44799 });/*
44800  * Based on:
44801  * Ext JS Library 1.1.1
44802  * Copyright(c) 2006-2007, Ext JS, LLC.
44803  *
44804  * Originally Released Under LGPL - original licence link has changed is not relivant.
44805  *
44806  * Fork - LGPL
44807  * <script type="text/javascript">
44808  */
44809  
44810 /**
44811  * @class Roo.form.Radio
44812  * @extends Roo.form.Checkbox
44813  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44814  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44815  * @constructor
44816  * Creates a new Radio
44817  * @param {Object} config Configuration options
44818  */
44819 Roo.form.Radio = function(){
44820     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44821 };
44822 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44823     inputType: 'radio',
44824
44825     /**
44826      * If this radio is part of a group, it will return the selected value
44827      * @return {String}
44828      */
44829     getGroupValue : function(){
44830         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44831     },
44832     
44833     
44834     onRender : function(ct, position){
44835         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44836         
44837         if(this.inputValue !== undefined){
44838             this.el.dom.value = this.inputValue;
44839         }
44840          
44841         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44842         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44843         //var viewEl = this.wrap.createChild({ 
44844         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44845         //this.viewEl = viewEl;   
44846         //this.wrap.on('click', this.onClick,  this); 
44847         
44848         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44849         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44850         
44851         
44852         
44853         if(this.boxLabel){
44854             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44855         //    viewEl.on('click', this.onClick,  this); 
44856         }
44857          if(this.checked){
44858             this.el.dom.checked =   'checked' ;
44859         }
44860          
44861     } 
44862     
44863     
44864 });Roo.rtf = {}; // namespace
44865 Roo.rtf.Hex = function(hex)
44866 {
44867     this.hexstr = hex;
44868 };
44869 Roo.rtf.Paragraph = function(opts)
44870 {
44871     this.content = []; ///??? is that used?
44872 };Roo.rtf.Span = function(opts)
44873 {
44874     this.value = opts.value;
44875 };
44876
44877 Roo.rtf.Group = function(parent)
44878 {
44879     // we dont want to acutally store parent - it will make debug a nightmare..
44880     this.content = [];
44881     this.cn  = [];
44882      
44883        
44884     
44885 };
44886
44887 Roo.rtf.Group.prototype = {
44888     ignorable : false,
44889     content: false,
44890     cn: false,
44891     addContent : function(node) {
44892         // could set styles...
44893         this.content.push(node);
44894     },
44895     addChild : function(cn)
44896     {
44897         this.cn.push(cn);
44898     },
44899     // only for images really...
44900     toDataURL : function()
44901     {
44902         var mimetype = false;
44903         switch(true) {
44904             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44905                 mimetype = "image/png";
44906                 break;
44907              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44908                 mimetype = "image/jpeg";
44909                 break;
44910             default :
44911                 return 'about:blank'; // ?? error?
44912         }
44913         
44914         
44915         var hexstring = this.content[this.content.length-1].value;
44916         
44917         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44918             return String.fromCharCode(parseInt(a, 16));
44919         }).join(""));
44920     }
44921     
44922 };
44923 // this looks like it's normally the {rtf{ .... }}
44924 Roo.rtf.Document = function()
44925 {
44926     // we dont want to acutally store parent - it will make debug a nightmare..
44927     this.rtlch  = [];
44928     this.content = [];
44929     this.cn = [];
44930     
44931 };
44932 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44933     addChild : function(cn)
44934     {
44935         this.cn.push(cn);
44936         switch(cn.type) {
44937             case 'rtlch': // most content seems to be inside this??
44938             case 'listtext':
44939             case 'shpinst':
44940                 this.rtlch.push(cn);
44941                 return;
44942             default:
44943                 this[cn.type] = cn;
44944         }
44945         
44946     },
44947     
44948     getElementsByType : function(type)
44949     {
44950         var ret =  [];
44951         this._getElementsByType(type, ret, this.cn, 'rtf');
44952         return ret;
44953     },
44954     _getElementsByType : function (type, ret, search_array, path)
44955     {
44956         search_array.forEach(function(n,i) {
44957             if (n.type == type) {
44958                 n.path = path + '/' + n.type + ':' + i;
44959                 ret.push(n);
44960             }
44961             if (n.cn.length > 0) {
44962                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44963             }
44964         },this);
44965     }
44966     
44967 });
44968  
44969 Roo.rtf.Ctrl = function(opts)
44970 {
44971     this.value = opts.value;
44972     this.param = opts.param;
44973 };
44974 /**
44975  *
44976  *
44977  * based on this https://github.com/iarna/rtf-parser
44978  * it's really only designed to extract pict from pasted RTF 
44979  *
44980  * usage:
44981  *
44982  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44983  *  
44984  *
44985  */
44986
44987  
44988
44989
44990
44991 Roo.rtf.Parser = function(text) {
44992     //super({objectMode: true})
44993     this.text = '';
44994     this.parserState = this.parseText;
44995     
44996     // these are for interpeter...
44997     this.doc = {};
44998     ///this.parserState = this.parseTop
44999     this.groupStack = [];
45000     this.hexStore = [];
45001     this.doc = false;
45002     
45003     this.groups = []; // where we put the return.
45004     
45005     for (var ii = 0; ii < text.length; ++ii) {
45006         ++this.cpos;
45007         
45008         if (text[ii] === '\n') {
45009             ++this.row;
45010             this.col = 1;
45011         } else {
45012             ++this.col;
45013         }
45014         this.parserState(text[ii]);
45015     }
45016     
45017     
45018     
45019 };
45020 Roo.rtf.Parser.prototype = {
45021     text : '', // string being parsed..
45022     controlWord : '',
45023     controlWordParam :  '',
45024     hexChar : '',
45025     doc : false,
45026     group: false,
45027     groupStack : false,
45028     hexStore : false,
45029     
45030     
45031     cpos : 0, 
45032     row : 1, // reportin?
45033     col : 1, //
45034
45035      
45036     push : function (el)
45037     {
45038         var m = 'cmd'+ el.type;
45039         if (typeof(this[m]) == 'undefined') {
45040             Roo.log('invalid cmd:' + el.type);
45041             return;
45042         }
45043         this[m](el);
45044         //Roo.log(el);
45045     },
45046     flushHexStore : function()
45047     {
45048         if (this.hexStore.length < 1) {
45049             return;
45050         }
45051         var hexstr = this.hexStore.map(
45052             function(cmd) {
45053                 return cmd.value;
45054         }).join('');
45055         
45056         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45057               
45058             
45059         this.hexStore.splice(0)
45060         
45061     },
45062     
45063     cmdgroupstart : function()
45064     {
45065         this.flushHexStore();
45066         if (this.group) {
45067             this.groupStack.push(this.group);
45068         }
45069          // parent..
45070         if (this.doc === false) {
45071             this.group = this.doc = new Roo.rtf.Document();
45072             return;
45073             
45074         }
45075         this.group = new Roo.rtf.Group(this.group);
45076     },
45077     cmdignorable : function()
45078     {
45079         this.flushHexStore();
45080         this.group.ignorable = true;
45081     },
45082     cmdendparagraph : function()
45083     {
45084         this.flushHexStore();
45085         this.group.addContent(new Roo.rtf.Paragraph());
45086     },
45087     cmdgroupend : function ()
45088     {
45089         this.flushHexStore();
45090         var endingGroup = this.group;
45091         
45092         
45093         this.group = this.groupStack.pop();
45094         if (this.group) {
45095             this.group.addChild(endingGroup);
45096         }
45097         
45098         
45099         
45100         var doc = this.group || this.doc;
45101         //if (endingGroup instanceof FontTable) {
45102         //  doc.fonts = endingGroup.table
45103         //} else if (endingGroup instanceof ColorTable) {
45104         //  doc.colors = endingGroup.table
45105         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45106         if (endingGroup.ignorable === false) {
45107             //code
45108             this.groups.push(endingGroup);
45109            // Roo.log( endingGroup );
45110         }
45111             //Roo.each(endingGroup.content, function(item)) {
45112             //    doc.addContent(item);
45113             //}
45114             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45115         //}
45116     },
45117     cmdtext : function (cmd)
45118     {
45119         this.flushHexStore();
45120         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45121             //this.group = this.doc
45122         }
45123         this.group.addContent(new Roo.rtf.Span(cmd));
45124     },
45125     cmdcontrolword : function (cmd)
45126     {
45127         this.flushHexStore();
45128         if (!this.group.type) {
45129             this.group.type = cmd.value;
45130             return;
45131         }
45132         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45133         // we actually don't care about ctrl words...
45134         return ;
45135         /*
45136         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45137         if (this[method]) {
45138             this[method](cmd.param)
45139         } else {
45140             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45141         }
45142         */
45143     },
45144     cmdhexchar : function(cmd) {
45145         this.hexStore.push(cmd);
45146     },
45147     cmderror : function(cmd) {
45148         throw new Exception (cmd.value);
45149     },
45150     
45151     /*
45152       _flush (done) {
45153         if (this.text !== '\u0000') this.emitText()
45154         done()
45155       }
45156       */
45157       
45158       
45159     parseText : function(c)
45160     {
45161         if (c === '\\') {
45162             this.parserState = this.parseEscapes;
45163         } else if (c === '{') {
45164             this.emitStartGroup();
45165         } else if (c === '}') {
45166             this.emitEndGroup();
45167         } else if (c === '\x0A' || c === '\x0D') {
45168             // cr/lf are noise chars
45169         } else {
45170             this.text += c;
45171         }
45172     },
45173     
45174     parseEscapes: function (c)
45175     {
45176         if (c === '\\' || c === '{' || c === '}') {
45177             this.text += c;
45178             this.parserState = this.parseText;
45179         } else {
45180             this.parserState = this.parseControlSymbol;
45181             this.parseControlSymbol(c);
45182         }
45183     },
45184     parseControlSymbol: function(c)
45185     {
45186         if (c === '~') {
45187             this.text += '\u00a0'; // nbsp
45188             this.parserState = this.parseText
45189         } else if (c === '-') {
45190              this.text += '\u00ad'; // soft hyphen
45191         } else if (c === '_') {
45192             this.text += '\u2011'; // non-breaking hyphen
45193         } else if (c === '*') {
45194             this.emitIgnorable();
45195             this.parserState = this.parseText;
45196         } else if (c === "'") {
45197             this.parserState = this.parseHexChar;
45198         } else if (c === '|') { // formula cacter
45199             this.emitFormula();
45200             this.parserState = this.parseText;
45201         } else if (c === ':') { // subentry in an index entry
45202             this.emitIndexSubEntry();
45203             this.parserState = this.parseText;
45204         } else if (c === '\x0a') {
45205             this.emitEndParagraph();
45206             this.parserState = this.parseText;
45207         } else if (c === '\x0d') {
45208             this.emitEndParagraph();
45209             this.parserState = this.parseText;
45210         } else {
45211             this.parserState = this.parseControlWord;
45212             this.parseControlWord(c);
45213         }
45214     },
45215     parseHexChar: function (c)
45216     {
45217         if (/^[A-Fa-f0-9]$/.test(c)) {
45218             this.hexChar += c;
45219             if (this.hexChar.length >= 2) {
45220               this.emitHexChar();
45221               this.parserState = this.parseText;
45222             }
45223             return;
45224         }
45225         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45226         this.parserState = this.parseText;
45227         
45228     },
45229     parseControlWord : function(c)
45230     {
45231         if (c === ' ') {
45232             this.emitControlWord();
45233             this.parserState = this.parseText;
45234         } else if (/^[-\d]$/.test(c)) {
45235             this.parserState = this.parseControlWordParam;
45236             this.controlWordParam += c;
45237         } else if (/^[A-Za-z]$/.test(c)) {
45238           this.controlWord += c;
45239         } else {
45240           this.emitControlWord();
45241           this.parserState = this.parseText;
45242           this.parseText(c);
45243         }
45244     },
45245     parseControlWordParam : function (c) {
45246         if (/^\d$/.test(c)) {
45247           this.controlWordParam += c;
45248         } else if (c === ' ') {
45249           this.emitControlWord();
45250           this.parserState = this.parseText;
45251         } else {
45252           this.emitControlWord();
45253           this.parserState = this.parseText;
45254           this.parseText(c);
45255         }
45256     },
45257     
45258     
45259     
45260     
45261     emitText : function () {
45262         if (this.text === '') {
45263             return;
45264         }
45265         this.push({
45266             type: 'text',
45267             value: this.text,
45268             pos: this.cpos,
45269             row: this.row,
45270             col: this.col
45271         });
45272         this.text = ''
45273     },
45274     emitControlWord : function ()
45275     {
45276         this.emitText();
45277         if (this.controlWord === '') {
45278             this.emitError('empty control word');
45279         } else {
45280             this.push({
45281                   type: 'controlword',
45282                   value: this.controlWord,
45283                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45284                   pos: this.cpos,
45285                   row: this.row,
45286                   col: this.col
45287             });
45288         }
45289         this.controlWord = '';
45290         this.controlWordParam = '';
45291     },
45292     emitStartGroup : function ()
45293     {
45294         this.emitText();
45295         this.push({
45296             type: 'groupstart',
45297             pos: this.cpos,
45298             row: this.row,
45299             col: this.col
45300         });
45301     },
45302     emitEndGroup : function ()
45303     {
45304         this.emitText();
45305         this.push({
45306             type: 'groupend',
45307             pos: this.cpos,
45308             row: this.row,
45309             col: this.col
45310         });
45311     },
45312     emitIgnorable : function ()
45313     {
45314         this.emitText();
45315         this.push({
45316             type: 'ignorable',
45317             pos: this.cpos,
45318             row: this.row,
45319             col: this.col
45320         });
45321     },
45322     emitHexChar : function ()
45323     {
45324         this.emitText();
45325         this.push({
45326             type: 'hexchar',
45327             value: this.hexChar,
45328             pos: this.cpos,
45329             row: this.row,
45330             col: this.col
45331         });
45332         this.hexChar = ''
45333     },
45334     emitError : function (message)
45335     {
45336       this.emitText();
45337       this.push({
45338             type: 'error',
45339             value: message,
45340             row: this.row,
45341             col: this.col,
45342             char: this.cpos //,
45343             //stack: new Error().stack
45344         });
45345     },
45346     emitEndParagraph : function () {
45347         this.emitText();
45348         this.push({
45349             type: 'endparagraph',
45350             pos: this.cpos,
45351             row: this.row,
45352             col: this.col
45353         });
45354     }
45355      
45356 } ;
45357 Roo.htmleditor = {};
45358  
45359 /**
45360  * @class Roo.htmleditor.Filter
45361  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45362  * @cfg {DomElement} node The node to iterate and filter
45363  * @cfg {boolean|String|Array} tag Tags to replace 
45364  * @constructor
45365  * Create a new Filter.
45366  * @param {Object} config Configuration options
45367  */
45368
45369
45370
45371 Roo.htmleditor.Filter = function(cfg) {
45372     Roo.apply(this.cfg);
45373     // this does not actually call walk as it's really just a abstract class
45374 }
45375
45376
45377 Roo.htmleditor.Filter.prototype = {
45378     
45379     node: false,
45380     
45381     tag: false,
45382
45383     // overrride to do replace comments.
45384     replaceComment : false,
45385     
45386     // overrride to do replace or do stuff with tags..
45387     replaceTag : false,
45388     
45389     walk : function(dom)
45390     {
45391         Roo.each( Array.from(dom.childNodes), function( e ) {
45392             switch(true) {
45393                 
45394                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45395                     this.replaceComment(e);
45396                     return;
45397                 
45398                 case e.nodeType != 1: //not a node.
45399                     return;
45400                 
45401                 case this.tag === true: // everything
45402                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45403                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45404                     if (this.replaceTag && false === this.replaceTag(e)) {
45405                         return;
45406                     }
45407                     if (e.hasChildNodes()) {
45408                         this.walk(e);
45409                     }
45410                     return;
45411                 
45412                 default:    // tags .. that do not match.
45413                     if (e.hasChildNodes()) {
45414                         this.walk(e);
45415                     }
45416             }
45417             
45418         }, this);
45419         
45420     }
45421 }; 
45422
45423 /**
45424  * @class Roo.htmleditor.FilterAttributes
45425  * clean attributes and  styles including http:// etc.. in attribute
45426  * @constructor
45427 * Run a new Attribute Filter
45428 * @param {Object} config Configuration options
45429  */
45430 Roo.htmleditor.FilterAttributes = function(cfg)
45431 {
45432     Roo.apply(this, cfg);
45433     this.attrib_black = this.attrib_black || [];
45434     this.attrib_white = this.attrib_white || [];
45435
45436     this.attrib_clean = this.attrib_clean || [];
45437     this.style_white = this.style_white || [];
45438     this.style_black = this.style_black || [];
45439     this.walk(cfg.node);
45440 }
45441
45442 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45443 {
45444     tag: true, // all tags
45445     
45446     attrib_black : false, // array
45447     attrib_clean : false,
45448     attrib_white : false,
45449
45450     style_white : false,
45451     style_black : false,
45452      
45453      
45454     replaceTag : function(node)
45455     {
45456         if (!node.attributes || !node.attributes.length) {
45457             return true;
45458         }
45459         
45460         for (var i = node.attributes.length-1; i > -1 ; i--) {
45461             var a = node.attributes[i];
45462             //console.log(a);
45463             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45464                 node.removeAttribute(a.name);
45465                 continue;
45466             }
45467             
45468             
45469             
45470             if (a.name.toLowerCase().substr(0,2)=='on')  {
45471                 node.removeAttribute(a.name);
45472                 continue;
45473             }
45474             
45475             
45476             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45477                 node.removeAttribute(a.name);
45478                 continue;
45479             }
45480             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45481                 this.cleanAttr(node,a.name,a.value); // fixme..
45482                 continue;
45483             }
45484             if (a.name == 'style') {
45485                 this.cleanStyle(node,a.name,a.value);
45486                 continue;
45487             }
45488             /// clean up MS crap..
45489             // tecnically this should be a list of valid class'es..
45490             
45491             
45492             if (a.name == 'class') {
45493                 if (a.value.match(/^Mso/)) {
45494                     node.removeAttribute('class');
45495                 }
45496                 
45497                 if (a.value.match(/^body$/)) {
45498                     node.removeAttribute('class');
45499                 }
45500                 continue;
45501             }
45502             
45503             
45504             // style cleanup!?
45505             // class cleanup?
45506             
45507         }
45508         return true; // clean children
45509     },
45510         
45511     cleanAttr: function(node, n,v)
45512     {
45513         
45514         if (v.match(/^\./) || v.match(/^\//)) {
45515             return;
45516         }
45517         if (v.match(/^(http|https):\/\//)
45518             || v.match(/^mailto:/) 
45519             || v.match(/^ftp:/)
45520             || v.match(/^data:/)
45521             ) {
45522             return;
45523         }
45524         if (v.match(/^#/)) {
45525             return;
45526         }
45527         if (v.match(/^\{/)) { // allow template editing.
45528             return;
45529         }
45530 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45531         node.removeAttribute(n);
45532         
45533     },
45534     cleanStyle : function(node,  n,v)
45535     {
45536         if (v.match(/expression/)) { //XSS?? should we even bother..
45537             node.removeAttribute(n);
45538             return;
45539         }
45540         
45541         var parts = v.split(/;/);
45542         var clean = [];
45543         
45544         Roo.each(parts, function(p) {
45545             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45546             if (!p.length) {
45547                 return true;
45548             }
45549             var l = p.split(':').shift().replace(/\s+/g,'');
45550             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45551             
45552             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45553                 return true;
45554             }
45555             //Roo.log()
45556             // only allow 'c whitelisted system attributes'
45557             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45558                 return true;
45559             }
45560             
45561             
45562             clean.push(p);
45563             return true;
45564         },this);
45565         if (clean.length) { 
45566             node.setAttribute(n, clean.join(';'));
45567         } else {
45568             node.removeAttribute(n);
45569         }
45570         
45571     }
45572         
45573         
45574         
45575     
45576 });/**
45577  * @class Roo.htmleditor.FilterBlack
45578  * remove blacklisted elements.
45579  * @constructor
45580  * Run a new Blacklisted Filter
45581  * @param {Object} config Configuration options
45582  */
45583
45584 Roo.htmleditor.FilterBlack = function(cfg)
45585 {
45586     Roo.apply(this, cfg);
45587     this.walk(cfg.node);
45588 }
45589
45590 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45591 {
45592     tag : true, // all elements.
45593    
45594     replace : function(n)
45595     {
45596         n.parentNode.removeChild(n);
45597     }
45598 });
45599 /**
45600  * @class Roo.htmleditor.FilterComment
45601  * remove comments.
45602  * @constructor
45603 * Run a new Comments Filter
45604 * @param {Object} config Configuration options
45605  */
45606 Roo.htmleditor.FilterComment = function(cfg)
45607 {
45608     this.walk(cfg.node);
45609 }
45610
45611 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45612 {
45613   
45614     replaceComment : function(n)
45615     {
45616         n.parentNode.removeChild(n);
45617     }
45618 });/**
45619  * @class Roo.htmleditor.FilterKeepChildren
45620  * remove tags but keep children
45621  * @constructor
45622  * Run a new Keep Children Filter
45623  * @param {Object} config Configuration options
45624  */
45625
45626 Roo.htmleditor.FilterKeepChildren = function(cfg)
45627 {
45628     Roo.apply(this, cfg);
45629     if (this.tag === false) {
45630         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45631     }
45632     this.walk(cfg.node);
45633 }
45634
45635 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45636 {
45637     
45638   
45639     replaceTag : function(node)
45640     {
45641         // walk children...
45642         //Roo.log(node);
45643         var ar = Array.from(node.childNodes);
45644         //remove first..
45645         for (var i = 0; i < ar.length; i++) {
45646             if (ar[i].nodeType == 1) {
45647                 if (
45648                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45649                     || // array and it matches
45650                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45651                 ) {
45652                     this.replaceTag(ar[i]); // child is blacklisted as well...
45653                     continue;
45654                 }
45655             }
45656         }  
45657         ar = Array.from(node.childNodes);
45658         for (var i = 0; i < ar.length; i++) {
45659          
45660             node.removeChild(ar[i]);
45661             // what if we need to walk these???
45662             node.parentNode.insertBefore(ar[i], node);
45663             if (this.tag !== false) {
45664                 this.walk(ar[i]);
45665                 
45666             }
45667         }
45668         node.parentNode.removeChild(node);
45669         return false; // don't walk children
45670         
45671         
45672     }
45673 });/**
45674  * @class Roo.htmleditor.FilterParagraph
45675  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45676  * like on 'push' to remove the <p> tags and replace them with line breaks.
45677  * @constructor
45678  * Run a new Paragraph Filter
45679  * @param {Object} config Configuration options
45680  */
45681
45682 Roo.htmleditor.FilterParagraph = function(cfg)
45683 {
45684     // no need to apply config.
45685     this.walk(cfg.node);
45686 }
45687
45688 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45689 {
45690     
45691      
45692     tag : 'P',
45693     
45694      
45695     replaceTag : function(node)
45696     {
45697         
45698         if (node.childNodes.length == 1 &&
45699             node.childNodes[0].nodeType == 3 &&
45700             node.childNodes[0].textContent.trim().length < 1
45701             ) {
45702             // remove and replace with '<BR>';
45703             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45704             return false; // no need to walk..
45705         }
45706         var ar = Array.from(node.childNodes);
45707         for (var i = 0; i < ar.length; i++) {
45708             node.removeChild(ar[i]);
45709             // what if we need to walk these???
45710             node.parentNode.insertBefore(ar[i], node);
45711         }
45712         // now what about this?
45713         // <p> &nbsp; </p>
45714         
45715         // double BR.
45716         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45717         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45718         node.parentNode.removeChild(node);
45719         
45720         return false;
45721
45722     }
45723     
45724 });/**
45725  * @class Roo.htmleditor.FilterSpan
45726  * filter span's with no attributes out..
45727  * @constructor
45728  * Run a new Span Filter
45729  * @param {Object} config Configuration options
45730  */
45731
45732 Roo.htmleditor.FilterSpan = function(cfg)
45733 {
45734     // no need to apply config.
45735     this.walk(cfg.node);
45736 }
45737
45738 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45739 {
45740      
45741     tag : 'SPAN',
45742      
45743  
45744     replaceTag : function(node)
45745     {
45746         if (node.attributes && node.attributes.length > 0) {
45747             return true; // walk if there are any.
45748         }
45749         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45750         return false;
45751      
45752     }
45753     
45754 });/**
45755  * @class Roo.htmleditor.FilterTableWidth
45756   try and remove table width data - as that frequently messes up other stuff.
45757  * 
45758  *      was cleanTableWidths.
45759  *
45760  * Quite often pasting from word etc.. results in tables with column and widths.
45761  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45762  *
45763  * @constructor
45764  * Run a new Table Filter
45765  * @param {Object} config Configuration options
45766  */
45767
45768 Roo.htmleditor.FilterTableWidth = function(cfg)
45769 {
45770     // no need to apply config.
45771     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45772     this.walk(cfg.node);
45773 }
45774
45775 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45776 {
45777      
45778      
45779     
45780     replaceTag: function(node) {
45781         
45782         
45783       
45784         if (node.hasAttribute('width')) {
45785             node.removeAttribute('width');
45786         }
45787         
45788          
45789         if (node.hasAttribute("style")) {
45790             // pretty basic...
45791             
45792             var styles = node.getAttribute("style").split(";");
45793             var nstyle = [];
45794             Roo.each(styles, function(s) {
45795                 if (!s.match(/:/)) {
45796                     return;
45797                 }
45798                 var kv = s.split(":");
45799                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45800                     return;
45801                 }
45802                 // what ever is left... we allow.
45803                 nstyle.push(s);
45804             });
45805             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45806             if (!nstyle.length) {
45807                 node.removeAttribute('style');
45808             }
45809         }
45810         
45811         return true; // continue doing children..
45812     }
45813 });/**
45814  * @class Roo.htmleditor.FilterWord
45815  * try and clean up all the mess that Word generates.
45816  * 
45817  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45818  
45819  * @constructor
45820  * Run a new Span Filter
45821  * @param {Object} config Configuration options
45822  */
45823
45824 Roo.htmleditor.FilterWord = function(cfg)
45825 {
45826     // no need to apply config.
45827     this.walk(cfg.node);
45828 }
45829
45830 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45831 {
45832     tag: true,
45833      
45834     
45835     /**
45836      * Clean up MS wordisms...
45837      */
45838     replaceTag : function(node)
45839     {
45840          
45841         // no idea what this does - span with text, replaceds with just text.
45842         if(
45843                 node.nodeName == 'SPAN' &&
45844                 !node.hasAttributes() &&
45845                 node.childNodes.length == 1 &&
45846                 node.firstChild.nodeName == "#text"  
45847         ) {
45848             var textNode = node.firstChild;
45849             node.removeChild(textNode);
45850             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45851                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45852             }
45853             node.parentNode.insertBefore(textNode, node);
45854             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45855                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45856             }
45857             
45858             node.parentNode.removeChild(node);
45859             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45860         }
45861         
45862    
45863         
45864         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45865             node.parentNode.removeChild(node);
45866             return false; // dont do chidlren
45867         }
45868         //Roo.log(node.tagName);
45869         // remove - but keep children..
45870         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45871             //Roo.log('-- removed');
45872             while (node.childNodes.length) {
45873                 var cn = node.childNodes[0];
45874                 node.removeChild(cn);
45875                 node.parentNode.insertBefore(cn, node);
45876                 // move node to parent - and clean it..
45877                 this.replaceTag(cn);
45878             }
45879             node.parentNode.removeChild(node);
45880             /// no need to iterate chidlren = it's got none..
45881             //this.iterateChildren(node, this.cleanWord);
45882             return false; // no need to iterate children.
45883         }
45884         // clean styles
45885         if (node.className.length) {
45886             
45887             var cn = node.className.split(/\W+/);
45888             var cna = [];
45889             Roo.each(cn, function(cls) {
45890                 if (cls.match(/Mso[a-zA-Z]+/)) {
45891                     return;
45892                 }
45893                 cna.push(cls);
45894             });
45895             node.className = cna.length ? cna.join(' ') : '';
45896             if (!cna.length) {
45897                 node.removeAttribute("class");
45898             }
45899         }
45900         
45901         if (node.hasAttribute("lang")) {
45902             node.removeAttribute("lang");
45903         }
45904         
45905         if (node.hasAttribute("style")) {
45906             
45907             var styles = node.getAttribute("style").split(";");
45908             var nstyle = [];
45909             Roo.each(styles, function(s) {
45910                 if (!s.match(/:/)) {
45911                     return;
45912                 }
45913                 var kv = s.split(":");
45914                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45915                     return;
45916                 }
45917                 // what ever is left... we allow.
45918                 nstyle.push(s);
45919             });
45920             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45921             if (!nstyle.length) {
45922                 node.removeAttribute('style');
45923             }
45924         }
45925         return true; // do children
45926         
45927         
45928         
45929     }
45930 });
45931 /**
45932  * @class Roo.htmleditor.FilterStyleToTag
45933  * part of the word stuff... - certain 'styles' should be converted to tags.
45934  * eg.
45935  *   font-weight: bold -> bold
45936  *   ?? super / subscrit etc..
45937  * 
45938  * @constructor
45939 * Run a new style to tag filter.
45940 * @param {Object} config Configuration options
45941  */
45942 Roo.htmleditor.FilterStyleToTag = function(cfg)
45943 {
45944     
45945     this.tags = {
45946         B  : [ 'fontWeight' , 'bold'],
45947         I :  [ 'fontStyle' , 'italic'],
45948         //pre :  [ 'font-style' , 'italic'],
45949         // h1.. h6 ?? font-size?
45950         SUP : [ 'verticalAlign' , 'super' ],
45951         SUB : [ 'verticalAlign' , 'sub' ]
45952         
45953         
45954     };
45955     
45956     Roo.apply(this, cfg);
45957      
45958     
45959     this.walk(cfg.node);
45960     
45961     
45962     
45963 }
45964
45965
45966 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45967 {
45968     tag: true, // all tags
45969     
45970     tags : false,
45971     
45972     
45973     replaceTag : function(node)
45974     {
45975         
45976         
45977         if (node.getAttribute("style") === null) {
45978             return true;
45979         }
45980         var inject = [];
45981         for (var k in this.tags) {
45982             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45983                 inject.push(k);
45984                 node.style.removeProperty(this.tags[k][0]);
45985             }
45986         }
45987         if (!inject.length) {
45988             return true; 
45989         }
45990         var cn = Array.from(node.childNodes);
45991         var nn = node;
45992         Roo.each(inject, function(t) {
45993             var nc = node.ownerDocument.createelement(t);
45994             nn.appendChild(nc);
45995             nn = nc;
45996         });
45997         for(var i = 0;i < cn.length;cn++) {
45998             node.removeChild(cn[i]);
45999             nn.appendChild(cn[i]);
46000         }
46001         return true /// iterate thru
46002     }
46003     
46004 })/**
46005  * @class Roo.htmleditor.FilterLongBr
46006  * BR/BR/BR - keep a maximum of 2...
46007  * @constructor
46008  * Run a new Long BR Filter
46009  * @param {Object} config Configuration options
46010  */
46011
46012 Roo.htmleditor.FilterLongBr = function(cfg)
46013 {
46014     // no need to apply config.
46015     this.walk(cfg.node);
46016 }
46017
46018 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46019 {
46020     
46021      
46022     tag : 'BR',
46023     
46024      
46025     replaceTag : function(node)
46026     {
46027         
46028         var ps = node.nextSibling;
46029         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46030             ps = ps.nextSibling;
46031         }
46032         
46033         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46034             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46035             return false;
46036         }
46037         
46038         if (!ps || ps.nodeType != 1) {
46039             return false;
46040         }
46041         
46042         if (!ps || ps.tagName != 'BR') {
46043            
46044             return false;
46045         }
46046         
46047         
46048         
46049         
46050         
46051         if (!node.previousSibling) {
46052             return false;
46053         }
46054         var ps = node.previousSibling;
46055         
46056         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46057             ps = ps.previousSibling;
46058         }
46059         if (!ps || ps.nodeType != 1) {
46060             return false;
46061         }
46062         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46063         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46064             return false;
46065         }
46066         
46067         node.parentNode.removeChild(node); // remove me...
46068         
46069         return false; // no need to do children
46070
46071     }
46072     
46073 });
46074 /**
46075  * @class Roo.htmleditor.Tidy
46076  * Tidy HTML 
46077  * @cfg {Roo.HtmlEditorCore} core the editor.
46078  * @constructor
46079  * Create a new Filter.
46080  * @param {Object} config Configuration options
46081  */
46082
46083
46084 Roo.htmleditor.Tidy = function(cfg) {
46085     Roo.apply(this, cfg);
46086     
46087     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46088      
46089 }
46090
46091 Roo.htmleditor.Tidy.toString = function(node)
46092 {
46093     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46094 }
46095
46096 Roo.htmleditor.Tidy.prototype = {
46097     
46098     
46099     wrap : function(s) {
46100         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46101     },
46102
46103     
46104     tidy : function(node, indent) {
46105      
46106         if  (node.nodeType == 3) {
46107             // text.
46108             
46109             
46110             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46111                 
46112             
46113         }
46114         
46115         if  (node.nodeType != 1) {
46116             return '';
46117         }
46118         
46119         
46120         
46121         if (node.tagName == 'BODY') {
46122             
46123             return this.cn(node, '');
46124         }
46125              
46126              // Prints the node tagName, such as <A>, <IMG>, etc
46127         var ret = "<" + node.tagName +  this.attr(node) ;
46128         
46129         // elements with no children..
46130         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46131                 return ret + '/>';
46132         }
46133         ret += '>';
46134         
46135         
46136         var cindent = indent === false ? '' : (indent + '  ');
46137         // tags where we will not pad the children.. (inline text tags etc..)
46138         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46139             cindent = false;
46140             
46141             
46142         }
46143         
46144         var cn = this.cn(node, cindent );
46145         
46146         return ret + cn  + '</' + node.tagName + '>';
46147         
46148     },
46149     cn: function(node, indent)
46150     {
46151         var ret = [];
46152         
46153         var ar = Array.from(node.childNodes);
46154         for (var i = 0 ; i < ar.length ; i++) {
46155             
46156             
46157             
46158             if (indent !== false   // indent==false preservies everything
46159                 && i > 0
46160                 && ar[i].nodeType == 3 
46161                 && ar[i].nodeValue.length > 0
46162                 && ar[i].nodeValue.match(/^\s+/)
46163             ) {
46164                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46165                     ret.pop(); // remove line break from last?
46166                 }
46167                 
46168                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46169             }
46170             if (indent !== false
46171                 && ar[i].nodeType == 1 // element - and indent is not set... 
46172             ) {
46173                 ret.push("\n" + indent); 
46174             }
46175             
46176             ret.push(this.tidy(ar[i], indent));
46177             // text + trailing indent 
46178             if (indent !== false
46179                 && ar[i].nodeType == 3
46180                 && ar[i].nodeValue.length > 0
46181                 && ar[i].nodeValue.match(/\s+$/)
46182             ){
46183                 ret.push("\n" + indent); 
46184             }
46185             
46186             
46187             
46188             
46189         }
46190         // what if all text?
46191         
46192         
46193         return ret.join('');
46194     },
46195     
46196          
46197         
46198     attr : function(node)
46199     {
46200         var attr = [];
46201         for(i = 0; i < node.attributes.length;i++) {
46202             
46203             // skip empty values?
46204             if (!node.attributes.item(i).value.length) {
46205                 continue;
46206             }
46207             attr.push(  node.attributes.item(i).name + '="' +
46208                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46209             );
46210         }
46211         return attr.length ? (' ' + attr.join(' ') ) : '';
46212         
46213     }
46214     
46215     
46216     
46217 }
46218 /**
46219  * @class Roo.htmleditor.KeyEnter
46220  * Handle Enter press..
46221  * @cfg {Roo.HtmlEditorCore} core the editor.
46222  * @constructor
46223  * Create a new Filter.
46224  * @param {Object} config Configuration options
46225  */
46226
46227
46228
46229 Roo.htmleditor.KeyEnter = function(cfg) {
46230     Roo.apply(this, cfg);
46231     // this does not actually call walk as it's really just a abstract class
46232  
46233     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46234 }
46235
46236
46237 Roo.htmleditor.KeyEnter.prototype = {
46238     
46239     core : false,
46240     
46241     keypress : function(e) {
46242         if (e.charCode != 13) {
46243             return true;
46244         }
46245         e.preventDefault();
46246         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46247         var doc = this.core.doc;
46248         
46249         var docFragment = doc.createDocumentFragment();
46250     
46251         //add a new line
46252         var newEle = doc.createTextNode('\n');
46253         docFragment.appendChild(newEle);
46254     
46255     
46256         var range = this.core.win.getSelection().getRangeAt(0);
46257         var n = range.commonAncestorContainer ;
46258         while (n && n.nodeType != 1) {
46259             n  = n.parentNode;
46260         }
46261         var li = false;
46262         if (n && n.tagName == 'UL') {
46263             li = doc.createElement('LI');
46264             n.appendChild(li);
46265             
46266         }
46267         if (n && n.tagName == 'LI') {
46268             li = doc.createElement('LI');
46269             if (n.nextSibling) {
46270                 n.parentNode.insertBefore(li, n.firstSibling);
46271                 
46272             } else {
46273                 n.parentNode.appendChild(li);
46274             }
46275         }
46276         if (li) {   
46277             range = doc.createRange();
46278             range.setStartAfter(li);
46279             range.collapse(true);
46280         
46281             //make the cursor there
46282             var sel = this.core.win.getSelection();
46283             sel.removeAllRanges();
46284             sel.addRange(range);
46285             return false;
46286             
46287             
46288         }
46289         //add the br, or p, or something else
46290         newEle = doc.createElement('br');
46291         docFragment.appendChild(newEle);
46292     
46293         //make the br replace selection
46294         
46295         range.deleteContents();
46296         
46297         range.insertNode(docFragment);
46298     
46299         //create a new range
46300         range = doc.createRange();
46301         range.setStartAfter(newEle);
46302         range.collapse(true);
46303     
46304         //make the cursor there
46305         var sel = this.core.win.getSelection();
46306         sel.removeAllRanges();
46307         sel.addRange(range);
46308     
46309         return false;
46310          
46311     }
46312 };
46313      
46314 /**
46315  * @class Roo.htmleditor.Block
46316  * Base class for html editor blocks - do not use it directly .. extend it..
46317  * @cfg {DomElement} node The node to apply stuff to.
46318  * @cfg {String} friendly_name the name that appears in the context bar about this block
46319  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46320  
46321  * @constructor
46322  * Create a new Filter.
46323  * @param {Object} config Configuration options
46324  */
46325
46326 Roo.htmleditor.Block  = function(cfg)
46327 {
46328     // do nothing .. should not be called really.
46329 }
46330
46331 Roo.htmleditor.Block.factory = function(node)
46332 {
46333     
46334     var id = Roo.get(node).id;
46335     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46336         Roo.htmleditor.Block.cache[id].readElement();
46337         return Roo.htmleditor.Block.cache[id];
46338     }
46339     
46340     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46341     if (typeof(cls) == 'undefined') {
46342         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46343         return false;
46344     }
46345     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46346     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46347 };
46348 // question goes here... do we need to clear out this cache sometimes?
46349 // or show we make it relivant to the htmleditor.
46350 Roo.htmleditor.Block.cache = {};
46351
46352 Roo.htmleditor.Block.prototype = {
46353     
46354     node : false,
46355     
46356      // used by context menu
46357     friendly_name : 'Image with caption',
46358     
46359     context : false,
46360     /**
46361      * Update a node with values from this object
46362      * @param {DomElement} node
46363      */
46364     updateElement : function(node)
46365     {
46366         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46367     },
46368      /**
46369      * convert to plain HTML for calling insertAtCursor..
46370      */
46371     toHTML : function()
46372     {
46373         return Roo.DomHelper.markup(this.toObject());
46374     },
46375     /**
46376      * used by readEleemnt to extract data from a node
46377      * may need improving as it's pretty basic
46378      
46379      * @param {DomElement} node
46380      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46381      * @param {String} attribute (use html - for contents, or style for using next param as style)
46382      * @param {String} style the style property - eg. text-align
46383      */
46384     getVal : function(node, tag, attr, style)
46385     {
46386         var n = node;
46387         if (tag !== true && n.tagName != tag.toUpperCase()) {
46388             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46389             // but kiss for now.
46390             n = node.getElementsByTagName(tag).item(0);
46391         }
46392         if (attr == 'html') {
46393             return n.innerHTML;
46394         }
46395         if (attr == 'style') {
46396             return Roo.get(n).getStyle(style);
46397         }
46398         
46399         return Roo.get(n).attr(attr);
46400             
46401     },
46402     /**
46403      * create a DomHelper friendly object - for use with 
46404      * Roo.DomHelper.markup / overwrite / etc..
46405      * (override this)
46406      */
46407     toObject : function()
46408     {
46409         return {};
46410     },
46411       /**
46412      * Read a node that has a 'data-block' property - and extract the values from it.
46413      * @param {DomElement} node - the node
46414      */
46415     readElement : function(node)
46416     {
46417         
46418     } 
46419     
46420     
46421 };
46422
46423  
46424
46425 /**
46426  * @class Roo.htmleditor.BlockFigure
46427  * Block that has an image and a figcaption
46428  * @cfg {String} image_src the url for the image
46429  * @cfg {String} align (left|right) alignment for the block default left
46430  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46431  * @cfg {String} caption the text to appear below  (and in the alt tag)
46432  * @cfg {String|number} image_width the width of the image number or %?
46433  * @cfg {String|number} image_height the height of the image number or %?
46434  * 
46435  * @constructor
46436  * Create a new Filter.
46437  * @param {Object} config Configuration options
46438  */
46439
46440 Roo.htmleditor.BlockFigure = function(cfg)
46441 {
46442     if (cfg.node) {
46443         this.readElement(cfg.node);
46444         this.updateElement(cfg.node);
46445     }
46446     Roo.apply(this, cfg);
46447 }
46448 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46449  
46450     
46451     // setable values.
46452     image_src: '',
46453     
46454     align: 'left',
46455     caption : '',
46456     text_align: 'left',
46457     
46458     width : '46%',
46459     margin: '2%',
46460     
46461     // used by context menu
46462     friendly_name : 'Image with caption',
46463     
46464     context : { // ?? static really
46465         width : {
46466             title: "Width",
46467             width: 40
46468             // ?? number
46469         },
46470         margin : {
46471             title: "Margin",
46472             width: 40
46473             // ?? number
46474         },
46475         align: {
46476             title: "Align",
46477             opts : [[ "left"],[ "right"]],
46478             width : 80
46479             
46480         },
46481         text_align: {
46482             title: "Caption Align",
46483             opts : [ [ "left"],[ "right"],[ "center"]],
46484             width : 80
46485         },
46486         
46487        
46488         image_src : {
46489             title: "Src",
46490             width: 220
46491         }
46492     },
46493     /**
46494      * create a DomHelper friendly object - for use with
46495      * Roo.DomHelper.markup / overwrite / etc..
46496      */
46497     toObject : function()
46498     {
46499         var d = document.createElement('div');
46500         d.innerHTML = this.caption;
46501         
46502         return {
46503             tag: 'figure',
46504             'data-block' : 'Figure',
46505             contenteditable : 'false',
46506             style : {
46507                 display: 'table',
46508                 float :  this.align ,
46509                 width :  this.width,
46510                 margin:  this.margin
46511             },
46512             cn : [
46513                 {
46514                     tag : 'img',
46515                     src : this.image_src,
46516                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46517                     style: {
46518                         width: '100%'
46519                     }
46520                 },
46521                 {
46522                     tag: 'figcaption',
46523                     contenteditable : true,
46524                     style : {
46525                         'text-align': this.text_align
46526                     },
46527                     html : this.caption
46528                     
46529                 }
46530             ]
46531         };
46532     },
46533     
46534     readElement : function(node)
46535     {
46536         this.image_src = this.getVal(node, 'img', 'src');
46537         this.align = this.getVal(node, 'figure', 'style', 'float');
46538         this.caption = this.getVal(node, 'figcaption', 'html');
46539         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46540         this.width = this.getVal(node, 'figure', 'style', 'width');
46541         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46542         
46543     } 
46544     
46545   
46546    
46547      
46548     
46549     
46550     
46551     
46552 })
46553
46554 //<script type="text/javascript">
46555
46556 /*
46557  * Based  Ext JS Library 1.1.1
46558  * Copyright(c) 2006-2007, Ext JS, LLC.
46559  * LGPL
46560  *
46561  */
46562  
46563 /**
46564  * @class Roo.HtmlEditorCore
46565  * @extends Roo.Component
46566  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46567  *
46568  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46569  */
46570
46571 Roo.HtmlEditorCore = function(config){
46572     
46573     
46574     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46575     
46576     
46577     this.addEvents({
46578         /**
46579          * @event initialize
46580          * Fires when the editor is fully initialized (including the iframe)
46581          * @param {Roo.HtmlEditorCore} this
46582          */
46583         initialize: true,
46584         /**
46585          * @event activate
46586          * Fires when the editor is first receives the focus. Any insertion must wait
46587          * until after this event.
46588          * @param {Roo.HtmlEditorCore} this
46589          */
46590         activate: true,
46591          /**
46592          * @event beforesync
46593          * Fires before the textarea is updated with content from the editor iframe. Return false
46594          * to cancel the sync.
46595          * @param {Roo.HtmlEditorCore} this
46596          * @param {String} html
46597          */
46598         beforesync: true,
46599          /**
46600          * @event beforepush
46601          * Fires before the iframe editor is updated with content from the textarea. Return false
46602          * to cancel the push.
46603          * @param {Roo.HtmlEditorCore} this
46604          * @param {String} html
46605          */
46606         beforepush: true,
46607          /**
46608          * @event sync
46609          * Fires when the textarea is updated with content from the editor iframe.
46610          * @param {Roo.HtmlEditorCore} this
46611          * @param {String} html
46612          */
46613         sync: true,
46614          /**
46615          * @event push
46616          * Fires when the iframe editor is updated with content from the textarea.
46617          * @param {Roo.HtmlEditorCore} this
46618          * @param {String} html
46619          */
46620         push: true,
46621         
46622         /**
46623          * @event editorevent
46624          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46625          * @param {Roo.HtmlEditorCore} this
46626          */
46627         editorevent: true
46628         
46629     });
46630     
46631     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46632     
46633     // defaults : white / black...
46634     this.applyBlacklists();
46635     
46636     
46637     
46638 };
46639
46640
46641 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46642
46643
46644      /**
46645      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46646      */
46647     
46648     owner : false,
46649     
46650      /**
46651      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46652      *                        Roo.resizable.
46653      */
46654     resizable : false,
46655      /**
46656      * @cfg {Number} height (in pixels)
46657      */   
46658     height: 300,
46659    /**
46660      * @cfg {Number} width (in pixels)
46661      */   
46662     width: 500,
46663     
46664     /**
46665      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46666      * 
46667      */
46668     stylesheets: false,
46669     
46670     /**
46671      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46672      */
46673     allowComments: false,
46674     // id of frame..
46675     frameId: false,
46676     
46677     // private properties
46678     validationEvent : false,
46679     deferHeight: true,
46680     initialized : false,
46681     activated : false,
46682     sourceEditMode : false,
46683     onFocus : Roo.emptyFn,
46684     iframePad:3,
46685     hideMode:'offsets',
46686     
46687     clearUp: true,
46688     
46689     // blacklist + whitelisted elements..
46690     black: false,
46691     white: false,
46692      
46693     bodyCls : '',
46694
46695     
46696     undoManager : false,
46697     /**
46698      * Protected method that will not generally be called directly. It
46699      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46700      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46701      */
46702     getDocMarkup : function(){
46703         // body styles..
46704         var st = '';
46705         
46706         // inherit styels from page...?? 
46707         if (this.stylesheets === false) {
46708             
46709             Roo.get(document.head).select('style').each(function(node) {
46710                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46711             });
46712             
46713             Roo.get(document.head).select('link').each(function(node) { 
46714                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46715             });
46716             
46717         } else if (!this.stylesheets.length) {
46718                 // simple..
46719                 st = '<style type="text/css">' +
46720                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46721                    '</style>';
46722         } else {
46723             for (var i in this.stylesheets) {
46724                 if (typeof(this.stylesheets[i]) != 'string') {
46725                     continue;
46726                 }
46727                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46728             }
46729             
46730         }
46731         
46732         st +=  '<style type="text/css">' +
46733             'IMG { cursor: pointer } ' +
46734         '</style>';
46735
46736         var cls = 'roo-htmleditor-body';
46737         
46738         if(this.bodyCls.length){
46739             cls += ' ' + this.bodyCls;
46740         }
46741         
46742         return '<html><head>' + st  +
46743             //<style type="text/css">' +
46744             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46745             //'</style>' +
46746             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46747     },
46748
46749     // private
46750     onRender : function(ct, position)
46751     {
46752         var _t = this;
46753         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46754         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46755         
46756         
46757         this.el.dom.style.border = '0 none';
46758         this.el.dom.setAttribute('tabIndex', -1);
46759         this.el.addClass('x-hidden hide');
46760         
46761         
46762         
46763         if(Roo.isIE){ // fix IE 1px bogus margin
46764             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46765         }
46766        
46767         
46768         this.frameId = Roo.id();
46769         
46770          
46771         
46772         var iframe = this.owner.wrap.createChild({
46773             tag: 'iframe',
46774             cls: 'form-control', // bootstrap..
46775             id: this.frameId,
46776             name: this.frameId,
46777             frameBorder : 'no',
46778             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46779         }, this.el
46780         );
46781         
46782         
46783         this.iframe = iframe.dom;
46784
46785         this.assignDocWin();
46786         
46787         this.doc.designMode = 'on';
46788        
46789         this.doc.open();
46790         this.doc.write(this.getDocMarkup());
46791         this.doc.close();
46792
46793         
46794         var task = { // must defer to wait for browser to be ready
46795             run : function(){
46796                 //console.log("run task?" + this.doc.readyState);
46797                 this.assignDocWin();
46798                 if(this.doc.body || this.doc.readyState == 'complete'){
46799                     try {
46800                         this.doc.designMode="on";
46801                         
46802                     } catch (e) {
46803                         return;
46804                     }
46805                     Roo.TaskMgr.stop(task);
46806                     this.initEditor.defer(10, this);
46807                 }
46808             },
46809             interval : 10,
46810             duration: 10000,
46811             scope: this
46812         };
46813         Roo.TaskMgr.start(task);
46814
46815     },
46816
46817     // private
46818     onResize : function(w, h)
46819     {
46820          Roo.log('resize: ' +w + ',' + h );
46821         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46822         if(!this.iframe){
46823             return;
46824         }
46825         if(typeof w == 'number'){
46826             
46827             this.iframe.style.width = w + 'px';
46828         }
46829         if(typeof h == 'number'){
46830             
46831             this.iframe.style.height = h + 'px';
46832             if(this.doc){
46833                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46834             }
46835         }
46836         
46837     },
46838
46839     /**
46840      * Toggles the editor between standard and source edit mode.
46841      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46842      */
46843     toggleSourceEdit : function(sourceEditMode){
46844         
46845         this.sourceEditMode = sourceEditMode === true;
46846         
46847         if(this.sourceEditMode){
46848  
46849             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46850             
46851         }else{
46852             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46853             //this.iframe.className = '';
46854             this.deferFocus();
46855         }
46856         //this.setSize(this.owner.wrap.getSize());
46857         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46858     },
46859
46860     
46861   
46862
46863     /**
46864      * Protected method that will not generally be called directly. If you need/want
46865      * custom HTML cleanup, this is the method you should override.
46866      * @param {String} html The HTML to be cleaned
46867      * return {String} The cleaned HTML
46868      */
46869     cleanHtml : function(html){
46870         html = String(html);
46871         if(html.length > 5){
46872             if(Roo.isSafari){ // strip safari nonsense
46873                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46874             }
46875         }
46876         if(html == '&nbsp;'){
46877             html = '';
46878         }
46879         return html;
46880     },
46881
46882     /**
46883      * HTML Editor -> Textarea
46884      * Protected method that will not generally be called directly. Syncs the contents
46885      * of the editor iframe with the textarea.
46886      */
46887     syncValue : function()
46888     {
46889         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46890         if(this.initialized){
46891             
46892             this.undoManager.addEvent();
46893
46894             
46895             var bd = (this.doc.body || this.doc.documentElement);
46896             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46897             
46898             // not sure if this is really the place for this
46899             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46900             // this has to update attributes that get duped.. like alt and caption..
46901             
46902             
46903             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46904             //     Roo.htmleditor.Block.factory(e);
46905             //},this);
46906             
46907             
46908             var div = document.createElement('div');
46909             div.innerHTML = bd.innerHTML;
46910             // remove content editable. (blocks)
46911             
46912            
46913             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46914             //?? tidy?
46915             var html = div.innerHTML;
46916             if(Roo.isSafari){
46917                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46918                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46919                 if(m && m[1]){
46920                     html = '<div style="'+m[0]+'">' + html + '</div>';
46921                 }
46922             }
46923             html = this.cleanHtml(html);
46924             // fix up the special chars.. normaly like back quotes in word...
46925             // however we do not want to do this with chinese..
46926             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46927                 
46928                 var cc = match.charCodeAt();
46929
46930                 // Get the character value, handling surrogate pairs
46931                 if (match.length == 2) {
46932                     // It's a surrogate pair, calculate the Unicode code point
46933                     var high = match.charCodeAt(0) - 0xD800;
46934                     var low  = match.charCodeAt(1) - 0xDC00;
46935                     cc = (high * 0x400) + low + 0x10000;
46936                 }  else if (
46937                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46938                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46939                     (cc >= 0xf900 && cc < 0xfb00 )
46940                 ) {
46941                         return match;
46942                 }  
46943          
46944                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46945                 return "&#" + cc + ";";
46946                 
46947                 
46948             });
46949             
46950             
46951              
46952             if(this.owner.fireEvent('beforesync', this, html) !== false){
46953                 this.el.dom.value = html;
46954                 this.owner.fireEvent('sync', this, html);
46955             }
46956         }
46957     },
46958
46959     /**
46960      * TEXTAREA -> EDITABLE
46961      * Protected method that will not generally be called directly. Pushes the value of the textarea
46962      * into the iframe editor.
46963      */
46964     pushValue : function()
46965     {
46966         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46967         if(this.initialized){
46968             var v = this.el.dom.value.trim();
46969             
46970             
46971             if(this.owner.fireEvent('beforepush', this, v) !== false){
46972                 var d = (this.doc.body || this.doc.documentElement);
46973                 d.innerHTML = v;
46974                  
46975                 this.el.dom.value = d.innerHTML;
46976                 this.owner.fireEvent('push', this, v);
46977             }
46978             
46979             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46980                 
46981                 Roo.htmleditor.Block.factory(e);
46982                 
46983             },this);
46984             var lc = this.doc.body.lastChild;
46985             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46986                 // add an extra line at the end.
46987                 this.doc.body.appendChild(this.doc.createElement('br'));
46988             }
46989             
46990             
46991         }
46992     },
46993
46994     // private
46995     deferFocus : function(){
46996         this.focus.defer(10, this);
46997     },
46998
46999     // doc'ed in Field
47000     focus : function(){
47001         if(this.win && !this.sourceEditMode){
47002             this.win.focus();
47003         }else{
47004             this.el.focus();
47005         }
47006     },
47007     
47008     assignDocWin: function()
47009     {
47010         var iframe = this.iframe;
47011         
47012          if(Roo.isIE){
47013             this.doc = iframe.contentWindow.document;
47014             this.win = iframe.contentWindow;
47015         } else {
47016 //            if (!Roo.get(this.frameId)) {
47017 //                return;
47018 //            }
47019 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47020 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47021             
47022             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47023                 return;
47024             }
47025             
47026             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47027             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47028         }
47029     },
47030     
47031     // private
47032     initEditor : function(){
47033         //console.log("INIT EDITOR");
47034         this.assignDocWin();
47035         
47036         
47037         
47038         this.doc.designMode="on";
47039         this.doc.open();
47040         this.doc.write(this.getDocMarkup());
47041         this.doc.close();
47042         
47043         var dbody = (this.doc.body || this.doc.documentElement);
47044         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47045         // this copies styles from the containing element into thsi one..
47046         // not sure why we need all of this..
47047         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47048         
47049         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47050         //ss['background-attachment'] = 'fixed'; // w3c
47051         dbody.bgProperties = 'fixed'; // ie
47052         //Roo.DomHelper.applyStyles(dbody, ss);
47053         Roo.EventManager.on(this.doc, {
47054             //'mousedown': this.onEditorEvent,
47055             'mouseup': this.onEditorEvent,
47056             'dblclick': this.onEditorEvent,
47057             'click': this.onEditorEvent,
47058             'keyup': this.onEditorEvent,
47059             
47060             buffer:100,
47061             scope: this
47062         });
47063         Roo.EventManager.on(this.doc, {
47064             'paste': this.onPasteEvent,
47065             scope : this
47066         });
47067         if(Roo.isGecko){
47068             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47069         }
47070         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47071             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47072         }
47073         this.initialized = true;
47074
47075         
47076         // initialize special key events - enter
47077         new Roo.htmleditor.KeyEnter({core : this});
47078         
47079          
47080         
47081         this.owner.fireEvent('initialize', this);
47082         this.pushValue();
47083     },
47084     
47085     onPasteEvent : function(e,v)
47086     {
47087         // I think we better assume paste is going to be a dirty load of rubish from word..
47088         
47089         // even pasting into a 'email version' of this widget will have to clean up that mess.
47090         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47091         
47092         // check what type of paste - if it's an image, then handle it differently.
47093         if (cd.files.length > 0) {
47094             // pasting images?
47095             var urlAPI = (window.createObjectURL && window) || 
47096                 (window.URL && URL.revokeObjectURL && URL) || 
47097                 (window.webkitURL && webkitURL);
47098     
47099             var url = urlAPI.createObjectURL( cd.files[0]);
47100             this.insertAtCursor('<img src=" + url + ">');
47101             return false;
47102         }
47103         
47104         var html = cd.getData('text/html'); // clipboard event
47105         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47106         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47107         Roo.log(images);
47108         //Roo.log(imgs);
47109         // fixme..
47110         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47111                        .map(function(g) { return g.toDataURL(); });
47112         
47113         
47114         html = this.cleanWordChars(html);
47115         
47116         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47117         
47118         if (images.length > 0) {
47119             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47120                 img.setAttribute('src', images[i]);
47121             });
47122         }
47123         
47124       
47125         new Roo.htmleditor.FilterStyleToTag({ node : d });
47126         new Roo.htmleditor.FilterAttributes({
47127             node : d,
47128             attrib_white : ['href', 'src', 'name', 'align'],
47129             attrib_clean : ['href', 'src' ] 
47130         });
47131         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47132         // should be fonts..
47133         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47134         new Roo.htmleditor.FilterParagraph({ node : d });
47135         new Roo.htmleditor.FilterSpan({ node : d });
47136         new Roo.htmleditor.FilterLongBr({ node : d });
47137         
47138         
47139         
47140         this.insertAtCursor(d.innerHTML);
47141         
47142         e.preventDefault();
47143         return false;
47144         // default behaveiour should be our local cleanup paste? (optional?)
47145         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47146         //this.owner.fireEvent('paste', e, v);
47147     },
47148     // private
47149     onDestroy : function(){
47150         
47151         
47152         
47153         if(this.rendered){
47154             
47155             //for (var i =0; i < this.toolbars.length;i++) {
47156             //    // fixme - ask toolbars for heights?
47157             //    this.toolbars[i].onDestroy();
47158            // }
47159             
47160             //this.wrap.dom.innerHTML = '';
47161             //this.wrap.remove();
47162         }
47163     },
47164
47165     // private
47166     onFirstFocus : function(){
47167         
47168         this.assignDocWin();
47169         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47170         
47171         this.activated = true;
47172          
47173     
47174         if(Roo.isGecko){ // prevent silly gecko errors
47175             this.win.focus();
47176             var s = this.win.getSelection();
47177             if(!s.focusNode || s.focusNode.nodeType != 3){
47178                 var r = s.getRangeAt(0);
47179                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47180                 r.collapse(true);
47181                 this.deferFocus();
47182             }
47183             try{
47184                 this.execCmd('useCSS', true);
47185                 this.execCmd('styleWithCSS', false);
47186             }catch(e){}
47187         }
47188         this.owner.fireEvent('activate', this);
47189     },
47190
47191     // private
47192     adjustFont: function(btn){
47193         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47194         //if(Roo.isSafari){ // safari
47195         //    adjust *= 2;
47196        // }
47197         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47198         if(Roo.isSafari){ // safari
47199             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47200             v =  (v < 10) ? 10 : v;
47201             v =  (v > 48) ? 48 : v;
47202             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47203             
47204         }
47205         
47206         
47207         v = Math.max(1, v+adjust);
47208         
47209         this.execCmd('FontSize', v  );
47210     },
47211
47212     onEditorEvent : function(e)
47213     {
47214         this.owner.fireEvent('editorevent', this, e);
47215       //  this.updateToolbar();
47216         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47217     },
47218
47219     insertTag : function(tg)
47220     {
47221         // could be a bit smarter... -> wrap the current selected tRoo..
47222         if (tg.toLowerCase() == 'span' ||
47223             tg.toLowerCase() == 'code' ||
47224             tg.toLowerCase() == 'sup' ||
47225             tg.toLowerCase() == 'sub' 
47226             ) {
47227             
47228             range = this.createRange(this.getSelection());
47229             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47230             wrappingNode.appendChild(range.extractContents());
47231             range.insertNode(wrappingNode);
47232
47233             return;
47234             
47235             
47236             
47237         }
47238         this.execCmd("formatblock",   tg);
47239         this.undoManager.addEvent(); 
47240     },
47241     
47242     insertText : function(txt)
47243     {
47244         
47245         
47246         var range = this.createRange();
47247         range.deleteContents();
47248                //alert(Sender.getAttribute('label'));
47249                
47250         range.insertNode(this.doc.createTextNode(txt));
47251         this.undoManager.addEvent();
47252     } ,
47253     
47254      
47255
47256     /**
47257      * Executes a Midas editor command on the editor document and performs necessary focus and
47258      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47259      * @param {String} cmd The Midas command
47260      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47261      */
47262     relayCmd : function(cmd, value){
47263         this.win.focus();
47264         this.execCmd(cmd, value);
47265         this.owner.fireEvent('editorevent', this);
47266         //this.updateToolbar();
47267         this.owner.deferFocus();
47268     },
47269
47270     /**
47271      * Executes a Midas editor command directly on the editor document.
47272      * For visual commands, you should use {@link #relayCmd} instead.
47273      * <b>This should only be called after the editor is initialized.</b>
47274      * @param {String} cmd The Midas command
47275      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47276      */
47277     execCmd : function(cmd, value){
47278         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47279         this.syncValue();
47280     },
47281  
47282  
47283    
47284     /**
47285      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47286      * to insert tRoo.
47287      * @param {String} text | dom node.. 
47288      */
47289     insertAtCursor : function(text)
47290     {
47291         
47292         if(!this.activated){
47293             return;
47294         }
47295          
47296         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47297             this.win.focus();
47298             
47299             
47300             // from jquery ui (MIT licenced)
47301             var range, node;
47302             var win = this.win;
47303             
47304             if (win.getSelection && win.getSelection().getRangeAt) {
47305                 
47306                 // delete the existing?
47307                 
47308                 this.createRange(this.getSelection()).deleteContents();
47309                 range = win.getSelection().getRangeAt(0);
47310                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47311                 range.insertNode(node);
47312                 range = range.cloneRange();
47313                 range.collapse(false);
47314                  
47315                 win.getSelection().removeAllRanges();
47316                 win.getSelection().addRange(range);
47317                 
47318                 
47319                 
47320             } else if (win.document.selection && win.document.selection.createRange) {
47321                 // no firefox support
47322                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47323                 win.document.selection.createRange().pasteHTML(txt);
47324             
47325             } else {
47326                 // no firefox support
47327                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47328                 this.execCmd('InsertHTML', txt);
47329             } 
47330             this.syncValue();
47331             
47332             this.deferFocus();
47333         }
47334     },
47335  // private
47336     mozKeyPress : function(e){
47337         if(e.ctrlKey){
47338             var c = e.getCharCode(), cmd;
47339           
47340             if(c > 0){
47341                 c = String.fromCharCode(c).toLowerCase();
47342                 switch(c){
47343                     case 'b':
47344                         cmd = 'bold';
47345                         break;
47346                     case 'i':
47347                         cmd = 'italic';
47348                         break;
47349                     
47350                     case 'u':
47351                         cmd = 'underline';
47352                         break;
47353                     
47354                     //case 'v':
47355                       //  this.cleanUpPaste.defer(100, this);
47356                       //  return;
47357                         
47358                 }
47359                 if(cmd){
47360                     this.win.focus();
47361                     this.execCmd(cmd);
47362                     this.deferFocus();
47363                     e.preventDefault();
47364                 }
47365                 
47366             }
47367         }
47368     },
47369
47370     // private
47371     fixKeys : function(){ // load time branching for fastest keydown performance
47372         if(Roo.isIE){
47373             return function(e){
47374                 var k = e.getKey(), r;
47375                 if(k == e.TAB){
47376                     e.stopEvent();
47377                     r = this.doc.selection.createRange();
47378                     if(r){
47379                         r.collapse(true);
47380                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47381                         this.deferFocus();
47382                     }
47383                     return;
47384                 }
47385                 
47386                 if(k == e.ENTER){
47387                     r = this.doc.selection.createRange();
47388                     if(r){
47389                         var target = r.parentElement();
47390                         if(!target || target.tagName.toLowerCase() != 'li'){
47391                             e.stopEvent();
47392                             r.pasteHTML('<br/>');
47393                             r.collapse(false);
47394                             r.select();
47395                         }
47396                     }
47397                 }
47398                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47399                 //    this.cleanUpPaste.defer(100, this);
47400                 //    return;
47401                 //}
47402                 
47403                 
47404             };
47405         }else if(Roo.isOpera){
47406             return function(e){
47407                 var k = e.getKey();
47408                 if(k == e.TAB){
47409                     e.stopEvent();
47410                     this.win.focus();
47411                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47412                     this.deferFocus();
47413                 }
47414                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47415                 //    this.cleanUpPaste.defer(100, this);
47416                  //   return;
47417                 //}
47418                 
47419             };
47420         }else if(Roo.isSafari){
47421             return function(e){
47422                 var k = e.getKey();
47423                 
47424                 if(k == e.TAB){
47425                     e.stopEvent();
47426                     this.execCmd('InsertText','\t');
47427                     this.deferFocus();
47428                     return;
47429                 }
47430                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47431                  //   this.cleanUpPaste.defer(100, this);
47432                  //   return;
47433                // }
47434                 
47435              };
47436         }
47437     }(),
47438     
47439     getAllAncestors: function()
47440     {
47441         var p = this.getSelectedNode();
47442         var a = [];
47443         if (!p) {
47444             a.push(p); // push blank onto stack..
47445             p = this.getParentElement();
47446         }
47447         
47448         
47449         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47450             a.push(p);
47451             p = p.parentNode;
47452         }
47453         a.push(this.doc.body);
47454         return a;
47455     },
47456     lastSel : false,
47457     lastSelNode : false,
47458     
47459     
47460     getSelection : function() 
47461     {
47462         this.assignDocWin();
47463         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47464     },
47465     /**
47466      * Select a dom node
47467      * @param {DomElement} node the node to select
47468      */
47469     selectNode : function(node)
47470     {
47471         Roo.select('.roo-ed-selection', false, this.doc).removeClass('roo-ed-selection');
47472         Roo.get(node).addClass('roo-ed-selection');
47473         var nodeRange = node.ownerDocument.createRange();
47474         try {
47475             nodeRange.selectNode(node);
47476         } catch (e) {
47477             nodeRange.selectNodeContents(node);
47478         }
47479         //nodeRange.collapse(true);
47480         var s = this.win.getSelection();
47481         s.removeAllRanges();
47482         s.addRange(nodeRange);
47483     },
47484     
47485     getSelectedNode: function() 
47486     {
47487         // this may only work on Gecko!!!
47488         
47489         // should we cache this!!!!
47490         
47491         
47492         
47493          
47494         var range = this.createRange(this.getSelection()).cloneRange();
47495         
47496         if (Roo.isIE) {
47497             var parent = range.parentElement();
47498             while (true) {
47499                 var testRange = range.duplicate();
47500                 testRange.moveToElementText(parent);
47501                 if (testRange.inRange(range)) {
47502                     break;
47503                 }
47504                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47505                     break;
47506                 }
47507                 parent = parent.parentElement;
47508             }
47509             return parent;
47510         }
47511         
47512         // is ancestor a text element.
47513         var ac =  range.commonAncestorContainer;
47514         if (ac.nodeType == 3) {
47515             ac = ac.parentNode;
47516         }
47517         
47518         var ar = ac.childNodes;
47519          
47520         var nodes = [];
47521         var other_nodes = [];
47522         var has_other_nodes = false;
47523         for (var i=0;i<ar.length;i++) {
47524             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47525                 continue;
47526             }
47527             // fullly contained node.
47528             
47529             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47530                 nodes.push(ar[i]);
47531                 continue;
47532             }
47533             
47534             // probably selected..
47535             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47536                 other_nodes.push(ar[i]);
47537                 continue;
47538             }
47539             // outer..
47540             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47541                 continue;
47542             }
47543             
47544             
47545             has_other_nodes = true;
47546         }
47547         if (!nodes.length && other_nodes.length) {
47548             nodes= other_nodes;
47549         }
47550         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47551             return false;
47552         }
47553         
47554         return nodes[0];
47555     },
47556     createRange: function(sel)
47557     {
47558         // this has strange effects when using with 
47559         // top toolbar - not sure if it's a great idea.
47560         //this.editor.contentWindow.focus();
47561         if (typeof sel != "undefined") {
47562             try {
47563                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47564             } catch(e) {
47565                 return this.doc.createRange();
47566             }
47567         } else {
47568             return this.doc.createRange();
47569         }
47570     },
47571     getParentElement: function()
47572     {
47573         
47574         this.assignDocWin();
47575         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47576         
47577         var range = this.createRange(sel);
47578          
47579         try {
47580             var p = range.commonAncestorContainer;
47581             while (p.nodeType == 3) { // text node
47582                 p = p.parentNode;
47583             }
47584             return p;
47585         } catch (e) {
47586             return null;
47587         }
47588     
47589     },
47590     /***
47591      *
47592      * Range intersection.. the hard stuff...
47593      *  '-1' = before
47594      *  '0' = hits..
47595      *  '1' = after.
47596      *         [ -- selected range --- ]
47597      *   [fail]                        [fail]
47598      *
47599      *    basically..
47600      *      if end is before start or  hits it. fail.
47601      *      if start is after end or hits it fail.
47602      *
47603      *   if either hits (but other is outside. - then it's not 
47604      *   
47605      *    
47606      **/
47607     
47608     
47609     // @see http://www.thismuchiknow.co.uk/?p=64.
47610     rangeIntersectsNode : function(range, node)
47611     {
47612         var nodeRange = node.ownerDocument.createRange();
47613         try {
47614             nodeRange.selectNode(node);
47615         } catch (e) {
47616             nodeRange.selectNodeContents(node);
47617         }
47618     
47619         var rangeStartRange = range.cloneRange();
47620         rangeStartRange.collapse(true);
47621     
47622         var rangeEndRange = range.cloneRange();
47623         rangeEndRange.collapse(false);
47624     
47625         var nodeStartRange = nodeRange.cloneRange();
47626         nodeStartRange.collapse(true);
47627     
47628         var nodeEndRange = nodeRange.cloneRange();
47629         nodeEndRange.collapse(false);
47630     
47631         return rangeStartRange.compareBoundaryPoints(
47632                  Range.START_TO_START, nodeEndRange) == -1 &&
47633                rangeEndRange.compareBoundaryPoints(
47634                  Range.START_TO_START, nodeStartRange) == 1;
47635         
47636          
47637     },
47638     rangeCompareNode : function(range, node)
47639     {
47640         var nodeRange = node.ownerDocument.createRange();
47641         try {
47642             nodeRange.selectNode(node);
47643         } catch (e) {
47644             nodeRange.selectNodeContents(node);
47645         }
47646         
47647         
47648         range.collapse(true);
47649     
47650         nodeRange.collapse(true);
47651      
47652         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47653         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47654          
47655         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47656         
47657         var nodeIsBefore   =  ss == 1;
47658         var nodeIsAfter    = ee == -1;
47659         
47660         if (nodeIsBefore && nodeIsAfter) {
47661             return 0; // outer
47662         }
47663         if (!nodeIsBefore && nodeIsAfter) {
47664             return 1; //right trailed.
47665         }
47666         
47667         if (nodeIsBefore && !nodeIsAfter) {
47668             return 2;  // left trailed.
47669         }
47670         // fully contined.
47671         return 3;
47672     },
47673  
47674     cleanWordChars : function(input) {// change the chars to hex code
47675         
47676        var swapCodes  = [ 
47677             [    8211, "&#8211;" ], 
47678             [    8212, "&#8212;" ], 
47679             [    8216,  "'" ],  
47680             [    8217, "'" ],  
47681             [    8220, '"' ],  
47682             [    8221, '"' ],  
47683             [    8226, "*" ],  
47684             [    8230, "..." ]
47685         ]; 
47686         var output = input;
47687         Roo.each(swapCodes, function(sw) { 
47688             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47689             
47690             output = output.replace(swapper, sw[1]);
47691         });
47692         
47693         return output;
47694     },
47695     
47696      
47697     
47698         
47699     
47700     cleanUpChild : function (node)
47701     {
47702         
47703         new Roo.htmleditor.FilterComment({node : node});
47704         new Roo.htmleditor.FilterAttributes({
47705                 node : node,
47706                 attrib_black : this.ablack,
47707                 attrib_clean : this.aclean,
47708                 style_white : this.cwhite,
47709                 style_black : this.cblack
47710         });
47711         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47712         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47713          
47714         
47715     },
47716     
47717     /**
47718      * Clean up MS wordisms...
47719      * @deprecated - use filter directly
47720      */
47721     cleanWord : function(node)
47722     {
47723         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47724         
47725     },
47726    
47727     
47728     /**
47729
47730      * @deprecated - use filters
47731      */
47732     cleanTableWidths : function(node)
47733     {
47734         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47735         
47736  
47737     },
47738     
47739      
47740         
47741     applyBlacklists : function()
47742     {
47743         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47744         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47745         
47746         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47747         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47748         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47749         
47750         this.white = [];
47751         this.black = [];
47752         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47753             if (b.indexOf(tag) > -1) {
47754                 return;
47755             }
47756             this.white.push(tag);
47757             
47758         }, this);
47759         
47760         Roo.each(w, function(tag) {
47761             if (b.indexOf(tag) > -1) {
47762                 return;
47763             }
47764             if (this.white.indexOf(tag) > -1) {
47765                 return;
47766             }
47767             this.white.push(tag);
47768             
47769         }, this);
47770         
47771         
47772         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47773             if (w.indexOf(tag) > -1) {
47774                 return;
47775             }
47776             this.black.push(tag);
47777             
47778         }, this);
47779         
47780         Roo.each(b, function(tag) {
47781             if (w.indexOf(tag) > -1) {
47782                 return;
47783             }
47784             if (this.black.indexOf(tag) > -1) {
47785                 return;
47786             }
47787             this.black.push(tag);
47788             
47789         }, this);
47790         
47791         
47792         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47793         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47794         
47795         this.cwhite = [];
47796         this.cblack = [];
47797         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47798             if (b.indexOf(tag) > -1) {
47799                 return;
47800             }
47801             this.cwhite.push(tag);
47802             
47803         }, this);
47804         
47805         Roo.each(w, function(tag) {
47806             if (b.indexOf(tag) > -1) {
47807                 return;
47808             }
47809             if (this.cwhite.indexOf(tag) > -1) {
47810                 return;
47811             }
47812             this.cwhite.push(tag);
47813             
47814         }, this);
47815         
47816         
47817         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47818             if (w.indexOf(tag) > -1) {
47819                 return;
47820             }
47821             this.cblack.push(tag);
47822             
47823         }, this);
47824         
47825         Roo.each(b, function(tag) {
47826             if (w.indexOf(tag) > -1) {
47827                 return;
47828             }
47829             if (this.cblack.indexOf(tag) > -1) {
47830                 return;
47831             }
47832             this.cblack.push(tag);
47833             
47834         }, this);
47835     },
47836     
47837     setStylesheets : function(stylesheets)
47838     {
47839         if(typeof(stylesheets) == 'string'){
47840             Roo.get(this.iframe.contentDocument.head).createChild({
47841                 tag : 'link',
47842                 rel : 'stylesheet',
47843                 type : 'text/css',
47844                 href : stylesheets
47845             });
47846             
47847             return;
47848         }
47849         var _this = this;
47850      
47851         Roo.each(stylesheets, function(s) {
47852             if(!s.length){
47853                 return;
47854             }
47855             
47856             Roo.get(_this.iframe.contentDocument.head).createChild({
47857                 tag : 'link',
47858                 rel : 'stylesheet',
47859                 type : 'text/css',
47860                 href : s
47861             });
47862         });
47863
47864         
47865     },
47866     
47867     removeStylesheets : function()
47868     {
47869         var _this = this;
47870         
47871         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47872             s.remove();
47873         });
47874     },
47875     
47876     setStyle : function(style)
47877     {
47878         Roo.get(this.iframe.contentDocument.head).createChild({
47879             tag : 'style',
47880             type : 'text/css',
47881             html : style
47882         });
47883
47884         return;
47885     }
47886     
47887     // hide stuff that is not compatible
47888     /**
47889      * @event blur
47890      * @hide
47891      */
47892     /**
47893      * @event change
47894      * @hide
47895      */
47896     /**
47897      * @event focus
47898      * @hide
47899      */
47900     /**
47901      * @event specialkey
47902      * @hide
47903      */
47904     /**
47905      * @cfg {String} fieldClass @hide
47906      */
47907     /**
47908      * @cfg {String} focusClass @hide
47909      */
47910     /**
47911      * @cfg {String} autoCreate @hide
47912      */
47913     /**
47914      * @cfg {String} inputType @hide
47915      */
47916     /**
47917      * @cfg {String} invalidClass @hide
47918      */
47919     /**
47920      * @cfg {String} invalidText @hide
47921      */
47922     /**
47923      * @cfg {String} msgFx @hide
47924      */
47925     /**
47926      * @cfg {String} validateOnBlur @hide
47927      */
47928 });
47929
47930 Roo.HtmlEditorCore.white = [
47931         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47932         
47933        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47934        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47935        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47936        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47937        'TABLE',   'UL',         'XMP', 
47938        
47939        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47940       'THEAD',   'TR', 
47941      
47942       'DIR', 'MENU', 'OL', 'UL', 'DL',
47943        
47944       'EMBED',  'OBJECT'
47945 ];
47946
47947
47948 Roo.HtmlEditorCore.black = [
47949     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47950         'APPLET', // 
47951         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47952         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47953         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47954         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47955         //'FONT' // CLEAN LATER..
47956         'COLGROUP', 'COL'  // messy tables.
47957         
47958 ];
47959 Roo.HtmlEditorCore.clean = [ // ?? needed???
47960      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47961 ];
47962 Roo.HtmlEditorCore.tag_remove = [
47963     'FONT', 'TBODY'  
47964 ];
47965 // attributes..
47966
47967 Roo.HtmlEditorCore.ablack = [
47968     'on'
47969 ];
47970     
47971 Roo.HtmlEditorCore.aclean = [ 
47972     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47973 ];
47974
47975 // protocols..
47976 Roo.HtmlEditorCore.pwhite= [
47977         'http',  'https',  'mailto'
47978 ];
47979
47980 // white listed style attributes.
47981 Roo.HtmlEditorCore.cwhite= [
47982       //  'text-align', /// default is to allow most things..
47983       
47984          
47985 //        'font-size'//??
47986 ];
47987
47988 // black listed style attributes.
47989 Roo.HtmlEditorCore.cblack= [
47990       //  'font-size' -- this can be set by the project 
47991 ];
47992
47993
47994
47995
47996     //<script type="text/javascript">
47997
47998 /*
47999  * Ext JS Library 1.1.1
48000  * Copyright(c) 2006-2007, Ext JS, LLC.
48001  * Licence LGPL
48002  * 
48003  */
48004  
48005  
48006 Roo.form.HtmlEditor = function(config){
48007     
48008     
48009     
48010     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48011     
48012     if (!this.toolbars) {
48013         this.toolbars = [];
48014     }
48015     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48016     
48017     
48018 };
48019
48020 /**
48021  * @class Roo.form.HtmlEditor
48022  * @extends Roo.form.Field
48023  * Provides a lightweight HTML Editor component.
48024  *
48025  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48026  * 
48027  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48028  * supported by this editor.</b><br/><br/>
48029  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48030  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48031  */
48032 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48033     /**
48034      * @cfg {Boolean} clearUp
48035      */
48036     clearUp : true,
48037       /**
48038      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48039      */
48040     toolbars : false,
48041    
48042      /**
48043      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48044      *                        Roo.resizable.
48045      */
48046     resizable : false,
48047      /**
48048      * @cfg {Number} height (in pixels)
48049      */   
48050     height: 300,
48051    /**
48052      * @cfg {Number} width (in pixels)
48053      */   
48054     width: 500,
48055     
48056     /**
48057      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48058      * 
48059      */
48060     stylesheets: false,
48061     
48062     
48063      /**
48064      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48065      * 
48066      */
48067     cblack: false,
48068     /**
48069      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48070      * 
48071      */
48072     cwhite: false,
48073     
48074      /**
48075      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48076      * 
48077      */
48078     black: false,
48079     /**
48080      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48081      * 
48082      */
48083     white: false,
48084     /**
48085      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48086      */
48087     allowComments: false,
48088     /**
48089      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48090      */
48091     
48092     
48093      bodyCls : '',
48094     
48095     // id of frame..
48096     frameId: false,
48097     
48098     // private properties
48099     validationEvent : false,
48100     deferHeight: true,
48101     initialized : false,
48102     activated : false,
48103     
48104     onFocus : Roo.emptyFn,
48105     iframePad:3,
48106     hideMode:'offsets',
48107     
48108     actionMode : 'container', // defaults to hiding it...
48109     
48110     defaultAutoCreate : { // modified by initCompnoent..
48111         tag: "textarea",
48112         style:"width:500px;height:300px;",
48113         autocomplete: "new-password"
48114     },
48115
48116     // private
48117     initComponent : function(){
48118         this.addEvents({
48119             /**
48120              * @event initialize
48121              * Fires when the editor is fully initialized (including the iframe)
48122              * @param {HtmlEditor} this
48123              */
48124             initialize: true,
48125             /**
48126              * @event activate
48127              * Fires when the editor is first receives the focus. Any insertion must wait
48128              * until after this event.
48129              * @param {HtmlEditor} this
48130              */
48131             activate: true,
48132              /**
48133              * @event beforesync
48134              * Fires before the textarea is updated with content from the editor iframe. Return false
48135              * to cancel the sync.
48136              * @param {HtmlEditor} this
48137              * @param {String} html
48138              */
48139             beforesync: true,
48140              /**
48141              * @event beforepush
48142              * Fires before the iframe editor is updated with content from the textarea. Return false
48143              * to cancel the push.
48144              * @param {HtmlEditor} this
48145              * @param {String} html
48146              */
48147             beforepush: true,
48148              /**
48149              * @event sync
48150              * Fires when the textarea is updated with content from the editor iframe.
48151              * @param {HtmlEditor} this
48152              * @param {String} html
48153              */
48154             sync: true,
48155              /**
48156              * @event push
48157              * Fires when the iframe editor is updated with content from the textarea.
48158              * @param {HtmlEditor} this
48159              * @param {String} html
48160              */
48161             push: true,
48162              /**
48163              * @event editmodechange
48164              * Fires when the editor switches edit modes
48165              * @param {HtmlEditor} this
48166              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48167              */
48168             editmodechange: true,
48169             /**
48170              * @event editorevent
48171              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48172              * @param {HtmlEditor} this
48173              */
48174             editorevent: true,
48175             /**
48176              * @event firstfocus
48177              * Fires when on first focus - needed by toolbars..
48178              * @param {HtmlEditor} this
48179              */
48180             firstfocus: true,
48181             /**
48182              * @event autosave
48183              * Auto save the htmlEditor value as a file into Events
48184              * @param {HtmlEditor} this
48185              */
48186             autosave: true,
48187             /**
48188              * @event savedpreview
48189              * preview the saved version of htmlEditor
48190              * @param {HtmlEditor} this
48191              */
48192             savedpreview: true,
48193             
48194             /**
48195             * @event stylesheetsclick
48196             * Fires when press the Sytlesheets button
48197             * @param {Roo.HtmlEditorCore} this
48198             */
48199             stylesheetsclick: true,
48200             /**
48201             * @event paste
48202             * Fires when press user pastes into the editor
48203             * @param {Roo.HtmlEditorCore} this
48204             */
48205             paste: true 
48206         });
48207         this.defaultAutoCreate =  {
48208             tag: "textarea",
48209             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48210             autocomplete: "new-password"
48211         };
48212     },
48213
48214     /**
48215      * Protected method that will not generally be called directly. It
48216      * is called when the editor creates its toolbar. Override this method if you need to
48217      * add custom toolbar buttons.
48218      * @param {HtmlEditor} editor
48219      */
48220     createToolbar : function(editor){
48221         Roo.log("create toolbars");
48222         if (!editor.toolbars || !editor.toolbars.length) {
48223             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48224         }
48225         
48226         for (var i =0 ; i < editor.toolbars.length;i++) {
48227             editor.toolbars[i] = Roo.factory(
48228                     typeof(editor.toolbars[i]) == 'string' ?
48229                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48230                 Roo.form.HtmlEditor);
48231             editor.toolbars[i].init(editor);
48232         }
48233          
48234         
48235     },
48236
48237      
48238     // private
48239     onRender : function(ct, position)
48240     {
48241         var _t = this;
48242         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48243         
48244         this.wrap = this.el.wrap({
48245             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48246         });
48247         
48248         this.editorcore.onRender(ct, position);
48249          
48250         if (this.resizable) {
48251             this.resizeEl = new Roo.Resizable(this.wrap, {
48252                 pinned : true,
48253                 wrap: true,
48254                 dynamic : true,
48255                 minHeight : this.height,
48256                 height: this.height,
48257                 handles : this.resizable,
48258                 width: this.width,
48259                 listeners : {
48260                     resize : function(r, w, h) {
48261                         _t.onResize(w,h); // -something
48262                     }
48263                 }
48264             });
48265             
48266         }
48267         this.createToolbar(this);
48268        
48269         
48270         if(!this.width){
48271             this.setSize(this.wrap.getSize());
48272         }
48273         if (this.resizeEl) {
48274             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48275             // should trigger onReize..
48276         }
48277         
48278         this.keyNav = new Roo.KeyNav(this.el, {
48279             
48280             "tab" : function(e){
48281                 e.preventDefault();
48282                 
48283                 var value = this.getValue();
48284                 
48285                 var start = this.el.dom.selectionStart;
48286                 var end = this.el.dom.selectionEnd;
48287                 
48288                 if(!e.shiftKey){
48289                     
48290                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48291                     this.el.dom.setSelectionRange(end + 1, end + 1);
48292                     return;
48293                 }
48294                 
48295                 var f = value.substring(0, start).split("\t");
48296                 
48297                 if(f.pop().length != 0){
48298                     return;
48299                 }
48300                 
48301                 this.setValue(f.join("\t") + value.substring(end));
48302                 this.el.dom.setSelectionRange(start - 1, start - 1);
48303                 
48304             },
48305             
48306             "home" : function(e){
48307                 e.preventDefault();
48308                 
48309                 var curr = this.el.dom.selectionStart;
48310                 var lines = this.getValue().split("\n");
48311                 
48312                 if(!lines.length){
48313                     return;
48314                 }
48315                 
48316                 if(e.ctrlKey){
48317                     this.el.dom.setSelectionRange(0, 0);
48318                     return;
48319                 }
48320                 
48321                 var pos = 0;
48322                 
48323                 for (var i = 0; i < lines.length;i++) {
48324                     pos += lines[i].length;
48325                     
48326                     if(i != 0){
48327                         pos += 1;
48328                     }
48329                     
48330                     if(pos < curr){
48331                         continue;
48332                     }
48333                     
48334                     pos -= lines[i].length;
48335                     
48336                     break;
48337                 }
48338                 
48339                 if(!e.shiftKey){
48340                     this.el.dom.setSelectionRange(pos, pos);
48341                     return;
48342                 }
48343                 
48344                 this.el.dom.selectionStart = pos;
48345                 this.el.dom.selectionEnd = curr;
48346             },
48347             
48348             "end" : function(e){
48349                 e.preventDefault();
48350                 
48351                 var curr = this.el.dom.selectionStart;
48352                 var lines = this.getValue().split("\n");
48353                 
48354                 if(!lines.length){
48355                     return;
48356                 }
48357                 
48358                 if(e.ctrlKey){
48359                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48360                     return;
48361                 }
48362                 
48363                 var pos = 0;
48364                 
48365                 for (var i = 0; i < lines.length;i++) {
48366                     
48367                     pos += lines[i].length;
48368                     
48369                     if(i != 0){
48370                         pos += 1;
48371                     }
48372                     
48373                     if(pos < curr){
48374                         continue;
48375                     }
48376                     
48377                     break;
48378                 }
48379                 
48380                 if(!e.shiftKey){
48381                     this.el.dom.setSelectionRange(pos, pos);
48382                     return;
48383                 }
48384                 
48385                 this.el.dom.selectionStart = curr;
48386                 this.el.dom.selectionEnd = pos;
48387             },
48388
48389             scope : this,
48390
48391             doRelay : function(foo, bar, hname){
48392                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48393             },
48394
48395             forceKeyDown: true
48396         });
48397         
48398 //        if(this.autosave && this.w){
48399 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48400 //        }
48401     },
48402
48403     // private
48404     onResize : function(w, h)
48405     {
48406         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48407         var ew = false;
48408         var eh = false;
48409         
48410         if(this.el ){
48411             if(typeof w == 'number'){
48412                 var aw = w - this.wrap.getFrameWidth('lr');
48413                 this.el.setWidth(this.adjustWidth('textarea', aw));
48414                 ew = aw;
48415             }
48416             if(typeof h == 'number'){
48417                 var tbh = 0;
48418                 for (var i =0; i < this.toolbars.length;i++) {
48419                     // fixme - ask toolbars for heights?
48420                     tbh += this.toolbars[i].tb.el.getHeight();
48421                     if (this.toolbars[i].footer) {
48422                         tbh += this.toolbars[i].footer.el.getHeight();
48423                     }
48424                 }
48425                 
48426                 
48427                 
48428                 
48429                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48430                 ah -= 5; // knock a few pixes off for look..
48431 //                Roo.log(ah);
48432                 this.el.setHeight(this.adjustWidth('textarea', ah));
48433                 var eh = ah;
48434             }
48435         }
48436         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48437         this.editorcore.onResize(ew,eh);
48438         
48439     },
48440
48441     /**
48442      * Toggles the editor between standard and source edit mode.
48443      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48444      */
48445     toggleSourceEdit : function(sourceEditMode)
48446     {
48447         this.editorcore.toggleSourceEdit(sourceEditMode);
48448         
48449         if(this.editorcore.sourceEditMode){
48450             Roo.log('editor - showing textarea');
48451             
48452 //            Roo.log('in');
48453 //            Roo.log(this.syncValue());
48454             this.editorcore.syncValue();
48455             this.el.removeClass('x-hidden');
48456             this.el.dom.removeAttribute('tabIndex');
48457             this.el.focus();
48458             this.el.dom.scrollTop = 0;
48459             
48460             
48461             for (var i = 0; i < this.toolbars.length; i++) {
48462                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48463                     this.toolbars[i].tb.hide();
48464                     this.toolbars[i].footer.hide();
48465                 }
48466             }
48467             
48468         }else{
48469             Roo.log('editor - hiding textarea');
48470 //            Roo.log('out')
48471 //            Roo.log(this.pushValue()); 
48472             this.editorcore.pushValue();
48473             
48474             this.el.addClass('x-hidden');
48475             this.el.dom.setAttribute('tabIndex', -1);
48476             
48477             for (var i = 0; i < this.toolbars.length; i++) {
48478                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48479                     this.toolbars[i].tb.show();
48480                     this.toolbars[i].footer.show();
48481                 }
48482             }
48483             
48484             //this.deferFocus();
48485         }
48486         
48487         this.setSize(this.wrap.getSize());
48488         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48489         
48490         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48491     },
48492  
48493     // private (for BoxComponent)
48494     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48495
48496     // private (for BoxComponent)
48497     getResizeEl : function(){
48498         return this.wrap;
48499     },
48500
48501     // private (for BoxComponent)
48502     getPositionEl : function(){
48503         return this.wrap;
48504     },
48505
48506     // private
48507     initEvents : function(){
48508         this.originalValue = this.getValue();
48509     },
48510
48511     /**
48512      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48513      * @method
48514      */
48515     markInvalid : Roo.emptyFn,
48516     /**
48517      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48518      * @method
48519      */
48520     clearInvalid : Roo.emptyFn,
48521
48522     setValue : function(v){
48523         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48524         this.editorcore.pushValue();
48525     },
48526
48527      
48528     // private
48529     deferFocus : function(){
48530         this.focus.defer(10, this);
48531     },
48532
48533     // doc'ed in Field
48534     focus : function(){
48535         this.editorcore.focus();
48536         
48537     },
48538       
48539
48540     // private
48541     onDestroy : function(){
48542         
48543         
48544         
48545         if(this.rendered){
48546             
48547             for (var i =0; i < this.toolbars.length;i++) {
48548                 // fixme - ask toolbars for heights?
48549                 this.toolbars[i].onDestroy();
48550             }
48551             
48552             this.wrap.dom.innerHTML = '';
48553             this.wrap.remove();
48554         }
48555     },
48556
48557     // private
48558     onFirstFocus : function(){
48559         //Roo.log("onFirstFocus");
48560         this.editorcore.onFirstFocus();
48561          for (var i =0; i < this.toolbars.length;i++) {
48562             this.toolbars[i].onFirstFocus();
48563         }
48564         
48565     },
48566     
48567     // private
48568     syncValue : function()
48569     {
48570         this.editorcore.syncValue();
48571     },
48572     
48573     pushValue : function()
48574     {
48575         this.editorcore.pushValue();
48576     },
48577     
48578     setStylesheets : function(stylesheets)
48579     {
48580         this.editorcore.setStylesheets(stylesheets);
48581     },
48582     
48583     removeStylesheets : function()
48584     {
48585         this.editorcore.removeStylesheets();
48586     }
48587      
48588     
48589     // hide stuff that is not compatible
48590     /**
48591      * @event blur
48592      * @hide
48593      */
48594     /**
48595      * @event change
48596      * @hide
48597      */
48598     /**
48599      * @event focus
48600      * @hide
48601      */
48602     /**
48603      * @event specialkey
48604      * @hide
48605      */
48606     /**
48607      * @cfg {String} fieldClass @hide
48608      */
48609     /**
48610      * @cfg {String} focusClass @hide
48611      */
48612     /**
48613      * @cfg {String} autoCreate @hide
48614      */
48615     /**
48616      * @cfg {String} inputType @hide
48617      */
48618     /**
48619      * @cfg {String} invalidClass @hide
48620      */
48621     /**
48622      * @cfg {String} invalidText @hide
48623      */
48624     /**
48625      * @cfg {String} msgFx @hide
48626      */
48627     /**
48628      * @cfg {String} validateOnBlur @hide
48629      */
48630 });
48631  
48632     // <script type="text/javascript">
48633 /*
48634  * Based on
48635  * Ext JS Library 1.1.1
48636  * Copyright(c) 2006-2007, Ext JS, LLC.
48637  *  
48638  
48639  */
48640
48641 /**
48642  * @class Roo.form.HtmlEditorToolbar1
48643  * Basic Toolbar
48644  * 
48645  * Usage:
48646  *
48647  new Roo.form.HtmlEditor({
48648     ....
48649     toolbars : [
48650         new Roo.form.HtmlEditorToolbar1({
48651             disable : { fonts: 1 , format: 1, ..., ... , ...],
48652             btns : [ .... ]
48653         })
48654     }
48655      
48656  * 
48657  * @cfg {Object} disable List of elements to disable..
48658  * @cfg {Array} btns List of additional buttons.
48659  * 
48660  * 
48661  * NEEDS Extra CSS? 
48662  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48663  */
48664  
48665 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48666 {
48667     
48668     Roo.apply(this, config);
48669     
48670     // default disabled, based on 'good practice'..
48671     this.disable = this.disable || {};
48672     Roo.applyIf(this.disable, {
48673         fontSize : true,
48674         colors : true,
48675         specialElements : true
48676     });
48677     
48678     
48679     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48680     // dont call parent... till later.
48681 }
48682
48683 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48684     
48685     tb: false,
48686     
48687     rendered: false,
48688     
48689     editor : false,
48690     editorcore : false,
48691     /**
48692      * @cfg {Object} disable  List of toolbar elements to disable
48693          
48694      */
48695     disable : false,
48696     
48697     
48698      /**
48699      * @cfg {String} createLinkText The default text for the create link prompt
48700      */
48701     createLinkText : 'Please enter the URL for the link:',
48702     /**
48703      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48704      */
48705     defaultLinkValue : 'http:/'+'/',
48706    
48707     
48708       /**
48709      * @cfg {Array} fontFamilies An array of available font families
48710      */
48711     fontFamilies : [
48712         'Arial',
48713         'Courier New',
48714         'Tahoma',
48715         'Times New Roman',
48716         'Verdana'
48717     ],
48718     
48719     specialChars : [
48720            "&#169;",
48721           "&#174;",     
48722           "&#8482;",    
48723           "&#163;" ,    
48724          // "&#8212;",    
48725           "&#8230;",    
48726           "&#247;" ,    
48727         //  "&#225;" ,     ?? a acute?
48728            "&#8364;"    , //Euro
48729        //   "&#8220;"    ,
48730         //  "&#8221;"    ,
48731         //  "&#8226;"    ,
48732           "&#176;"  //   , // degrees
48733
48734          // "&#233;"     , // e ecute
48735          // "&#250;"     , // u ecute?
48736     ],
48737     
48738     specialElements : [
48739         {
48740             text: "Insert Table",
48741             xtype: 'MenuItem',
48742             xns : Roo.Menu,
48743             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48744                 
48745         },
48746         {    
48747             text: "Insert Image",
48748             xtype: 'MenuItem',
48749             xns : Roo.Menu,
48750             ihtml : '<img src="about:blank"/>'
48751             
48752         }
48753         
48754          
48755     ],
48756     
48757     
48758     inputElements : [ 
48759             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48760             "input:submit", "input:button", "select", "textarea", "label" ],
48761     formats : [
48762         ["p"] ,  
48763         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48764         ["pre"],[ "code"], 
48765         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48766         ['div'],['span'],
48767         ['sup'],['sub']
48768     ],
48769     
48770     cleanStyles : [
48771         "font-size"
48772     ],
48773      /**
48774      * @cfg {String} defaultFont default font to use.
48775      */
48776     defaultFont: 'tahoma',
48777    
48778     fontSelect : false,
48779     
48780     
48781     formatCombo : false,
48782     
48783     init : function(editor)
48784     {
48785         this.editor = editor;
48786         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48787         var editorcore = this.editorcore;
48788         
48789         var _t = this;
48790         
48791         var fid = editorcore.frameId;
48792         var etb = this;
48793         function btn(id, toggle, handler){
48794             var xid = fid + '-'+ id ;
48795             return {
48796                 id : xid,
48797                 cmd : id,
48798                 cls : 'x-btn-icon x-edit-'+id,
48799                 enableToggle:toggle !== false,
48800                 scope: _t, // was editor...
48801                 handler:handler||_t.relayBtnCmd,
48802                 clickEvent:'mousedown',
48803                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48804                 tabIndex:-1
48805             };
48806         }
48807         
48808         
48809         
48810         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48811         this.tb = tb;
48812          // stop form submits
48813         tb.el.on('click', function(e){
48814             e.preventDefault(); // what does this do?
48815         });
48816
48817         if(!this.disable.font) { // && !Roo.isSafari){
48818             /* why no safari for fonts 
48819             editor.fontSelect = tb.el.createChild({
48820                 tag:'select',
48821                 tabIndex: -1,
48822                 cls:'x-font-select',
48823                 html: this.createFontOptions()
48824             });
48825             
48826             editor.fontSelect.on('change', function(){
48827                 var font = editor.fontSelect.dom.value;
48828                 editor.relayCmd('fontname', font);
48829                 editor.deferFocus();
48830             }, editor);
48831             
48832             tb.add(
48833                 editor.fontSelect.dom,
48834                 '-'
48835             );
48836             */
48837             
48838         };
48839         if(!this.disable.formats){
48840             this.formatCombo = new Roo.form.ComboBox({
48841                 store: new Roo.data.SimpleStore({
48842                     id : 'tag',
48843                     fields: ['tag'],
48844                     data : this.formats // from states.js
48845                 }),
48846                 blockFocus : true,
48847                 name : '',
48848                 //autoCreate : {tag: "div",  size: "20"},
48849                 displayField:'tag',
48850                 typeAhead: false,
48851                 mode: 'local',
48852                 editable : false,
48853                 triggerAction: 'all',
48854                 emptyText:'Add tag',
48855                 selectOnFocus:true,
48856                 width:135,
48857                 listeners : {
48858                     'select': function(c, r, i) {
48859                         editorcore.insertTag(r.get('tag'));
48860                         editor.focus();
48861                     }
48862                 }
48863
48864             });
48865             tb.addField(this.formatCombo);
48866             
48867         }
48868         
48869         if(!this.disable.format){
48870             tb.add(
48871                 btn('bold'),
48872                 btn('italic'),
48873                 btn('underline'),
48874                 btn('strikethrough')
48875             );
48876         };
48877         if(!this.disable.fontSize){
48878             tb.add(
48879                 '-',
48880                 
48881                 
48882                 btn('increasefontsize', false, editorcore.adjustFont),
48883                 btn('decreasefontsize', false, editorcore.adjustFont)
48884             );
48885         };
48886         
48887         
48888         if(!this.disable.colors){
48889             tb.add(
48890                 '-', {
48891                     id:editorcore.frameId +'-forecolor',
48892                     cls:'x-btn-icon x-edit-forecolor',
48893                     clickEvent:'mousedown',
48894                     tooltip: this.buttonTips['forecolor'] || undefined,
48895                     tabIndex:-1,
48896                     menu : new Roo.menu.ColorMenu({
48897                         allowReselect: true,
48898                         focus: Roo.emptyFn,
48899                         value:'000000',
48900                         plain:true,
48901                         selectHandler: function(cp, color){
48902                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48903                             editor.deferFocus();
48904                         },
48905                         scope: editorcore,
48906                         clickEvent:'mousedown'
48907                     })
48908                 }, {
48909                     id:editorcore.frameId +'backcolor',
48910                     cls:'x-btn-icon x-edit-backcolor',
48911                     clickEvent:'mousedown',
48912                     tooltip: this.buttonTips['backcolor'] || undefined,
48913                     tabIndex:-1,
48914                     menu : new Roo.menu.ColorMenu({
48915                         focus: Roo.emptyFn,
48916                         value:'FFFFFF',
48917                         plain:true,
48918                         allowReselect: true,
48919                         selectHandler: function(cp, color){
48920                             if(Roo.isGecko){
48921                                 editorcore.execCmd('useCSS', false);
48922                                 editorcore.execCmd('hilitecolor', color);
48923                                 editorcore.execCmd('useCSS', true);
48924                                 editor.deferFocus();
48925                             }else{
48926                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48927                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48928                                 editor.deferFocus();
48929                             }
48930                         },
48931                         scope:editorcore,
48932                         clickEvent:'mousedown'
48933                     })
48934                 }
48935             );
48936         };
48937         // now add all the items...
48938         
48939
48940         if(!this.disable.alignments){
48941             tb.add(
48942                 '-',
48943                 btn('justifyleft'),
48944                 btn('justifycenter'),
48945                 btn('justifyright')
48946             );
48947         };
48948
48949         //if(!Roo.isSafari){
48950             if(!this.disable.links){
48951                 tb.add(
48952                     '-',
48953                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48954                 );
48955             };
48956
48957             if(!this.disable.lists){
48958                 tb.add(
48959                     '-',
48960                     btn('insertorderedlist'),
48961                     btn('insertunorderedlist')
48962                 );
48963             }
48964             if(!this.disable.sourceEdit){
48965                 tb.add(
48966                     '-',
48967                     btn('sourceedit', true, function(btn){
48968                         this.toggleSourceEdit(btn.pressed);
48969                     })
48970                 );
48971             }
48972         //}
48973         
48974         var smenu = { };
48975         // special menu.. - needs to be tidied up..
48976         if (!this.disable.special) {
48977             smenu = {
48978                 text: "&#169;",
48979                 cls: 'x-edit-none',
48980                 
48981                 menu : {
48982                     items : []
48983                 }
48984             };
48985             for (var i =0; i < this.specialChars.length; i++) {
48986                 smenu.menu.items.push({
48987                     
48988                     html: this.specialChars[i],
48989                     handler: function(a,b) {
48990                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48991                         //editor.insertAtCursor(a.html);
48992                         
48993                     },
48994                     tabIndex:-1
48995                 });
48996             }
48997             
48998             
48999             tb.add(smenu);
49000             
49001             
49002         }
49003         
49004         var cmenu = { };
49005         if (!this.disable.cleanStyles) {
49006             cmenu = {
49007                 cls: 'x-btn-icon x-btn-clear',
49008                 
49009                 menu : {
49010                     items : []
49011                 }
49012             };
49013             for (var i =0; i < this.cleanStyles.length; i++) {
49014                 cmenu.menu.items.push({
49015                     actiontype : this.cleanStyles[i],
49016                     html: 'Remove ' + this.cleanStyles[i],
49017                     handler: function(a,b) {
49018 //                        Roo.log(a);
49019 //                        Roo.log(b);
49020                         var c = Roo.get(editorcore.doc.body);
49021                         c.select('[style]').each(function(s) {
49022                             s.dom.style.removeProperty(a.actiontype);
49023                         });
49024                         editorcore.syncValue();
49025                     },
49026                     tabIndex:-1
49027                 });
49028             }
49029             cmenu.menu.items.push({
49030                 actiontype : 'tablewidths',
49031                 html: 'Remove Table Widths',
49032                 handler: function(a,b) {
49033                     editorcore.cleanTableWidths();
49034                     editorcore.syncValue();
49035                 },
49036                 tabIndex:-1
49037             });
49038             cmenu.menu.items.push({
49039                 actiontype : 'word',
49040                 html: 'Remove MS Word Formating',
49041                 handler: function(a,b) {
49042                     editorcore.cleanWord();
49043                     editorcore.syncValue();
49044                 },
49045                 tabIndex:-1
49046             });
49047             
49048             cmenu.menu.items.push({
49049                 actiontype : 'all',
49050                 html: 'Remove All Styles',
49051                 handler: function(a,b) {
49052                     
49053                     var c = Roo.get(editorcore.doc.body);
49054                     c.select('[style]').each(function(s) {
49055                         s.dom.removeAttribute('style');
49056                     });
49057                     editorcore.syncValue();
49058                 },
49059                 tabIndex:-1
49060             });
49061             
49062             cmenu.menu.items.push({
49063                 actiontype : 'all',
49064                 html: 'Remove All CSS Classes',
49065                 handler: function(a,b) {
49066                     
49067                     var c = Roo.get(editorcore.doc.body);
49068                     c.select('[class]').each(function(s) {
49069                         s.dom.removeAttribute('class');
49070                     });
49071                     editorcore.cleanWord();
49072                     editorcore.syncValue();
49073                 },
49074                 tabIndex:-1
49075             });
49076             
49077              cmenu.menu.items.push({
49078                 actiontype : 'tidy',
49079                 html: 'Tidy HTML Source',
49080                 handler: function(a,b) {
49081                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49082                     editorcore.syncValue();
49083                 },
49084                 tabIndex:-1
49085             });
49086             
49087             
49088             tb.add(cmenu);
49089         }
49090          
49091         if (!this.disable.specialElements) {
49092             var semenu = {
49093                 text: "Other;",
49094                 cls: 'x-edit-none',
49095                 menu : {
49096                     items : []
49097                 }
49098             };
49099             for (var i =0; i < this.specialElements.length; i++) {
49100                 semenu.menu.items.push(
49101                     Roo.apply({ 
49102                         handler: function(a,b) {
49103                             editor.insertAtCursor(this.ihtml);
49104                         }
49105                     }, this.specialElements[i])
49106                 );
49107                     
49108             }
49109             
49110             tb.add(semenu);
49111             
49112             
49113         }
49114          
49115         
49116         if (this.btns) {
49117             for(var i =0; i< this.btns.length;i++) {
49118                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49119                 b.cls =  'x-edit-none';
49120                 
49121                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49122                     b.cls += ' x-init-enable';
49123                 }
49124                 
49125                 b.scope = editorcore;
49126                 tb.add(b);
49127             }
49128         
49129         }
49130         
49131         
49132         
49133         // disable everything...
49134         
49135         this.tb.items.each(function(item){
49136             
49137            if(
49138                 item.id != editorcore.frameId+ '-sourceedit' && 
49139                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49140             ){
49141                 
49142                 item.disable();
49143             }
49144         });
49145         this.rendered = true;
49146         
49147         // the all the btns;
49148         editor.on('editorevent', this.updateToolbar, this);
49149         // other toolbars need to implement this..
49150         //editor.on('editmodechange', this.updateToolbar, this);
49151     },
49152     
49153     
49154     relayBtnCmd : function(btn) {
49155         this.editorcore.relayCmd(btn.cmd);
49156     },
49157     // private used internally
49158     createLink : function(){
49159         Roo.log("create link?");
49160         var url = prompt(this.createLinkText, this.defaultLinkValue);
49161         if(url && url != 'http:/'+'/'){
49162             this.editorcore.relayCmd('createlink', url);
49163         }
49164     },
49165
49166     
49167     /**
49168      * Protected method that will not generally be called directly. It triggers
49169      * a toolbar update by reading the markup state of the current selection in the editor.
49170      */
49171     updateToolbar: function(){
49172
49173         if(!this.editorcore.activated){
49174             this.editor.onFirstFocus();
49175             return;
49176         }
49177
49178         var btns = this.tb.items.map, 
49179             doc = this.editorcore.doc,
49180             frameId = this.editorcore.frameId;
49181
49182         if(!this.disable.font && !Roo.isSafari){
49183             /*
49184             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49185             if(name != this.fontSelect.dom.value){
49186                 this.fontSelect.dom.value = name;
49187             }
49188             */
49189         }
49190         if(!this.disable.format){
49191             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49192             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49193             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49194             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49195         }
49196         if(!this.disable.alignments){
49197             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49198             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49199             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49200         }
49201         if(!Roo.isSafari && !this.disable.lists){
49202             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49203             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49204         }
49205         
49206         var ans = this.editorcore.getAllAncestors();
49207         if (this.formatCombo) {
49208             
49209             
49210             var store = this.formatCombo.store;
49211             this.formatCombo.setValue("");
49212             for (var i =0; i < ans.length;i++) {
49213                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49214                     // select it..
49215                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49216                     break;
49217                 }
49218             }
49219         }
49220         
49221         
49222         
49223         // hides menus... - so this cant be on a menu...
49224         Roo.menu.MenuMgr.hideAll();
49225
49226         //this.editorsyncValue();
49227     },
49228    
49229     
49230     createFontOptions : function(){
49231         var buf = [], fs = this.fontFamilies, ff, lc;
49232         
49233         
49234         
49235         for(var i = 0, len = fs.length; i< len; i++){
49236             ff = fs[i];
49237             lc = ff.toLowerCase();
49238             buf.push(
49239                 '<option value="',lc,'" style="font-family:',ff,';"',
49240                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49241                     ff,
49242                 '</option>'
49243             );
49244         }
49245         return buf.join('');
49246     },
49247     
49248     toggleSourceEdit : function(sourceEditMode){
49249         
49250         Roo.log("toolbar toogle");
49251         if(sourceEditMode === undefined){
49252             sourceEditMode = !this.sourceEditMode;
49253         }
49254         this.sourceEditMode = sourceEditMode === true;
49255         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49256         // just toggle the button?
49257         if(btn.pressed !== this.sourceEditMode){
49258             btn.toggle(this.sourceEditMode);
49259             return;
49260         }
49261         
49262         if(sourceEditMode){
49263             Roo.log("disabling buttons");
49264             this.tb.items.each(function(item){
49265                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49266                     item.disable();
49267                 }
49268             });
49269           
49270         }else{
49271             Roo.log("enabling buttons");
49272             if(this.editorcore.initialized){
49273                 this.tb.items.each(function(item){
49274                     item.enable();
49275                 });
49276             }
49277             
49278         }
49279         Roo.log("calling toggole on editor");
49280         // tell the editor that it's been pressed..
49281         this.editor.toggleSourceEdit(sourceEditMode);
49282        
49283     },
49284      /**
49285      * Object collection of toolbar tooltips for the buttons in the editor. The key
49286      * is the command id associated with that button and the value is a valid QuickTips object.
49287      * For example:
49288 <pre><code>
49289 {
49290     bold : {
49291         title: 'Bold (Ctrl+B)',
49292         text: 'Make the selected text bold.',
49293         cls: 'x-html-editor-tip'
49294     },
49295     italic : {
49296         title: 'Italic (Ctrl+I)',
49297         text: 'Make the selected text italic.',
49298         cls: 'x-html-editor-tip'
49299     },
49300     ...
49301 </code></pre>
49302     * @type Object
49303      */
49304     buttonTips : {
49305         bold : {
49306             title: 'Bold (Ctrl+B)',
49307             text: 'Make the selected text bold.',
49308             cls: 'x-html-editor-tip'
49309         },
49310         italic : {
49311             title: 'Italic (Ctrl+I)',
49312             text: 'Make the selected text italic.',
49313             cls: 'x-html-editor-tip'
49314         },
49315         underline : {
49316             title: 'Underline (Ctrl+U)',
49317             text: 'Underline the selected text.',
49318             cls: 'x-html-editor-tip'
49319         },
49320         strikethrough : {
49321             title: 'Strikethrough',
49322             text: 'Strikethrough the selected text.',
49323             cls: 'x-html-editor-tip'
49324         },
49325         increasefontsize : {
49326             title: 'Grow Text',
49327             text: 'Increase the font size.',
49328             cls: 'x-html-editor-tip'
49329         },
49330         decreasefontsize : {
49331             title: 'Shrink Text',
49332             text: 'Decrease the font size.',
49333             cls: 'x-html-editor-tip'
49334         },
49335         backcolor : {
49336             title: 'Text Highlight Color',
49337             text: 'Change the background color of the selected text.',
49338             cls: 'x-html-editor-tip'
49339         },
49340         forecolor : {
49341             title: 'Font Color',
49342             text: 'Change the color of the selected text.',
49343             cls: 'x-html-editor-tip'
49344         },
49345         justifyleft : {
49346             title: 'Align Text Left',
49347             text: 'Align text to the left.',
49348             cls: 'x-html-editor-tip'
49349         },
49350         justifycenter : {
49351             title: 'Center Text',
49352             text: 'Center text in the editor.',
49353             cls: 'x-html-editor-tip'
49354         },
49355         justifyright : {
49356             title: 'Align Text Right',
49357             text: 'Align text to the right.',
49358             cls: 'x-html-editor-tip'
49359         },
49360         insertunorderedlist : {
49361             title: 'Bullet List',
49362             text: 'Start a bulleted list.',
49363             cls: 'x-html-editor-tip'
49364         },
49365         insertorderedlist : {
49366             title: 'Numbered List',
49367             text: 'Start a numbered list.',
49368             cls: 'x-html-editor-tip'
49369         },
49370         createlink : {
49371             title: 'Hyperlink',
49372             text: 'Make the selected text a hyperlink.',
49373             cls: 'x-html-editor-tip'
49374         },
49375         sourceedit : {
49376             title: 'Source Edit',
49377             text: 'Switch to source editing mode.',
49378             cls: 'x-html-editor-tip'
49379         }
49380     },
49381     // private
49382     onDestroy : function(){
49383         if(this.rendered){
49384             
49385             this.tb.items.each(function(item){
49386                 if(item.menu){
49387                     item.menu.removeAll();
49388                     if(item.menu.el){
49389                         item.menu.el.destroy();
49390                     }
49391                 }
49392                 item.destroy();
49393             });
49394              
49395         }
49396     },
49397     onFirstFocus: function() {
49398         this.tb.items.each(function(item){
49399            item.enable();
49400         });
49401     }
49402 });
49403
49404
49405
49406
49407 // <script type="text/javascript">
49408 /*
49409  * Based on
49410  * Ext JS Library 1.1.1
49411  * Copyright(c) 2006-2007, Ext JS, LLC.
49412  *  
49413  
49414  */
49415
49416  
49417 /**
49418  * @class Roo.form.HtmlEditor.ToolbarContext
49419  * Context Toolbar
49420  * 
49421  * Usage:
49422  *
49423  new Roo.form.HtmlEditor({
49424     ....
49425     toolbars : [
49426         { xtype: 'ToolbarStandard', styles : {} }
49427         { xtype: 'ToolbarContext', disable : {} }
49428     ]
49429 })
49430
49431      
49432  * 
49433  * @config : {Object} disable List of elements to disable.. (not done yet.)
49434  * @config : {Object} styles  Map of styles available.
49435  * 
49436  */
49437
49438 Roo.form.HtmlEditor.ToolbarContext = function(config)
49439 {
49440     
49441     Roo.apply(this, config);
49442     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49443     // dont call parent... till later.
49444     this.styles = this.styles || {};
49445 }
49446
49447  
49448
49449 Roo.form.HtmlEditor.ToolbarContext.types = {
49450     'IMG' : [
49451         {
49452             name : 'width',
49453             title: "Width",
49454             width: 40
49455         },
49456         {
49457             name : 'height',
49458             title: "Height",
49459             width: 40
49460         },
49461         {
49462             name : 'align',
49463             title: "Align",
49464             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49465             width : 80
49466             
49467         },
49468         {
49469             name : 'border',
49470             title: "Border",
49471             width: 40
49472         },
49473         {
49474             name : 'alt',
49475             title: "Alt",
49476             width: 120
49477         },
49478         {
49479             name : 'src',
49480             title: "Src",
49481             width: 220
49482         }
49483         
49484     ],
49485     
49486     'FIGURE' : [
49487         {
49488             name : 'align',
49489             title: "Align",
49490             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49491             width : 80  
49492         }
49493     ],
49494     'A' : [
49495         {
49496             name : 'name',
49497             title: "Name",
49498             width: 50
49499         },
49500         {
49501             name : 'target',
49502             title: "Target",
49503             width: 120
49504         },
49505         {
49506             name : 'href',
49507             title: "Href",
49508             width: 220
49509         } // border?
49510         
49511     ],
49512     
49513     'INPUT' : [
49514         {
49515             name : 'name',
49516             title: "name",
49517             width: 120
49518         },
49519         {
49520             name : 'value',
49521             title: "Value",
49522             width: 120
49523         },
49524         {
49525             name : 'width',
49526             title: "Width",
49527             width: 40
49528         }
49529     ],
49530     'LABEL' : [
49531          {
49532             name : 'for',
49533             title: "For",
49534             width: 120
49535         }
49536     ],
49537     'TEXTAREA' : [
49538         {
49539             name : 'name',
49540             title: "name",
49541             width: 120
49542         },
49543         {
49544             name : 'rows',
49545             title: "Rows",
49546             width: 20
49547         },
49548         {
49549             name : 'cols',
49550             title: "Cols",
49551             width: 20
49552         }
49553     ],
49554     'SELECT' : [
49555         {
49556             name : 'name',
49557             title: "name",
49558             width: 120
49559         },
49560         {
49561             name : 'selectoptions',
49562             title: "Options",
49563             width: 200
49564         }
49565     ],
49566     
49567     // should we really allow this??
49568     // should this just be 
49569     'BODY' : [
49570         
49571         {
49572             name : 'title',
49573             title: "Title",
49574             width: 200,
49575             disabled : true
49576         }
49577     ],
49578  
49579     '*' : [
49580         // empty.
49581     ]
49582
49583 };
49584
49585 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49586 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49587
49588 Roo.form.HtmlEditor.ToolbarContext.options = {
49589         'font-family'  : [ 
49590                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49591                 [ 'Courier New', 'Courier New'],
49592                 [ 'Tahoma', 'Tahoma'],
49593                 [ 'Times New Roman,serif', 'Times'],
49594                 [ 'Verdana','Verdana' ]
49595         ]
49596 };
49597
49598 // fixme - these need to be configurable..
49599  
49600
49601 //Roo.form.HtmlEditor.ToolbarContext.types
49602
49603
49604 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49605     
49606     tb: false,
49607     
49608     rendered: false,
49609     
49610     editor : false,
49611     editorcore : false,
49612     /**
49613      * @cfg {Object} disable  List of toolbar elements to disable
49614          
49615      */
49616     disable : false,
49617     /**
49618      * @cfg {Object} styles List of styles 
49619      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49620      *
49621      * These must be defined in the page, so they get rendered correctly..
49622      * .headline { }
49623      * TD.underline { }
49624      * 
49625      */
49626     styles : false,
49627     
49628     options: false,
49629     
49630     toolbars : false,
49631     
49632     init : function(editor)
49633     {
49634         this.editor = editor;
49635         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49636         var editorcore = this.editorcore;
49637         
49638         var fid = editorcore.frameId;
49639         var etb = this;
49640         function btn(id, toggle, handler){
49641             var xid = fid + '-'+ id ;
49642             return {
49643                 id : xid,
49644                 cmd : id,
49645                 cls : 'x-btn-icon x-edit-'+id,
49646                 enableToggle:toggle !== false,
49647                 scope: editorcore, // was editor...
49648                 handler:handler||editorcore.relayBtnCmd,
49649                 clickEvent:'mousedown',
49650                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49651                 tabIndex:-1
49652             };
49653         }
49654         // create a new element.
49655         var wdiv = editor.wrap.createChild({
49656                 tag: 'div'
49657             }, editor.wrap.dom.firstChild.nextSibling, true);
49658         
49659         // can we do this more than once??
49660         
49661          // stop form submits
49662       
49663  
49664         // disable everything...
49665         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49666         this.toolbars = {};
49667            
49668         for (var i in  ty) {
49669           
49670             this.toolbars[i] = this.buildToolbar(ty[i],i);
49671         }
49672         this.tb = this.toolbars.BODY;
49673         this.tb.el.show();
49674         this.buildFooter();
49675         this.footer.show();
49676         editor.on('hide', function( ) { this.footer.hide() }, this);
49677         editor.on('show', function( ) { this.footer.show() }, this);
49678         
49679          
49680         this.rendered = true;
49681         
49682         // the all the btns;
49683         editor.on('editorevent', this.updateToolbar, this);
49684         // other toolbars need to implement this..
49685         //editor.on('editmodechange', this.updateToolbar, this);
49686     },
49687     
49688     
49689     
49690     /**
49691      * Protected method that will not generally be called directly. It triggers
49692      * a toolbar update by reading the markup state of the current selection in the editor.
49693      *
49694      * Note you can force an update by calling on('editorevent', scope, false)
49695      */
49696     updateToolbar: function(editor ,ev, sel)
49697     {
49698         
49699         if (ev) {
49700             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49701         }
49702         
49703         //Roo.log(ev);
49704         // capture mouse up - this is handy for selecting images..
49705         // perhaps should go somewhere else...
49706         if(!this.editorcore.activated){
49707              this.editor.onFirstFocus();
49708             return;
49709         }
49710         //Roo.log(ev ? ev.target : 'NOTARGET');
49711         
49712         
49713         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49714         // selectNode - might want to handle IE?
49715         
49716         
49717         
49718         if (ev &&
49719             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49720             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49721             // they have click on an image...
49722             // let's see if we can change the selection...
49723             sel = ev.target;
49724             
49725             // this triggers looping?
49726             //this.editorcore.selectNode(sel);
49727              
49728         }  
49729         
49730       
49731         //var updateFooter = sel ? false : true; 
49732         
49733         
49734         var ans = this.editorcore.getAllAncestors();
49735         
49736         // pick
49737         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49738         
49739         if (!sel) { 
49740             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49741             sel = sel ? sel : this.editorcore.doc.body;
49742             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49743             
49744         }
49745         
49746         var tn = sel.tagName.toUpperCase();
49747         var lastSel = this.tb.selectedNode;
49748         this.tb.selectedNode = sel;
49749         var left_label = tn;
49750         
49751         // ok see if we are editing a block?
49752         var sel_el = Roo.get(sel);
49753         var db = false;
49754         // you are not actually selecting the block.
49755         if (sel && sel.hasAttribute('data-block')) {
49756             db = sel;
49757         } else if (sel && !sel.hasAttribute('contenteditable')) {
49758             db = sel_el.findParent('[data-block]');
49759             var cepar = sel_el.findParent('[contenteditable=true]');
49760             if (db && cepar && cepar.tagName != 'BODY') {
49761                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49762             }   
49763         }
49764         
49765         
49766         var block = false;
49767         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49768         if (db) {
49769             block = Roo.htmleditor.Block.factory(db);
49770             if (block) {
49771                 tn = 'BLOCK.' + db.getAttribute('data-block');
49772                 
49773                 //this.editorcore.selectNode(db);
49774                 if (typeof(this.toolbars[tn]) == 'undefined') {
49775                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49776                 }
49777                 this.toolbars[tn].selectedNode = db;
49778                 left_label = block.friendly_name;
49779                 ans = this.editorcore.getAllAncestors();
49780             }
49781             
49782                 
49783             
49784         }
49785         
49786         
49787         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49788             return; // no change?
49789         }
49790         
49791         
49792           
49793         this.tb.el.hide();
49794         ///console.log("show: " + tn);
49795         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49796         
49797         this.tb.el.show();
49798         // update name
49799         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49800         
49801         
49802         // update attributes
49803         if (block) {
49804              
49805             this.tb.fields.each(function(e) {
49806                 e.setValue(block[e.name]);
49807             });
49808             
49809             
49810         } else  if (this.tb.fields && this.tb.selectedNode) {
49811             this.tb.fields.each( function(e) {
49812                 if (e.stylename) {
49813                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49814                     return;
49815                 } 
49816                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49817             }, this);
49818             this.updateToolbarStyles(this.tb.selectedNode);  
49819         }
49820         
49821         
49822        
49823         Roo.menu.MenuMgr.hideAll();
49824
49825         
49826         
49827     
49828         // update the footer
49829         //
49830         this.updateFooter(ans);
49831              
49832     },
49833     
49834     updateToolbarStyles : function(sel)
49835     {
49836         var hasStyles = false;
49837         for(var i in this.styles) {
49838             hasStyles = true;
49839             break;
49840         }
49841         
49842         // update styles
49843         if (hasStyles && this.tb.hasStyles) { 
49844             var st = this.tb.fields.item(0);
49845             
49846             st.store.removeAll();
49847             var cn = sel.className.split(/\s+/);
49848             
49849             var avs = [];
49850             if (this.styles['*']) {
49851                 
49852                 Roo.each(this.styles['*'], function(v) {
49853                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49854                 });
49855             }
49856             if (this.styles[tn]) { 
49857                 Roo.each(this.styles[tn], function(v) {
49858                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49859                 });
49860             }
49861             
49862             st.store.loadData(avs);
49863             st.collapse();
49864             st.setValue(cn);
49865         }
49866     },
49867     
49868      
49869     updateFooter : function(ans)
49870     {
49871         var html = '';
49872         if (ans === false) {
49873             this.footDisp.dom.innerHTML = '';
49874             return;
49875         }
49876         
49877         this.footerEls = ans.reverse();
49878         Roo.each(this.footerEls, function(a,i) {
49879             if (!a) { return; }
49880             html += html.length ? ' &gt; '  :  '';
49881             
49882             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49883             
49884         });
49885        
49886         // 
49887         var sz = this.footDisp.up('td').getSize();
49888         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49889         this.footDisp.dom.style.marginLeft = '5px';
49890         
49891         this.footDisp.dom.style.overflow = 'hidden';
49892         
49893         this.footDisp.dom.innerHTML = html;
49894             
49895         
49896     },
49897    
49898        
49899     // private
49900     onDestroy : function(){
49901         if(this.rendered){
49902             
49903             this.tb.items.each(function(item){
49904                 if(item.menu){
49905                     item.menu.removeAll();
49906                     if(item.menu.el){
49907                         item.menu.el.destroy();
49908                     }
49909                 }
49910                 item.destroy();
49911             });
49912              
49913         }
49914     },
49915     onFirstFocus: function() {
49916         // need to do this for all the toolbars..
49917         this.tb.items.each(function(item){
49918            item.enable();
49919         });
49920     },
49921     buildToolbar: function(tlist, nm, friendly_name, block)
49922     {
49923         var editor = this.editor;
49924         var editorcore = this.editorcore;
49925          // create a new element.
49926         var wdiv = editor.wrap.createChild({
49927                 tag: 'div'
49928             }, editor.wrap.dom.firstChild.nextSibling, true);
49929         
49930        
49931         var tb = new Roo.Toolbar(wdiv);
49932         ///this.tb = tb; // << this sets the active toolbar..
49933         if (tlist === false && block) {
49934             tlist = block.contextMenu(this);
49935         }
49936         
49937         tb.hasStyles = false;
49938         tb.name = nm;
49939         
49940         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49941         
49942         var styles = Array.from(this.styles);
49943         
49944         
49945         // styles...
49946         if (styles && styles.length) {
49947             tb.hasStyles = true;
49948             // this needs a multi-select checkbox...
49949             tb.addField( new Roo.form.ComboBox({
49950                 store: new Roo.data.SimpleStore({
49951                     id : 'val',
49952                     fields: ['val', 'selected'],
49953                     data : [] 
49954                 }),
49955                 name : '-roo-edit-className',
49956                 attrname : 'className',
49957                 displayField: 'val',
49958                 typeAhead: false,
49959                 mode: 'local',
49960                 editable : false,
49961                 triggerAction: 'all',
49962                 emptyText:'Select Style',
49963                 selectOnFocus:true,
49964                 width: 130,
49965                 listeners : {
49966                     'select': function(c, r, i) {
49967                         // initial support only for on class per el..
49968                         tb.selectedNode.className =  r ? r.get('val') : '';
49969                         editorcore.syncValue();
49970                     }
49971                 }
49972     
49973             }));
49974         }
49975         
49976         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49977         
49978         
49979         for (var i = 0; i < tlist.length; i++) {
49980             
49981             // newer versions will use xtype cfg to create menus.
49982             if (typeof(tlist[i].xtype) != 'undefined') {
49983                 
49984                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49985                 
49986                 
49987                 continue;
49988             }
49989             
49990             var item = tlist[i];
49991             tb.add(item.title + ":&nbsp;");
49992             
49993             
49994             //optname == used so you can configure the options available..
49995             var opts = item.opts ? item.opts : false;
49996             if (item.optname) { // use the b
49997                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49998            
49999             }
50000             
50001             if (opts) {
50002                 // opts == pulldown..
50003                 tb.addField( new Roo.form.ComboBox({
50004                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50005                         id : 'val',
50006                         fields: ['val', 'display'],
50007                         data : opts  
50008                     }),
50009                     name : '-roo-edit-' + tlist[i].name,
50010                     
50011                     attrname : tlist[i].name,
50012                     stylename : item.style ? item.style : false,
50013                     
50014                     displayField: item.displayField ? item.displayField : 'val',
50015                     valueField :  'val',
50016                     typeAhead: false,
50017                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50018                     editable : false,
50019                     triggerAction: 'all',
50020                     emptyText:'Select',
50021                     selectOnFocus:true,
50022                     width: item.width ? item.width  : 130,
50023                     listeners : {
50024                         'select': function(c, r, i) {
50025                             if (tb.selectedNode.hasAttribute('data-block')) {
50026                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50027                                 b[c.attrname] = r.get('val');
50028                                 b.updateElement(tb.selectedNode);
50029                                 editorcore.syncValue();
50030                                 return;
50031                             }
50032                             
50033                             if (c.stylename) {
50034                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50035                                 editorcore.syncValue();
50036                                 return;
50037                             }
50038                             if (r === false) {
50039                                 tb.selectedNode.removeAttribute(c.attrname);
50040                                 editorcore.syncValue();
50041                                 return;
50042                             }
50043                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50044                             editorcore.syncValue();
50045                         }
50046                     }
50047
50048                 }));
50049                 continue;
50050                     
50051                  
50052                 /*
50053                 tb.addField( new Roo.form.TextField({
50054                     name: i,
50055                     width: 100,
50056                     //allowBlank:false,
50057                     value: ''
50058                 }));
50059                 continue;
50060                 */
50061             }
50062             tb.addField( new Roo.form.TextField({
50063                 name: '-roo-edit-' + tlist[i].name,
50064                 attrname : tlist[i].name,
50065                 
50066                 width: item.width,
50067                 //allowBlank:true,
50068                 value: '',
50069                 listeners: {
50070                     'change' : function(f, nv, ov) {
50071                         
50072                         if (tb.selectedNode.hasAttribute('data-block')) {
50073                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50074                             b[f.attrname] = nv;
50075                             b.updateElement(tb.selectedNode);
50076                             editorcore.syncValue();
50077                             return;
50078                         }
50079                         
50080                         tb.selectedNode.setAttribute(f.attrname, nv);
50081                         editorcore.syncValue();
50082                     }
50083                 }
50084             }));
50085              
50086         }
50087         
50088         var _this = this;
50089         
50090         if(nm == 'BODY'){
50091             tb.addSeparator();
50092         
50093             tb.addButton( {
50094                 text: 'Stylesheets',
50095
50096                 listeners : {
50097                     click : function ()
50098                     {
50099                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50100                     }
50101                 }
50102             });
50103         }
50104         
50105         tb.addFill();
50106         tb.addButton({
50107             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50108     
50109             listeners : {
50110                 click : function ()
50111                 {
50112                     var sn = tb.selectedNode;
50113                     if (block) {
50114                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50115                         
50116                     }
50117                     if (!sn) {
50118                         return;
50119                     }
50120                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50121                     if (sn.hasAttribute('data-block')) {
50122                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50123                         sn.parentNode.removeChild(sn);
50124                         
50125                     } else if (sn && sn.tagName != 'BODY') {
50126                         // remove and keep parents.
50127                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50128                         a.removeTag(sn);
50129                     }
50130                     
50131                     
50132                     var range = editorcore.createRange();
50133         
50134                     range.setStart(stn,0);
50135                     range.setEnd(stn,0); 
50136                     var selection = editorcore.getSelection();
50137                     selection.removeAllRanges();
50138                     selection.addRange(range);
50139                     
50140                     
50141                     //_this.updateToolbar(null, null, pn);
50142                     _this.updateToolbar(null, null, null);
50143                     _this.updateFooter(false);
50144                     
50145                 }
50146             }
50147             
50148                     
50149                 
50150             
50151         });
50152         
50153         
50154         tb.el.on('click', function(e){
50155             e.preventDefault(); // what does this do?
50156         });
50157         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50158         tb.el.hide();
50159         
50160         // dont need to disable them... as they will get hidden
50161         return tb;
50162          
50163         
50164     },
50165     buildFooter : function()
50166     {
50167         
50168         var fel = this.editor.wrap.createChild();
50169         this.footer = new Roo.Toolbar(fel);
50170         // toolbar has scrolly on left / right?
50171         var footDisp= new Roo.Toolbar.Fill();
50172         var _t = this;
50173         this.footer.add(
50174             {
50175                 text : '&lt;',
50176                 xtype: 'Button',
50177                 handler : function() {
50178                     _t.footDisp.scrollTo('left',0,true)
50179                 }
50180             }
50181         );
50182         this.footer.add( footDisp );
50183         this.footer.add( 
50184             {
50185                 text : '&gt;',
50186                 xtype: 'Button',
50187                 handler : function() {
50188                     // no animation..
50189                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50190                 }
50191             }
50192         );
50193         var fel = Roo.get(footDisp.el);
50194         fel.addClass('x-editor-context');
50195         this.footDispWrap = fel; 
50196         this.footDispWrap.overflow  = 'hidden';
50197         
50198         this.footDisp = fel.createChild();
50199         this.footDispWrap.on('click', this.onContextClick, this)
50200         
50201         
50202     },
50203     // when the footer contect changes
50204     onContextClick : function (ev,dom)
50205     {
50206         ev.preventDefault();
50207         var  cn = dom.className;
50208         //Roo.log(cn);
50209         if (!cn.match(/x-ed-loc-/)) {
50210             return;
50211         }
50212         var n = cn.split('-').pop();
50213         var ans = this.footerEls;
50214         var sel = ans[n];
50215         
50216         this.editorcore.selectNode(sel);
50217         
50218         
50219         this.updateToolbar(null, null, sel);
50220         
50221         
50222     }
50223     
50224     
50225     
50226     
50227     
50228 });
50229
50230
50231
50232
50233
50234 /*
50235  * Based on:
50236  * Ext JS Library 1.1.1
50237  * Copyright(c) 2006-2007, Ext JS, LLC.
50238  *
50239  * Originally Released Under LGPL - original licence link has changed is not relivant.
50240  *
50241  * Fork - LGPL
50242  * <script type="text/javascript">
50243  */
50244  
50245 /**
50246  * @class Roo.form.BasicForm
50247  * @extends Roo.util.Observable
50248  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50249  * @constructor
50250  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50251  * @param {Object} config Configuration options
50252  */
50253 Roo.form.BasicForm = function(el, config){
50254     this.allItems = [];
50255     this.childForms = [];
50256     Roo.apply(this, config);
50257     /*
50258      * The Roo.form.Field items in this form.
50259      * @type MixedCollection
50260      */
50261      
50262      
50263     this.items = new Roo.util.MixedCollection(false, function(o){
50264         return o.id || (o.id = Roo.id());
50265     });
50266     this.addEvents({
50267         /**
50268          * @event beforeaction
50269          * Fires before any action is performed. Return false to cancel the action.
50270          * @param {Form} this
50271          * @param {Action} action The action to be performed
50272          */
50273         beforeaction: true,
50274         /**
50275          * @event actionfailed
50276          * Fires when an action fails.
50277          * @param {Form} this
50278          * @param {Action} action The action that failed
50279          */
50280         actionfailed : true,
50281         /**
50282          * @event actioncomplete
50283          * Fires when an action is completed.
50284          * @param {Form} this
50285          * @param {Action} action The action that completed
50286          */
50287         actioncomplete : true
50288     });
50289     if(el){
50290         this.initEl(el);
50291     }
50292     Roo.form.BasicForm.superclass.constructor.call(this);
50293     
50294     Roo.form.BasicForm.popover.apply();
50295 };
50296
50297 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50298     /**
50299      * @cfg {String} method
50300      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50301      */
50302     /**
50303      * @cfg {DataReader} reader
50304      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50305      * This is optional as there is built-in support for processing JSON.
50306      */
50307     /**
50308      * @cfg {DataReader} errorReader
50309      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50310      * This is completely optional as there is built-in support for processing JSON.
50311      */
50312     /**
50313      * @cfg {String} url
50314      * The URL to use for form actions if one isn't supplied in the action options.
50315      */
50316     /**
50317      * @cfg {Boolean} fileUpload
50318      * Set to true if this form is a file upload.
50319      */
50320      
50321     /**
50322      * @cfg {Object} baseParams
50323      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50324      */
50325      /**
50326      
50327     /**
50328      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50329      */
50330     timeout: 30,
50331
50332     // private
50333     activeAction : null,
50334
50335     /**
50336      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50337      * or setValues() data instead of when the form was first created.
50338      */
50339     trackResetOnLoad : false,
50340     
50341     
50342     /**
50343      * childForms - used for multi-tab forms
50344      * @type {Array}
50345      */
50346     childForms : false,
50347     
50348     /**
50349      * allItems - full list of fields.
50350      * @type {Array}
50351      */
50352     allItems : false,
50353     
50354     /**
50355      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50356      * element by passing it or its id or mask the form itself by passing in true.
50357      * @type Mixed
50358      */
50359     waitMsgTarget : false,
50360     
50361     /**
50362      * @type Boolean
50363      */
50364     disableMask : false,
50365     
50366     /**
50367      * @cfg {Boolean} errorMask (true|false) default false
50368      */
50369     errorMask : false,
50370     
50371     /**
50372      * @cfg {Number} maskOffset Default 100
50373      */
50374     maskOffset : 100,
50375
50376     // private
50377     initEl : function(el){
50378         this.el = Roo.get(el);
50379         this.id = this.el.id || Roo.id();
50380         this.el.on('submit', this.onSubmit, this);
50381         this.el.addClass('x-form');
50382     },
50383
50384     // private
50385     onSubmit : function(e){
50386         e.stopEvent();
50387     },
50388
50389     /**
50390      * Returns true if client-side validation on the form is successful.
50391      * @return Boolean
50392      */
50393     isValid : function(){
50394         var valid = true;
50395         var target = false;
50396         this.items.each(function(f){
50397             if(f.validate()){
50398                 return;
50399             }
50400             
50401             valid = false;
50402                 
50403             if(!target && f.el.isVisible(true)){
50404                 target = f;
50405             }
50406         });
50407         
50408         if(this.errorMask && !valid){
50409             Roo.form.BasicForm.popover.mask(this, target);
50410         }
50411         
50412         return valid;
50413     },
50414     /**
50415      * Returns array of invalid form fields.
50416      * @return Array
50417      */
50418     
50419     invalidFields : function()
50420     {
50421         var ret = [];
50422         this.items.each(function(f){
50423             if(f.validate()){
50424                 return;
50425             }
50426             ret.push(f);
50427             
50428         });
50429         
50430         return ret;
50431     },
50432     
50433     
50434     /**
50435      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50436      * @return Boolean
50437      */
50438     isDirty : function(){
50439         var dirty = false;
50440         this.items.each(function(f){
50441            if(f.isDirty()){
50442                dirty = true;
50443                return false;
50444            }
50445         });
50446         return dirty;
50447     },
50448     
50449     /**
50450      * Returns true if any fields in this form have changed since their original load. (New version)
50451      * @return Boolean
50452      */
50453     
50454     hasChanged : function()
50455     {
50456         var dirty = false;
50457         this.items.each(function(f){
50458            if(f.hasChanged()){
50459                dirty = true;
50460                return false;
50461            }
50462         });
50463         return dirty;
50464         
50465     },
50466     /**
50467      * Resets all hasChanged to 'false' -
50468      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50469      * So hasChanged storage is only to be used for this purpose
50470      * @return Boolean
50471      */
50472     resetHasChanged : function()
50473     {
50474         this.items.each(function(f){
50475            f.resetHasChanged();
50476         });
50477         
50478     },
50479     
50480     
50481     /**
50482      * Performs a predefined action (submit or load) or custom actions you define on this form.
50483      * @param {String} actionName The name of the action type
50484      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50485      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50486      * accept other config options):
50487      * <pre>
50488 Property          Type             Description
50489 ----------------  ---------------  ----------------------------------------------------------------------------------
50490 url               String           The url for the action (defaults to the form's url)
50491 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50492 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50493 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50494                                    validate the form on the client (defaults to false)
50495      * </pre>
50496      * @return {BasicForm} this
50497      */
50498     doAction : function(action, options){
50499         if(typeof action == 'string'){
50500             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50501         }
50502         if(this.fireEvent('beforeaction', this, action) !== false){
50503             this.beforeAction(action);
50504             action.run.defer(100, action);
50505         }
50506         return this;
50507     },
50508
50509     /**
50510      * Shortcut to do a submit action.
50511      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50512      * @return {BasicForm} this
50513      */
50514     submit : function(options){
50515         this.doAction('submit', options);
50516         return this;
50517     },
50518
50519     /**
50520      * Shortcut to do a load action.
50521      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50522      * @return {BasicForm} this
50523      */
50524     load : function(options){
50525         this.doAction('load', options);
50526         return this;
50527     },
50528
50529     /**
50530      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50531      * @param {Record} record The record to edit
50532      * @return {BasicForm} this
50533      */
50534     updateRecord : function(record){
50535         record.beginEdit();
50536         var fs = record.fields;
50537         fs.each(function(f){
50538             var field = this.findField(f.name);
50539             if(field){
50540                 record.set(f.name, field.getValue());
50541             }
50542         }, this);
50543         record.endEdit();
50544         return this;
50545     },
50546
50547     /**
50548      * Loads an Roo.data.Record into this form.
50549      * @param {Record} record The record to load
50550      * @return {BasicForm} this
50551      */
50552     loadRecord : function(record){
50553         this.setValues(record.data);
50554         return this;
50555     },
50556
50557     // private
50558     beforeAction : function(action){
50559         var o = action.options;
50560         
50561         if(!this.disableMask) {
50562             if(this.waitMsgTarget === true){
50563                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50564             }else if(this.waitMsgTarget){
50565                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50566                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50567             }else {
50568                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50569             }
50570         }
50571         
50572          
50573     },
50574
50575     // private
50576     afterAction : function(action, success){
50577         this.activeAction = null;
50578         var o = action.options;
50579         
50580         if(!this.disableMask) {
50581             if(this.waitMsgTarget === true){
50582                 this.el.unmask();
50583             }else if(this.waitMsgTarget){
50584                 this.waitMsgTarget.unmask();
50585             }else{
50586                 Roo.MessageBox.updateProgress(1);
50587                 Roo.MessageBox.hide();
50588             }
50589         }
50590         
50591         if(success){
50592             if(o.reset){
50593                 this.reset();
50594             }
50595             Roo.callback(o.success, o.scope, [this, action]);
50596             this.fireEvent('actioncomplete', this, action);
50597             
50598         }else{
50599             
50600             // failure condition..
50601             // we have a scenario where updates need confirming.
50602             // eg. if a locking scenario exists..
50603             // we look for { errors : { needs_confirm : true }} in the response.
50604             if (
50605                 (typeof(action.result) != 'undefined')  &&
50606                 (typeof(action.result.errors) != 'undefined')  &&
50607                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50608            ){
50609                 var _t = this;
50610                 Roo.MessageBox.confirm(
50611                     "Change requires confirmation",
50612                     action.result.errorMsg,
50613                     function(r) {
50614                         if (r != 'yes') {
50615                             return;
50616                         }
50617                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50618                     }
50619                     
50620                 );
50621                 
50622                 
50623                 
50624                 return;
50625             }
50626             
50627             Roo.callback(o.failure, o.scope, [this, action]);
50628             // show an error message if no failed handler is set..
50629             if (!this.hasListener('actionfailed')) {
50630                 Roo.MessageBox.alert("Error",
50631                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50632                         action.result.errorMsg :
50633                         "Saving Failed, please check your entries or try again"
50634                 );
50635             }
50636             
50637             this.fireEvent('actionfailed', this, action);
50638         }
50639         
50640     },
50641
50642     /**
50643      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50644      * @param {String} id The value to search for
50645      * @return Field
50646      */
50647     findField : function(id){
50648         var field = this.items.get(id);
50649         if(!field){
50650             this.items.each(function(f){
50651                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50652                     field = f;
50653                     return false;
50654                 }
50655             });
50656         }
50657         return field || null;
50658     },
50659
50660     /**
50661      * Add a secondary form to this one, 
50662      * Used to provide tabbed forms. One form is primary, with hidden values 
50663      * which mirror the elements from the other forms.
50664      * 
50665      * @param {Roo.form.Form} form to add.
50666      * 
50667      */
50668     addForm : function(form)
50669     {
50670        
50671         if (this.childForms.indexOf(form) > -1) {
50672             // already added..
50673             return;
50674         }
50675         this.childForms.push(form);
50676         var n = '';
50677         Roo.each(form.allItems, function (fe) {
50678             
50679             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50680             if (this.findField(n)) { // already added..
50681                 return;
50682             }
50683             var add = new Roo.form.Hidden({
50684                 name : n
50685             });
50686             add.render(this.el);
50687             
50688             this.add( add );
50689         }, this);
50690         
50691     },
50692     /**
50693      * Mark fields in this form invalid in bulk.
50694      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50695      * @return {BasicForm} this
50696      */
50697     markInvalid : function(errors){
50698         if(errors instanceof Array){
50699             for(var i = 0, len = errors.length; i < len; i++){
50700                 var fieldError = errors[i];
50701                 var f = this.findField(fieldError.id);
50702                 if(f){
50703                     f.markInvalid(fieldError.msg);
50704                 }
50705             }
50706         }else{
50707             var field, id;
50708             for(id in errors){
50709                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50710                     field.markInvalid(errors[id]);
50711                 }
50712             }
50713         }
50714         Roo.each(this.childForms || [], function (f) {
50715             f.markInvalid(errors);
50716         });
50717         
50718         return this;
50719     },
50720
50721     /**
50722      * Set values for fields in this form in bulk.
50723      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50724      * @return {BasicForm} this
50725      */
50726     setValues : function(values){
50727         if(values instanceof Array){ // array of objects
50728             for(var i = 0, len = values.length; i < len; i++){
50729                 var v = values[i];
50730                 var f = this.findField(v.id);
50731                 if(f){
50732                     f.setValue(v.value);
50733                     if(this.trackResetOnLoad){
50734                         f.originalValue = f.getValue();
50735                     }
50736                 }
50737             }
50738         }else{ // object hash
50739             var field, id;
50740             for(id in values){
50741                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50742                     
50743                     if (field.setFromData && 
50744                         field.valueField && 
50745                         field.displayField &&
50746                         // combos' with local stores can 
50747                         // be queried via setValue()
50748                         // to set their value..
50749                         (field.store && !field.store.isLocal)
50750                         ) {
50751                         // it's a combo
50752                         var sd = { };
50753                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50754                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50755                         field.setFromData(sd);
50756                         
50757                     } else {
50758                         field.setValue(values[id]);
50759                     }
50760                     
50761                     
50762                     if(this.trackResetOnLoad){
50763                         field.originalValue = field.getValue();
50764                     }
50765                 }
50766             }
50767         }
50768         this.resetHasChanged();
50769         
50770         
50771         Roo.each(this.childForms || [], function (f) {
50772             f.setValues(values);
50773             f.resetHasChanged();
50774         });
50775                 
50776         return this;
50777     },
50778  
50779     /**
50780      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50781      * they are returned as an array.
50782      * @param {Boolean} asString
50783      * @return {Object}
50784      */
50785     getValues : function(asString)
50786     {
50787         if (this.childForms) {
50788             // copy values from the child forms
50789             Roo.each(this.childForms, function (f) {
50790                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50791             }, this);
50792         }
50793         
50794         // use formdata
50795         if (typeof(FormData) != 'undefined' && asString !== true) {
50796             // this relies on a 'recent' version of chrome apparently...
50797             try {
50798                 var fd = (new FormData(this.el.dom)).entries();
50799                 var ret = {};
50800                 var ent = fd.next();
50801                 while (!ent.done) {
50802                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50803                     ent = fd.next();
50804                 };
50805                 return ret;
50806             } catch(e) {
50807                 
50808             }
50809             
50810         }
50811         
50812         
50813         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50814         if(asString === true){
50815             return fs;
50816         }
50817         return Roo.urlDecode(fs);
50818     },
50819     
50820     /**
50821      * Returns the fields in this form as an object with key/value pairs. 
50822      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50823      * @return {Object}
50824      */
50825     getFieldValues : function(with_hidden)
50826     {
50827         if (this.childForms) {
50828             // copy values from the child forms
50829             // should this call getFieldValues - probably not as we do not currently copy
50830             // hidden fields when we generate..
50831             Roo.each(this.childForms, function (f) {
50832                 this.setValues(f.getFieldValues());
50833             }, this);
50834         }
50835         
50836         var ret = {};
50837         this.items.each(function(f){
50838             
50839             if (f.readOnly) {
50840                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50841                         // if a subform contains a copy of them.
50842                         // if you have subforms with the same editable data, you will need to copy the data back
50843                         // and forth.
50844             }
50845             
50846             if (!f.getName()) {
50847                 return;
50848             }
50849             var v = f.getValue();
50850             if (f.inputType =='radio') {
50851                 if (typeof(ret[f.getName()]) == 'undefined') {
50852                     ret[f.getName()] = ''; // empty..
50853                 }
50854                 
50855                 if (!f.el.dom.checked) {
50856                     return;
50857                     
50858                 }
50859                 v = f.el.dom.value;
50860                 
50861             }
50862             
50863             // not sure if this supported any more..
50864             if ((typeof(v) == 'object') && f.getRawValue) {
50865                 v = f.getRawValue() ; // dates..
50866             }
50867             // combo boxes where name != hiddenName...
50868             if (f.name != f.getName()) {
50869                 ret[f.name] = f.getRawValue();
50870             }
50871             ret[f.getName()] = v;
50872         });
50873         
50874         return ret;
50875     },
50876
50877     /**
50878      * Clears all invalid messages in this form.
50879      * @return {BasicForm} this
50880      */
50881     clearInvalid : function(){
50882         this.items.each(function(f){
50883            f.clearInvalid();
50884         });
50885         
50886         Roo.each(this.childForms || [], function (f) {
50887             f.clearInvalid();
50888         });
50889         
50890         
50891         return this;
50892     },
50893
50894     /**
50895      * Resets this form.
50896      * @return {BasicForm} this
50897      */
50898     reset : function(){
50899         this.items.each(function(f){
50900             f.reset();
50901         });
50902         
50903         Roo.each(this.childForms || [], function (f) {
50904             f.reset();
50905         });
50906         this.resetHasChanged();
50907         
50908         return this;
50909     },
50910
50911     /**
50912      * Add Roo.form components to this form.
50913      * @param {Field} field1
50914      * @param {Field} field2 (optional)
50915      * @param {Field} etc (optional)
50916      * @return {BasicForm} this
50917      */
50918     add : function(){
50919         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50920         return this;
50921     },
50922
50923
50924     /**
50925      * Removes a field from the items collection (does NOT remove its markup).
50926      * @param {Field} field
50927      * @return {BasicForm} this
50928      */
50929     remove : function(field){
50930         this.items.remove(field);
50931         return this;
50932     },
50933
50934     /**
50935      * Looks at the fields in this form, checks them for an id attribute,
50936      * and calls applyTo on the existing dom element with that id.
50937      * @return {BasicForm} this
50938      */
50939     render : function(){
50940         this.items.each(function(f){
50941             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50942                 f.applyTo(f.id);
50943             }
50944         });
50945         return this;
50946     },
50947
50948     /**
50949      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50950      * @param {Object} values
50951      * @return {BasicForm} this
50952      */
50953     applyToFields : function(o){
50954         this.items.each(function(f){
50955            Roo.apply(f, o);
50956         });
50957         return this;
50958     },
50959
50960     /**
50961      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50962      * @param {Object} values
50963      * @return {BasicForm} this
50964      */
50965     applyIfToFields : function(o){
50966         this.items.each(function(f){
50967            Roo.applyIf(f, o);
50968         });
50969         return this;
50970     }
50971 });
50972
50973 // back compat
50974 Roo.BasicForm = Roo.form.BasicForm;
50975
50976 Roo.apply(Roo.form.BasicForm, {
50977     
50978     popover : {
50979         
50980         padding : 5,
50981         
50982         isApplied : false,
50983         
50984         isMasked : false,
50985         
50986         form : false,
50987         
50988         target : false,
50989         
50990         intervalID : false,
50991         
50992         maskEl : false,
50993         
50994         apply : function()
50995         {
50996             if(this.isApplied){
50997                 return;
50998             }
50999             
51000             this.maskEl = {
51001                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51002                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51003                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51004                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51005             };
51006             
51007             this.maskEl.top.enableDisplayMode("block");
51008             this.maskEl.left.enableDisplayMode("block");
51009             this.maskEl.bottom.enableDisplayMode("block");
51010             this.maskEl.right.enableDisplayMode("block");
51011             
51012             Roo.get(document.body).on('click', function(){
51013                 this.unmask();
51014             }, this);
51015             
51016             Roo.get(document.body).on('touchstart', function(){
51017                 this.unmask();
51018             }, this);
51019             
51020             this.isApplied = true
51021         },
51022         
51023         mask : function(form, target)
51024         {
51025             this.form = form;
51026             
51027             this.target = target;
51028             
51029             if(!this.form.errorMask || !target.el){
51030                 return;
51031             }
51032             
51033             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51034             
51035             var ot = this.target.el.calcOffsetsTo(scrollable);
51036             
51037             var scrollTo = ot[1] - this.form.maskOffset;
51038             
51039             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51040             
51041             scrollable.scrollTo('top', scrollTo);
51042             
51043             var el = this.target.wrap || this.target.el;
51044             
51045             var box = el.getBox();
51046             
51047             this.maskEl.top.setStyle('position', 'absolute');
51048             this.maskEl.top.setStyle('z-index', 10000);
51049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51050             this.maskEl.top.setLeft(0);
51051             this.maskEl.top.setTop(0);
51052             this.maskEl.top.show();
51053             
51054             this.maskEl.left.setStyle('position', 'absolute');
51055             this.maskEl.left.setStyle('z-index', 10000);
51056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51057             this.maskEl.left.setLeft(0);
51058             this.maskEl.left.setTop(box.y - this.padding);
51059             this.maskEl.left.show();
51060
51061             this.maskEl.bottom.setStyle('position', 'absolute');
51062             this.maskEl.bottom.setStyle('z-index', 10000);
51063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51064             this.maskEl.bottom.setLeft(0);
51065             this.maskEl.bottom.setTop(box.bottom + this.padding);
51066             this.maskEl.bottom.show();
51067
51068             this.maskEl.right.setStyle('position', 'absolute');
51069             this.maskEl.right.setStyle('z-index', 10000);
51070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51071             this.maskEl.right.setLeft(box.right + this.padding);
51072             this.maskEl.right.setTop(box.y - this.padding);
51073             this.maskEl.right.show();
51074
51075             this.intervalID = window.setInterval(function() {
51076                 Roo.form.BasicForm.popover.unmask();
51077             }, 10000);
51078
51079             window.onwheel = function(){ return false;};
51080             
51081             (function(){ this.isMasked = true; }).defer(500, this);
51082             
51083         },
51084         
51085         unmask : function()
51086         {
51087             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51088                 return;
51089             }
51090             
51091             this.maskEl.top.setStyle('position', 'absolute');
51092             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51093             this.maskEl.top.hide();
51094
51095             this.maskEl.left.setStyle('position', 'absolute');
51096             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51097             this.maskEl.left.hide();
51098
51099             this.maskEl.bottom.setStyle('position', 'absolute');
51100             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51101             this.maskEl.bottom.hide();
51102
51103             this.maskEl.right.setStyle('position', 'absolute');
51104             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51105             this.maskEl.right.hide();
51106             
51107             window.onwheel = function(){ return true;};
51108             
51109             if(this.intervalID){
51110                 window.clearInterval(this.intervalID);
51111                 this.intervalID = false;
51112             }
51113             
51114             this.isMasked = false;
51115             
51116         }
51117         
51118     }
51119     
51120 });/*
51121  * Based on:
51122  * Ext JS Library 1.1.1
51123  * Copyright(c) 2006-2007, Ext JS, LLC.
51124  *
51125  * Originally Released Under LGPL - original licence link has changed is not relivant.
51126  *
51127  * Fork - LGPL
51128  * <script type="text/javascript">
51129  */
51130
51131 /**
51132  * @class Roo.form.Form
51133  * @extends Roo.form.BasicForm
51134  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51135  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51136  * @constructor
51137  * @param {Object} config Configuration options
51138  */
51139 Roo.form.Form = function(config){
51140     var xitems =  [];
51141     if (config.items) {
51142         xitems = config.items;
51143         delete config.items;
51144     }
51145    
51146     
51147     Roo.form.Form.superclass.constructor.call(this, null, config);
51148     this.url = this.url || this.action;
51149     if(!this.root){
51150         this.root = new Roo.form.Layout(Roo.applyIf({
51151             id: Roo.id()
51152         }, config));
51153     }
51154     this.active = this.root;
51155     /**
51156      * Array of all the buttons that have been added to this form via {@link addButton}
51157      * @type Array
51158      */
51159     this.buttons = [];
51160     this.allItems = [];
51161     this.addEvents({
51162         /**
51163          * @event clientvalidation
51164          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51165          * @param {Form} this
51166          * @param {Boolean} valid true if the form has passed client-side validation
51167          */
51168         clientvalidation: true,
51169         /**
51170          * @event rendered
51171          * Fires when the form is rendered
51172          * @param {Roo.form.Form} form
51173          */
51174         rendered : true
51175     });
51176     
51177     if (this.progressUrl) {
51178             // push a hidden field onto the list of fields..
51179             this.addxtype( {
51180                     xns: Roo.form, 
51181                     xtype : 'Hidden', 
51182                     name : 'UPLOAD_IDENTIFIER' 
51183             });
51184         }
51185         
51186     
51187     Roo.each(xitems, this.addxtype, this);
51188     
51189 };
51190
51191 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51192      /**
51193      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51194      */
51195     
51196     /**
51197      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51198      */
51199     /**
51200      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51201      */
51202     /**
51203      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51204      */
51205     buttonAlign:'center',
51206
51207     /**
51208      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51209      */
51210     minButtonWidth:75,
51211
51212     /**
51213      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51214      * This property cascades to child containers if not set.
51215      */
51216     labelAlign:'left',
51217
51218     /**
51219      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51220      * fires a looping event with that state. This is required to bind buttons to the valid
51221      * state using the config value formBind:true on the button.
51222      */
51223     monitorValid : false,
51224
51225     /**
51226      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51227      */
51228     monitorPoll : 200,
51229     
51230     /**
51231      * @cfg {String} progressUrl - Url to return progress data 
51232      */
51233     
51234     progressUrl : false,
51235     /**
51236      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51237      * sending a formdata with extra parameters - eg uploaded elements.
51238      */
51239     
51240     formData : false,
51241     
51242     /**
51243      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51244      * fields are added and the column is closed. If no fields are passed the column remains open
51245      * until end() is called.
51246      * @param {Object} config The config to pass to the column
51247      * @param {Field} field1 (optional)
51248      * @param {Field} field2 (optional)
51249      * @param {Field} etc (optional)
51250      * @return Column The column container object
51251      */
51252     column : function(c){
51253         var col = new Roo.form.Column(c);
51254         this.start(col);
51255         if(arguments.length > 1){ // duplicate code required because of Opera
51256             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51257             this.end();
51258         }
51259         return col;
51260     },
51261
51262     /**
51263      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51264      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51265      * until end() is called.
51266      * @param {Object} config The config to pass to the fieldset
51267      * @param {Field} field1 (optional)
51268      * @param {Field} field2 (optional)
51269      * @param {Field} etc (optional)
51270      * @return FieldSet The fieldset container object
51271      */
51272     fieldset : function(c){
51273         var fs = new Roo.form.FieldSet(c);
51274         this.start(fs);
51275         if(arguments.length > 1){ // duplicate code required because of Opera
51276             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51277             this.end();
51278         }
51279         return fs;
51280     },
51281
51282     /**
51283      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51284      * fields are added and the container is closed. If no fields are passed the container remains open
51285      * until end() is called.
51286      * @param {Object} config The config to pass to the Layout
51287      * @param {Field} field1 (optional)
51288      * @param {Field} field2 (optional)
51289      * @param {Field} etc (optional)
51290      * @return Layout The container object
51291      */
51292     container : function(c){
51293         var l = new Roo.form.Layout(c);
51294         this.start(l);
51295         if(arguments.length > 1){ // duplicate code required because of Opera
51296             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51297             this.end();
51298         }
51299         return l;
51300     },
51301
51302     /**
51303      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51304      * @param {Object} container A Roo.form.Layout or subclass of Layout
51305      * @return {Form} this
51306      */
51307     start : function(c){
51308         // cascade label info
51309         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51310         this.active.stack.push(c);
51311         c.ownerCt = this.active;
51312         this.active = c;
51313         return this;
51314     },
51315
51316     /**
51317      * Closes the current open container
51318      * @return {Form} this
51319      */
51320     end : function(){
51321         if(this.active == this.root){
51322             return this;
51323         }
51324         this.active = this.active.ownerCt;
51325         return this;
51326     },
51327
51328     /**
51329      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51330      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51331      * as the label of the field.
51332      * @param {Field} field1
51333      * @param {Field} field2 (optional)
51334      * @param {Field} etc. (optional)
51335      * @return {Form} this
51336      */
51337     add : function(){
51338         this.active.stack.push.apply(this.active.stack, arguments);
51339         this.allItems.push.apply(this.allItems,arguments);
51340         var r = [];
51341         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51342             if(a[i].isFormField){
51343                 r.push(a[i]);
51344             }
51345         }
51346         if(r.length > 0){
51347             Roo.form.Form.superclass.add.apply(this, r);
51348         }
51349         return this;
51350     },
51351     
51352
51353     
51354     
51355     
51356      /**
51357      * Find any element that has been added to a form, using it's ID or name
51358      * This can include framesets, columns etc. along with regular fields..
51359      * @param {String} id - id or name to find.
51360      
51361      * @return {Element} e - or false if nothing found.
51362      */
51363     findbyId : function(id)
51364     {
51365         var ret = false;
51366         if (!id) {
51367             return ret;
51368         }
51369         Roo.each(this.allItems, function(f){
51370             if (f.id == id || f.name == id ){
51371                 ret = f;
51372                 return false;
51373             }
51374         });
51375         return ret;
51376     },
51377
51378     
51379     
51380     /**
51381      * Render this form into the passed container. This should only be called once!
51382      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51383      * @return {Form} this
51384      */
51385     render : function(ct)
51386     {
51387         
51388         
51389         
51390         ct = Roo.get(ct);
51391         var o = this.autoCreate || {
51392             tag: 'form',
51393             method : this.method || 'POST',
51394             id : this.id || Roo.id()
51395         };
51396         this.initEl(ct.createChild(o));
51397
51398         this.root.render(this.el);
51399         
51400        
51401              
51402         this.items.each(function(f){
51403             f.render('x-form-el-'+f.id);
51404         });
51405
51406         if(this.buttons.length > 0){
51407             // tables are required to maintain order and for correct IE layout
51408             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51409                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51410                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51411             }}, null, true);
51412             var tr = tb.getElementsByTagName('tr')[0];
51413             for(var i = 0, len = this.buttons.length; i < len; i++) {
51414                 var b = this.buttons[i];
51415                 var td = document.createElement('td');
51416                 td.className = 'x-form-btn-td';
51417                 b.render(tr.appendChild(td));
51418             }
51419         }
51420         if(this.monitorValid){ // initialize after render
51421             this.startMonitoring();
51422         }
51423         this.fireEvent('rendered', this);
51424         return this;
51425     },
51426
51427     /**
51428      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51429      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51430      * object or a valid Roo.DomHelper element config
51431      * @param {Function} handler The function called when the button is clicked
51432      * @param {Object} scope (optional) The scope of the handler function
51433      * @return {Roo.Button}
51434      */
51435     addButton : function(config, handler, scope){
51436         var bc = {
51437             handler: handler,
51438             scope: scope,
51439             minWidth: this.minButtonWidth,
51440             hideParent:true
51441         };
51442         if(typeof config == "string"){
51443             bc.text = config;
51444         }else{
51445             Roo.apply(bc, config);
51446         }
51447         var btn = new Roo.Button(null, bc);
51448         this.buttons.push(btn);
51449         return btn;
51450     },
51451
51452      /**
51453      * Adds a series of form elements (using the xtype property as the factory method.
51454      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51455      * @param {Object} config 
51456      */
51457     
51458     addxtype : function()
51459     {
51460         var ar = Array.prototype.slice.call(arguments, 0);
51461         var ret = false;
51462         for(var i = 0; i < ar.length; i++) {
51463             if (!ar[i]) {
51464                 continue; // skip -- if this happends something invalid got sent, we 
51465                 // should ignore it, as basically that interface element will not show up
51466                 // and that should be pretty obvious!!
51467             }
51468             
51469             if (Roo.form[ar[i].xtype]) {
51470                 ar[i].form = this;
51471                 var fe = Roo.factory(ar[i], Roo.form);
51472                 if (!ret) {
51473                     ret = fe;
51474                 }
51475                 fe.form = this;
51476                 if (fe.store) {
51477                     fe.store.form = this;
51478                 }
51479                 if (fe.isLayout) {  
51480                          
51481                     this.start(fe);
51482                     this.allItems.push(fe);
51483                     if (fe.items && fe.addxtype) {
51484                         fe.addxtype.apply(fe, fe.items);
51485                         delete fe.items;
51486                     }
51487                      this.end();
51488                     continue;
51489                 }
51490                 
51491                 
51492                  
51493                 this.add(fe);
51494               //  console.log('adding ' + ar[i].xtype);
51495             }
51496             if (ar[i].xtype == 'Button') {  
51497                 //console.log('adding button');
51498                 //console.log(ar[i]);
51499                 this.addButton(ar[i]);
51500                 this.allItems.push(fe);
51501                 continue;
51502             }
51503             
51504             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51505                 alert('end is not supported on xtype any more, use items');
51506             //    this.end();
51507             //    //console.log('adding end');
51508             }
51509             
51510         }
51511         return ret;
51512     },
51513     
51514     /**
51515      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51516      * option "monitorValid"
51517      */
51518     startMonitoring : function(){
51519         if(!this.bound){
51520             this.bound = true;
51521             Roo.TaskMgr.start({
51522                 run : this.bindHandler,
51523                 interval : this.monitorPoll || 200,
51524                 scope: this
51525             });
51526         }
51527     },
51528
51529     /**
51530      * Stops monitoring of the valid state of this form
51531      */
51532     stopMonitoring : function(){
51533         this.bound = false;
51534     },
51535
51536     // private
51537     bindHandler : function(){
51538         if(!this.bound){
51539             return false; // stops binding
51540         }
51541         var valid = true;
51542         this.items.each(function(f){
51543             if(!f.isValid(true)){
51544                 valid = false;
51545                 return false;
51546             }
51547         });
51548         for(var i = 0, len = this.buttons.length; i < len; i++){
51549             var btn = this.buttons[i];
51550             if(btn.formBind === true && btn.disabled === valid){
51551                 btn.setDisabled(!valid);
51552             }
51553         }
51554         this.fireEvent('clientvalidation', this, valid);
51555     }
51556     
51557     
51558     
51559     
51560     
51561     
51562     
51563     
51564 });
51565
51566
51567 // back compat
51568 Roo.Form = Roo.form.Form;
51569 /*
51570  * Based on:
51571  * Ext JS Library 1.1.1
51572  * Copyright(c) 2006-2007, Ext JS, LLC.
51573  *
51574  * Originally Released Under LGPL - original licence link has changed is not relivant.
51575  *
51576  * Fork - LGPL
51577  * <script type="text/javascript">
51578  */
51579
51580 // as we use this in bootstrap.
51581 Roo.namespace('Roo.form');
51582  /**
51583  * @class Roo.form.Action
51584  * Internal Class used to handle form actions
51585  * @constructor
51586  * @param {Roo.form.BasicForm} el The form element or its id
51587  * @param {Object} config Configuration options
51588  */
51589
51590  
51591  
51592 // define the action interface
51593 Roo.form.Action = function(form, options){
51594     this.form = form;
51595     this.options = options || {};
51596 };
51597 /**
51598  * Client Validation Failed
51599  * @const 
51600  */
51601 Roo.form.Action.CLIENT_INVALID = 'client';
51602 /**
51603  * Server Validation Failed
51604  * @const 
51605  */
51606 Roo.form.Action.SERVER_INVALID = 'server';
51607  /**
51608  * Connect to Server Failed
51609  * @const 
51610  */
51611 Roo.form.Action.CONNECT_FAILURE = 'connect';
51612 /**
51613  * Reading Data from Server Failed
51614  * @const 
51615  */
51616 Roo.form.Action.LOAD_FAILURE = 'load';
51617
51618 Roo.form.Action.prototype = {
51619     type : 'default',
51620     failureType : undefined,
51621     response : undefined,
51622     result : undefined,
51623
51624     // interface method
51625     run : function(options){
51626
51627     },
51628
51629     // interface method
51630     success : function(response){
51631
51632     },
51633
51634     // interface method
51635     handleResponse : function(response){
51636
51637     },
51638
51639     // default connection failure
51640     failure : function(response){
51641         
51642         this.response = response;
51643         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51644         this.form.afterAction(this, false);
51645     },
51646
51647     processResponse : function(response){
51648         this.response = response;
51649         if(!response.responseText){
51650             return true;
51651         }
51652         this.result = this.handleResponse(response);
51653         return this.result;
51654     },
51655
51656     // utility functions used internally
51657     getUrl : function(appendParams){
51658         var url = this.options.url || this.form.url || this.form.el.dom.action;
51659         if(appendParams){
51660             var p = this.getParams();
51661             if(p){
51662                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51663             }
51664         }
51665         return url;
51666     },
51667
51668     getMethod : function(){
51669         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51670     },
51671
51672     getParams : function(){
51673         var bp = this.form.baseParams;
51674         var p = this.options.params;
51675         if(p){
51676             if(typeof p == "object"){
51677                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51678             }else if(typeof p == 'string' && bp){
51679                 p += '&' + Roo.urlEncode(bp);
51680             }
51681         }else if(bp){
51682             p = Roo.urlEncode(bp);
51683         }
51684         return p;
51685     },
51686
51687     createCallback : function(){
51688         return {
51689             success: this.success,
51690             failure: this.failure,
51691             scope: this,
51692             timeout: (this.form.timeout*1000),
51693             upload: this.form.fileUpload ? this.success : undefined
51694         };
51695     }
51696 };
51697
51698 Roo.form.Action.Submit = function(form, options){
51699     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51700 };
51701
51702 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51703     type : 'submit',
51704
51705     haveProgress : false,
51706     uploadComplete : false,
51707     
51708     // uploadProgress indicator.
51709     uploadProgress : function()
51710     {
51711         if (!this.form.progressUrl) {
51712             return;
51713         }
51714         
51715         if (!this.haveProgress) {
51716             Roo.MessageBox.progress("Uploading", "Uploading");
51717         }
51718         if (this.uploadComplete) {
51719            Roo.MessageBox.hide();
51720            return;
51721         }
51722         
51723         this.haveProgress = true;
51724    
51725         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51726         
51727         var c = new Roo.data.Connection();
51728         c.request({
51729             url : this.form.progressUrl,
51730             params: {
51731                 id : uid
51732             },
51733             method: 'GET',
51734             success : function(req){
51735                //console.log(data);
51736                 var rdata = false;
51737                 var edata;
51738                 try  {
51739                    rdata = Roo.decode(req.responseText)
51740                 } catch (e) {
51741                     Roo.log("Invalid data from server..");
51742                     Roo.log(edata);
51743                     return;
51744                 }
51745                 if (!rdata || !rdata.success) {
51746                     Roo.log(rdata);
51747                     Roo.MessageBox.alert(Roo.encode(rdata));
51748                     return;
51749                 }
51750                 var data = rdata.data;
51751                 
51752                 if (this.uploadComplete) {
51753                    Roo.MessageBox.hide();
51754                    return;
51755                 }
51756                    
51757                 if (data){
51758                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51759                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51760                     );
51761                 }
51762                 this.uploadProgress.defer(2000,this);
51763             },
51764        
51765             failure: function(data) {
51766                 Roo.log('progress url failed ');
51767                 Roo.log(data);
51768             },
51769             scope : this
51770         });
51771            
51772     },
51773     
51774     
51775     run : function()
51776     {
51777         // run get Values on the form, so it syncs any secondary forms.
51778         this.form.getValues();
51779         
51780         var o = this.options;
51781         var method = this.getMethod();
51782         var isPost = method == 'POST';
51783         if(o.clientValidation === false || this.form.isValid()){
51784             
51785             if (this.form.progressUrl) {
51786                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51787                     (new Date() * 1) + '' + Math.random());
51788                     
51789             } 
51790             
51791             
51792             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51793                 form:this.form.el.dom,
51794                 url:this.getUrl(!isPost),
51795                 method: method,
51796                 params:isPost ? this.getParams() : null,
51797                 isUpload: this.form.fileUpload,
51798                 formData : this.form.formData
51799             }));
51800             
51801             this.uploadProgress();
51802
51803         }else if (o.clientValidation !== false){ // client validation failed
51804             this.failureType = Roo.form.Action.CLIENT_INVALID;
51805             this.form.afterAction(this, false);
51806         }
51807     },
51808
51809     success : function(response)
51810     {
51811         this.uploadComplete= true;
51812         if (this.haveProgress) {
51813             Roo.MessageBox.hide();
51814         }
51815         
51816         
51817         var result = this.processResponse(response);
51818         if(result === true || result.success){
51819             this.form.afterAction(this, true);
51820             return;
51821         }
51822         if(result.errors){
51823             this.form.markInvalid(result.errors);
51824             this.failureType = Roo.form.Action.SERVER_INVALID;
51825         }
51826         this.form.afterAction(this, false);
51827     },
51828     failure : function(response)
51829     {
51830         this.uploadComplete= true;
51831         if (this.haveProgress) {
51832             Roo.MessageBox.hide();
51833         }
51834         
51835         this.response = response;
51836         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51837         this.form.afterAction(this, false);
51838     },
51839     
51840     handleResponse : function(response){
51841         if(this.form.errorReader){
51842             var rs = this.form.errorReader.read(response);
51843             var errors = [];
51844             if(rs.records){
51845                 for(var i = 0, len = rs.records.length; i < len; i++) {
51846                     var r = rs.records[i];
51847                     errors[i] = r.data;
51848                 }
51849             }
51850             if(errors.length < 1){
51851                 errors = null;
51852             }
51853             return {
51854                 success : rs.success,
51855                 errors : errors
51856             };
51857         }
51858         var ret = false;
51859         try {
51860             ret = Roo.decode(response.responseText);
51861         } catch (e) {
51862             ret = {
51863                 success: false,
51864                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51865                 errors : []
51866             };
51867         }
51868         return ret;
51869         
51870     }
51871 });
51872
51873
51874 Roo.form.Action.Load = function(form, options){
51875     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51876     this.reader = this.form.reader;
51877 };
51878
51879 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51880     type : 'load',
51881
51882     run : function(){
51883         
51884         Roo.Ajax.request(Roo.apply(
51885                 this.createCallback(), {
51886                     method:this.getMethod(),
51887                     url:this.getUrl(false),
51888                     params:this.getParams()
51889         }));
51890     },
51891
51892     success : function(response){
51893         
51894         var result = this.processResponse(response);
51895         if(result === true || !result.success || !result.data){
51896             this.failureType = Roo.form.Action.LOAD_FAILURE;
51897             this.form.afterAction(this, false);
51898             return;
51899         }
51900         this.form.clearInvalid();
51901         this.form.setValues(result.data);
51902         this.form.afterAction(this, true);
51903     },
51904
51905     handleResponse : function(response){
51906         if(this.form.reader){
51907             var rs = this.form.reader.read(response);
51908             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51909             return {
51910                 success : rs.success,
51911                 data : data
51912             };
51913         }
51914         return Roo.decode(response.responseText);
51915     }
51916 });
51917
51918 Roo.form.Action.ACTION_TYPES = {
51919     'load' : Roo.form.Action.Load,
51920     'submit' : Roo.form.Action.Submit
51921 };/*
51922  * Based on:
51923  * Ext JS Library 1.1.1
51924  * Copyright(c) 2006-2007, Ext JS, LLC.
51925  *
51926  * Originally Released Under LGPL - original licence link has changed is not relivant.
51927  *
51928  * Fork - LGPL
51929  * <script type="text/javascript">
51930  */
51931  
51932 /**
51933  * @class Roo.form.Layout
51934  * @extends Roo.Component
51935  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51936  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51937  * @constructor
51938  * @param {Object} config Configuration options
51939  */
51940 Roo.form.Layout = function(config){
51941     var xitems = [];
51942     if (config.items) {
51943         xitems = config.items;
51944         delete config.items;
51945     }
51946     Roo.form.Layout.superclass.constructor.call(this, config);
51947     this.stack = [];
51948     Roo.each(xitems, this.addxtype, this);
51949      
51950 };
51951
51952 Roo.extend(Roo.form.Layout, Roo.Component, {
51953     /**
51954      * @cfg {String/Object} autoCreate
51955      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51956      */
51957     /**
51958      * @cfg {String/Object/Function} style
51959      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51960      * a function which returns such a specification.
51961      */
51962     /**
51963      * @cfg {String} labelAlign
51964      * Valid values are "left," "top" and "right" (defaults to "left")
51965      */
51966     /**
51967      * @cfg {Number} labelWidth
51968      * Fixed width in pixels of all field labels (defaults to undefined)
51969      */
51970     /**
51971      * @cfg {Boolean} clear
51972      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51973      */
51974     clear : true,
51975     /**
51976      * @cfg {String} labelSeparator
51977      * The separator to use after field labels (defaults to ':')
51978      */
51979     labelSeparator : ':',
51980     /**
51981      * @cfg {Boolean} hideLabels
51982      * True to suppress the display of field labels in this layout (defaults to false)
51983      */
51984     hideLabels : false,
51985
51986     // private
51987     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51988     
51989     isLayout : true,
51990     
51991     // private
51992     onRender : function(ct, position){
51993         if(this.el){ // from markup
51994             this.el = Roo.get(this.el);
51995         }else {  // generate
51996             var cfg = this.getAutoCreate();
51997             this.el = ct.createChild(cfg, position);
51998         }
51999         if(this.style){
52000             this.el.applyStyles(this.style);
52001         }
52002         if(this.labelAlign){
52003             this.el.addClass('x-form-label-'+this.labelAlign);
52004         }
52005         if(this.hideLabels){
52006             this.labelStyle = "display:none";
52007             this.elementStyle = "padding-left:0;";
52008         }else{
52009             if(typeof this.labelWidth == 'number'){
52010                 this.labelStyle = "width:"+this.labelWidth+"px;";
52011                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52012             }
52013             if(this.labelAlign == 'top'){
52014                 this.labelStyle = "width:auto;";
52015                 this.elementStyle = "padding-left:0;";
52016             }
52017         }
52018         var stack = this.stack;
52019         var slen = stack.length;
52020         if(slen > 0){
52021             if(!this.fieldTpl){
52022                 var t = new Roo.Template(
52023                     '<div class="x-form-item {5}">',
52024                         '<label for="{0}" style="{2}">{1}{4}</label>',
52025                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52026                         '</div>',
52027                     '</div><div class="x-form-clear-left"></div>'
52028                 );
52029                 t.disableFormats = true;
52030                 t.compile();
52031                 Roo.form.Layout.prototype.fieldTpl = t;
52032             }
52033             for(var i = 0; i < slen; i++) {
52034                 if(stack[i].isFormField){
52035                     this.renderField(stack[i]);
52036                 }else{
52037                     this.renderComponent(stack[i]);
52038                 }
52039             }
52040         }
52041         if(this.clear){
52042             this.el.createChild({cls:'x-form-clear'});
52043         }
52044     },
52045
52046     // private
52047     renderField : function(f){
52048         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52049                f.id, //0
52050                f.fieldLabel, //1
52051                f.labelStyle||this.labelStyle||'', //2
52052                this.elementStyle||'', //3
52053                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52054                f.itemCls||this.itemCls||''  //5
52055        ], true).getPrevSibling());
52056     },
52057
52058     // private
52059     renderComponent : function(c){
52060         c.render(c.isLayout ? this.el : this.el.createChild());    
52061     },
52062     /**
52063      * Adds a object form elements (using the xtype property as the factory method.)
52064      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52065      * @param {Object} config 
52066      */
52067     addxtype : function(o)
52068     {
52069         // create the lement.
52070         o.form = this.form;
52071         var fe = Roo.factory(o, Roo.form);
52072         this.form.allItems.push(fe);
52073         this.stack.push(fe);
52074         
52075         if (fe.isFormField) {
52076             this.form.items.add(fe);
52077         }
52078          
52079         return fe;
52080     }
52081 });
52082
52083 /**
52084  * @class Roo.form.Column
52085  * @extends Roo.form.Layout
52086  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52087  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52088  * @constructor
52089  * @param {Object} config Configuration options
52090  */
52091 Roo.form.Column = function(config){
52092     Roo.form.Column.superclass.constructor.call(this, config);
52093 };
52094
52095 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52096     /**
52097      * @cfg {Number/String} width
52098      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52099      */
52100     /**
52101      * @cfg {String/Object} autoCreate
52102      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52103      */
52104
52105     // private
52106     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52107
52108     // private
52109     onRender : function(ct, position){
52110         Roo.form.Column.superclass.onRender.call(this, ct, position);
52111         if(this.width){
52112             this.el.setWidth(this.width);
52113         }
52114     }
52115 });
52116
52117
52118 /**
52119  * @class Roo.form.Row
52120  * @extends Roo.form.Layout
52121  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52122  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52123  * @constructor
52124  * @param {Object} config Configuration options
52125  */
52126
52127  
52128 Roo.form.Row = function(config){
52129     Roo.form.Row.superclass.constructor.call(this, config);
52130 };
52131  
52132 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52133       /**
52134      * @cfg {Number/String} width
52135      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52136      */
52137     /**
52138      * @cfg {Number/String} height
52139      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52140      */
52141     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52142     
52143     padWidth : 20,
52144     // private
52145     onRender : function(ct, position){
52146         //console.log('row render');
52147         if(!this.rowTpl){
52148             var t = new Roo.Template(
52149                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52150                     '<label for="{0}" style="{2}">{1}{4}</label>',
52151                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52152                     '</div>',
52153                 '</div>'
52154             );
52155             t.disableFormats = true;
52156             t.compile();
52157             Roo.form.Layout.prototype.rowTpl = t;
52158         }
52159         this.fieldTpl = this.rowTpl;
52160         
52161         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52162         var labelWidth = 100;
52163         
52164         if ((this.labelAlign != 'top')) {
52165             if (typeof this.labelWidth == 'number') {
52166                 labelWidth = this.labelWidth
52167             }
52168             this.padWidth =  20 + labelWidth;
52169             
52170         }
52171         
52172         Roo.form.Column.superclass.onRender.call(this, ct, position);
52173         if(this.width){
52174             this.el.setWidth(this.width);
52175         }
52176         if(this.height){
52177             this.el.setHeight(this.height);
52178         }
52179     },
52180     
52181     // private
52182     renderField : function(f){
52183         f.fieldEl = this.fieldTpl.append(this.el, [
52184                f.id, f.fieldLabel,
52185                f.labelStyle||this.labelStyle||'',
52186                this.elementStyle||'',
52187                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52188                f.itemCls||this.itemCls||'',
52189                f.width ? f.width + this.padWidth : 160 + this.padWidth
52190        ],true);
52191     }
52192 });
52193  
52194
52195 /**
52196  * @class Roo.form.FieldSet
52197  * @extends Roo.form.Layout
52198  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52199  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52200  * @constructor
52201  * @param {Object} config Configuration options
52202  */
52203 Roo.form.FieldSet = function(config){
52204     Roo.form.FieldSet.superclass.constructor.call(this, config);
52205 };
52206
52207 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52208     /**
52209      * @cfg {String} legend
52210      * The text to display as the legend for the FieldSet (defaults to '')
52211      */
52212     /**
52213      * @cfg {String/Object} autoCreate
52214      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52215      */
52216
52217     // private
52218     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52219
52220     // private
52221     onRender : function(ct, position){
52222         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52223         if(this.legend){
52224             this.setLegend(this.legend);
52225         }
52226     },
52227
52228     // private
52229     setLegend : function(text){
52230         if(this.rendered){
52231             this.el.child('legend').update(text);
52232         }
52233     }
52234 });/*
52235  * Based on:
52236  * Ext JS Library 1.1.1
52237  * Copyright(c) 2006-2007, Ext JS, LLC.
52238  *
52239  * Originally Released Under LGPL - original licence link has changed is not relivant.
52240  *
52241  * Fork - LGPL
52242  * <script type="text/javascript">
52243  */
52244 /**
52245  * @class Roo.form.VTypes
52246  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52247  * @static
52248  */
52249 Roo.form.VTypes = function(){
52250     // closure these in so they are only created once.
52251     var alpha = /^[a-zA-Z_]+$/;
52252     var alphanum = /^[a-zA-Z0-9_]+$/;
52253     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52254     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52255
52256     // All these messages and functions are configurable
52257     return {
52258         /**
52259          * The function used to validate email addresses
52260          * @param {String} value The email address
52261          */
52262         'email' : function(v){
52263             return email.test(v);
52264         },
52265         /**
52266          * The error text to display when the email validation function returns false
52267          * @type String
52268          */
52269         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52270         /**
52271          * The keystroke filter mask to be applied on email input
52272          * @type RegExp
52273          */
52274         'emailMask' : /[a-z0-9_\.\-@]/i,
52275
52276         /**
52277          * The function used to validate URLs
52278          * @param {String} value The URL
52279          */
52280         'url' : function(v){
52281             return url.test(v);
52282         },
52283         /**
52284          * The error text to display when the url validation function returns false
52285          * @type String
52286          */
52287         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52288         
52289         /**
52290          * The function used to validate alpha values
52291          * @param {String} value The value
52292          */
52293         'alpha' : function(v){
52294             return alpha.test(v);
52295         },
52296         /**
52297          * The error text to display when the alpha validation function returns false
52298          * @type String
52299          */
52300         'alphaText' : 'This field should only contain letters and _',
52301         /**
52302          * The keystroke filter mask to be applied on alpha input
52303          * @type RegExp
52304          */
52305         'alphaMask' : /[a-z_]/i,
52306
52307         /**
52308          * The function used to validate alphanumeric values
52309          * @param {String} value The value
52310          */
52311         'alphanum' : function(v){
52312             return alphanum.test(v);
52313         },
52314         /**
52315          * The error text to display when the alphanumeric validation function returns false
52316          * @type String
52317          */
52318         'alphanumText' : 'This field should only contain letters, numbers and _',
52319         /**
52320          * The keystroke filter mask to be applied on alphanumeric input
52321          * @type RegExp
52322          */
52323         'alphanumMask' : /[a-z0-9_]/i
52324     };
52325 }();//<script type="text/javascript">
52326
52327 /**
52328  * @class Roo.form.FCKeditor
52329  * @extends Roo.form.TextArea
52330  * Wrapper around the FCKEditor http://www.fckeditor.net
52331  * @constructor
52332  * Creates a new FCKeditor
52333  * @param {Object} config Configuration options
52334  */
52335 Roo.form.FCKeditor = function(config){
52336     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52337     this.addEvents({
52338          /**
52339          * @event editorinit
52340          * Fired when the editor is initialized - you can add extra handlers here..
52341          * @param {FCKeditor} this
52342          * @param {Object} the FCK object.
52343          */
52344         editorinit : true
52345     });
52346     
52347     
52348 };
52349 Roo.form.FCKeditor.editors = { };
52350 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52351 {
52352     //defaultAutoCreate : {
52353     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52354     //},
52355     // private
52356     /**
52357      * @cfg {Object} fck options - see fck manual for details.
52358      */
52359     fckconfig : false,
52360     
52361     /**
52362      * @cfg {Object} fck toolbar set (Basic or Default)
52363      */
52364     toolbarSet : 'Basic',
52365     /**
52366      * @cfg {Object} fck BasePath
52367      */ 
52368     basePath : '/fckeditor/',
52369     
52370     
52371     frame : false,
52372     
52373     value : '',
52374     
52375    
52376     onRender : function(ct, position)
52377     {
52378         if(!this.el){
52379             this.defaultAutoCreate = {
52380                 tag: "textarea",
52381                 style:"width:300px;height:60px;",
52382                 autocomplete: "new-password"
52383             };
52384         }
52385         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52386         /*
52387         if(this.grow){
52388             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52389             if(this.preventScrollbars){
52390                 this.el.setStyle("overflow", "hidden");
52391             }
52392             this.el.setHeight(this.growMin);
52393         }
52394         */
52395         //console.log('onrender' + this.getId() );
52396         Roo.form.FCKeditor.editors[this.getId()] = this;
52397          
52398
52399         this.replaceTextarea() ;
52400         
52401     },
52402     
52403     getEditor : function() {
52404         return this.fckEditor;
52405     },
52406     /**
52407      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52408      * @param {Mixed} value The value to set
52409      */
52410     
52411     
52412     setValue : function(value)
52413     {
52414         //console.log('setValue: ' + value);
52415         
52416         if(typeof(value) == 'undefined') { // not sure why this is happending...
52417             return;
52418         }
52419         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52420         
52421         //if(!this.el || !this.getEditor()) {
52422         //    this.value = value;
52423             //this.setValue.defer(100,this,[value]);    
52424         //    return;
52425         //} 
52426         
52427         if(!this.getEditor()) {
52428             return;
52429         }
52430         
52431         this.getEditor().SetData(value);
52432         
52433         //
52434
52435     },
52436
52437     /**
52438      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52439      * @return {Mixed} value The field value
52440      */
52441     getValue : function()
52442     {
52443         
52444         if (this.frame && this.frame.dom.style.display == 'none') {
52445             return Roo.form.FCKeditor.superclass.getValue.call(this);
52446         }
52447         
52448         if(!this.el || !this.getEditor()) {
52449            
52450            // this.getValue.defer(100,this); 
52451             return this.value;
52452         }
52453        
52454         
52455         var value=this.getEditor().GetData();
52456         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52457         return Roo.form.FCKeditor.superclass.getValue.call(this);
52458         
52459
52460     },
52461
52462     /**
52463      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52464      * @return {Mixed} value The field value
52465      */
52466     getRawValue : function()
52467     {
52468         if (this.frame && this.frame.dom.style.display == 'none') {
52469             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52470         }
52471         
52472         if(!this.el || !this.getEditor()) {
52473             //this.getRawValue.defer(100,this); 
52474             return this.value;
52475             return;
52476         }
52477         
52478         
52479         
52480         var value=this.getEditor().GetData();
52481         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52482         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52483          
52484     },
52485     
52486     setSize : function(w,h) {
52487         
52488         
52489         
52490         //if (this.frame && this.frame.dom.style.display == 'none') {
52491         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52492         //    return;
52493         //}
52494         //if(!this.el || !this.getEditor()) {
52495         //    this.setSize.defer(100,this, [w,h]); 
52496         //    return;
52497         //}
52498         
52499         
52500         
52501         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52502         
52503         this.frame.dom.setAttribute('width', w);
52504         this.frame.dom.setAttribute('height', h);
52505         this.frame.setSize(w,h);
52506         
52507     },
52508     
52509     toggleSourceEdit : function(value) {
52510         
52511       
52512          
52513         this.el.dom.style.display = value ? '' : 'none';
52514         this.frame.dom.style.display = value ?  'none' : '';
52515         
52516     },
52517     
52518     
52519     focus: function(tag)
52520     {
52521         if (this.frame.dom.style.display == 'none') {
52522             return Roo.form.FCKeditor.superclass.focus.call(this);
52523         }
52524         if(!this.el || !this.getEditor()) {
52525             this.focus.defer(100,this, [tag]); 
52526             return;
52527         }
52528         
52529         
52530         
52531         
52532         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52533         this.getEditor().Focus();
52534         if (tgs.length) {
52535             if (!this.getEditor().Selection.GetSelection()) {
52536                 this.focus.defer(100,this, [tag]); 
52537                 return;
52538             }
52539             
52540             
52541             var r = this.getEditor().EditorDocument.createRange();
52542             r.setStart(tgs[0],0);
52543             r.setEnd(tgs[0],0);
52544             this.getEditor().Selection.GetSelection().removeAllRanges();
52545             this.getEditor().Selection.GetSelection().addRange(r);
52546             this.getEditor().Focus();
52547         }
52548         
52549     },
52550     
52551     
52552     
52553     replaceTextarea : function()
52554     {
52555         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52556             return ;
52557         }
52558         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52559         //{
52560             // We must check the elements firstly using the Id and then the name.
52561         var oTextarea = document.getElementById( this.getId() );
52562         
52563         var colElementsByName = document.getElementsByName( this.getId() ) ;
52564          
52565         oTextarea.style.display = 'none' ;
52566
52567         if ( oTextarea.tabIndex ) {            
52568             this.TabIndex = oTextarea.tabIndex ;
52569         }
52570         
52571         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52572         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52573         this.frame = Roo.get(this.getId() + '___Frame')
52574     },
52575     
52576     _getConfigHtml : function()
52577     {
52578         var sConfig = '' ;
52579
52580         for ( var o in this.fckconfig ) {
52581             sConfig += sConfig.length > 0  ? '&amp;' : '';
52582             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52583         }
52584
52585         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52586     },
52587     
52588     
52589     _getIFrameHtml : function()
52590     {
52591         var sFile = 'fckeditor.html' ;
52592         /* no idea what this is about..
52593         try
52594         {
52595             if ( (/fcksource=true/i).test( window.top.location.search ) )
52596                 sFile = 'fckeditor.original.html' ;
52597         }
52598         catch (e) { 
52599         */
52600
52601         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52602         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52603         
52604         
52605         var html = '<iframe id="' + this.getId() +
52606             '___Frame" src="' + sLink +
52607             '" width="' + this.width +
52608             '" height="' + this.height + '"' +
52609             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52610             ' frameborder="0" scrolling="no"></iframe>' ;
52611
52612         return html ;
52613     },
52614     
52615     _insertHtmlBefore : function( html, element )
52616     {
52617         if ( element.insertAdjacentHTML )       {
52618             // IE
52619             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52620         } else { // Gecko
52621             var oRange = document.createRange() ;
52622             oRange.setStartBefore( element ) ;
52623             var oFragment = oRange.createContextualFragment( html );
52624             element.parentNode.insertBefore( oFragment, element ) ;
52625         }
52626     }
52627     
52628     
52629   
52630     
52631     
52632     
52633     
52634
52635 });
52636
52637 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52638
52639 function FCKeditor_OnComplete(editorInstance){
52640     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52641     f.fckEditor = editorInstance;
52642     //console.log("loaded");
52643     f.fireEvent('editorinit', f, editorInstance);
52644
52645   
52646
52647  
52648
52649
52650
52651
52652
52653
52654
52655
52656
52657
52658
52659
52660
52661
52662
52663 //<script type="text/javascript">
52664 /**
52665  * @class Roo.form.GridField
52666  * @extends Roo.form.Field
52667  * Embed a grid (or editable grid into a form)
52668  * STATUS ALPHA
52669  * 
52670  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52671  * it needs 
52672  * xgrid.store = Roo.data.Store
52673  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52674  * xgrid.store.reader = Roo.data.JsonReader 
52675  * 
52676  * 
52677  * @constructor
52678  * Creates a new GridField
52679  * @param {Object} config Configuration options
52680  */
52681 Roo.form.GridField = function(config){
52682     Roo.form.GridField.superclass.constructor.call(this, config);
52683      
52684 };
52685
52686 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52687     /**
52688      * @cfg {Number} width  - used to restrict width of grid..
52689      */
52690     width : 100,
52691     /**
52692      * @cfg {Number} height - used to restrict height of grid..
52693      */
52694     height : 50,
52695      /**
52696      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52697          * 
52698          *}
52699      */
52700     xgrid : false, 
52701     /**
52702      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52703      * {tag: "input", type: "checkbox", autocomplete: "off"})
52704      */
52705    // defaultAutoCreate : { tag: 'div' },
52706     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52707     /**
52708      * @cfg {String} addTitle Text to include for adding a title.
52709      */
52710     addTitle : false,
52711     //
52712     onResize : function(){
52713         Roo.form.Field.superclass.onResize.apply(this, arguments);
52714     },
52715
52716     initEvents : function(){
52717         // Roo.form.Checkbox.superclass.initEvents.call(this);
52718         // has no events...
52719        
52720     },
52721
52722
52723     getResizeEl : function(){
52724         return this.wrap;
52725     },
52726
52727     getPositionEl : function(){
52728         return this.wrap;
52729     },
52730
52731     // private
52732     onRender : function(ct, position){
52733         
52734         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52735         var style = this.style;
52736         delete this.style;
52737         
52738         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52739         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52740         this.viewEl = this.wrap.createChild({ tag: 'div' });
52741         if (style) {
52742             this.viewEl.applyStyles(style);
52743         }
52744         if (this.width) {
52745             this.viewEl.setWidth(this.width);
52746         }
52747         if (this.height) {
52748             this.viewEl.setHeight(this.height);
52749         }
52750         //if(this.inputValue !== undefined){
52751         //this.setValue(this.value);
52752         
52753         
52754         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52755         
52756         
52757         this.grid.render();
52758         this.grid.getDataSource().on('remove', this.refreshValue, this);
52759         this.grid.getDataSource().on('update', this.refreshValue, this);
52760         this.grid.on('afteredit', this.refreshValue, this);
52761  
52762     },
52763      
52764     
52765     /**
52766      * Sets the value of the item. 
52767      * @param {String} either an object  or a string..
52768      */
52769     setValue : function(v){
52770         //this.value = v;
52771         v = v || []; // empty set..
52772         // this does not seem smart - it really only affects memoryproxy grids..
52773         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52774             var ds = this.grid.getDataSource();
52775             // assumes a json reader..
52776             var data = {}
52777             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52778             ds.loadData( data);
52779         }
52780         // clear selection so it does not get stale.
52781         if (this.grid.sm) { 
52782             this.grid.sm.clearSelections();
52783         }
52784         
52785         Roo.form.GridField.superclass.setValue.call(this, v);
52786         this.refreshValue();
52787         // should load data in the grid really....
52788     },
52789     
52790     // private
52791     refreshValue: function() {
52792          var val = [];
52793         this.grid.getDataSource().each(function(r) {
52794             val.push(r.data);
52795         });
52796         this.el.dom.value = Roo.encode(val);
52797     }
52798     
52799      
52800     
52801     
52802 });/*
52803  * Based on:
52804  * Ext JS Library 1.1.1
52805  * Copyright(c) 2006-2007, Ext JS, LLC.
52806  *
52807  * Originally Released Under LGPL - original licence link has changed is not relivant.
52808  *
52809  * Fork - LGPL
52810  * <script type="text/javascript">
52811  */
52812 /**
52813  * @class Roo.form.DisplayField
52814  * @extends Roo.form.Field
52815  * A generic Field to display non-editable data.
52816  * @cfg {Boolean} closable (true|false) default false
52817  * @constructor
52818  * Creates a new Display Field item.
52819  * @param {Object} config Configuration options
52820  */
52821 Roo.form.DisplayField = function(config){
52822     Roo.form.DisplayField.superclass.constructor.call(this, config);
52823     
52824     this.addEvents({
52825         /**
52826          * @event close
52827          * Fires after the click the close btn
52828              * @param {Roo.form.DisplayField} this
52829              */
52830         close : true
52831     });
52832 };
52833
52834 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52835     inputType:      'hidden',
52836     allowBlank:     true,
52837     readOnly:         true,
52838     
52839  
52840     /**
52841      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52842      */
52843     focusClass : undefined,
52844     /**
52845      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52846      */
52847     fieldClass: 'x-form-field',
52848     
52849      /**
52850      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52851      */
52852     valueRenderer: undefined,
52853     
52854     width: 100,
52855     /**
52856      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52857      * {tag: "input", type: "checkbox", autocomplete: "off"})
52858      */
52859      
52860  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52861  
52862     closable : false,
52863     
52864     onResize : function(){
52865         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52866         
52867     },
52868
52869     initEvents : function(){
52870         // Roo.form.Checkbox.superclass.initEvents.call(this);
52871         // has no events...
52872         
52873         if(this.closable){
52874             this.closeEl.on('click', this.onClose, this);
52875         }
52876        
52877     },
52878
52879
52880     getResizeEl : function(){
52881         return this.wrap;
52882     },
52883
52884     getPositionEl : function(){
52885         return this.wrap;
52886     },
52887
52888     // private
52889     onRender : function(ct, position){
52890         
52891         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52892         //if(this.inputValue !== undefined){
52893         this.wrap = this.el.wrap();
52894         
52895         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52896         
52897         if(this.closable){
52898             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52899         }
52900         
52901         if (this.bodyStyle) {
52902             this.viewEl.applyStyles(this.bodyStyle);
52903         }
52904         //this.viewEl.setStyle('padding', '2px');
52905         
52906         this.setValue(this.value);
52907         
52908     },
52909 /*
52910     // private
52911     initValue : Roo.emptyFn,
52912
52913   */
52914
52915         // private
52916     onClick : function(){
52917         
52918     },
52919
52920     /**
52921      * Sets the checked state of the checkbox.
52922      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52923      */
52924     setValue : function(v){
52925         this.value = v;
52926         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52927         // this might be called before we have a dom element..
52928         if (!this.viewEl) {
52929             return;
52930         }
52931         this.viewEl.dom.innerHTML = html;
52932         Roo.form.DisplayField.superclass.setValue.call(this, v);
52933
52934     },
52935     
52936     onClose : function(e)
52937     {
52938         e.preventDefault();
52939         
52940         this.fireEvent('close', this);
52941     }
52942 });/*
52943  * 
52944  * Licence- LGPL
52945  * 
52946  */
52947
52948 /**
52949  * @class Roo.form.DayPicker
52950  * @extends Roo.form.Field
52951  * A Day picker show [M] [T] [W] ....
52952  * @constructor
52953  * Creates a new Day Picker
52954  * @param {Object} config Configuration options
52955  */
52956 Roo.form.DayPicker= function(config){
52957     Roo.form.DayPicker.superclass.constructor.call(this, config);
52958      
52959 };
52960
52961 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52962     /**
52963      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52964      */
52965     focusClass : undefined,
52966     /**
52967      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52968      */
52969     fieldClass: "x-form-field",
52970    
52971     /**
52972      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52973      * {tag: "input", type: "checkbox", autocomplete: "off"})
52974      */
52975     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52976     
52977    
52978     actionMode : 'viewEl', 
52979     //
52980     // private
52981  
52982     inputType : 'hidden',
52983     
52984      
52985     inputElement: false, // real input element?
52986     basedOn: false, // ????
52987     
52988     isFormField: true, // not sure where this is needed!!!!
52989
52990     onResize : function(){
52991         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52992         if(!this.boxLabel){
52993             this.el.alignTo(this.wrap, 'c-c');
52994         }
52995     },
52996
52997     initEvents : function(){
52998         Roo.form.Checkbox.superclass.initEvents.call(this);
52999         this.el.on("click", this.onClick,  this);
53000         this.el.on("change", this.onClick,  this);
53001     },
53002
53003
53004     getResizeEl : function(){
53005         return this.wrap;
53006     },
53007
53008     getPositionEl : function(){
53009         return this.wrap;
53010     },
53011
53012     
53013     // private
53014     onRender : function(ct, position){
53015         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53016        
53017         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53018         
53019         var r1 = '<table><tr>';
53020         var r2 = '<tr class="x-form-daypick-icons">';
53021         for (var i=0; i < 7; i++) {
53022             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53023             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53024         }
53025         
53026         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53027         viewEl.select('img').on('click', this.onClick, this);
53028         this.viewEl = viewEl;   
53029         
53030         
53031         // this will not work on Chrome!!!
53032         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53033         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53034         
53035         
53036           
53037
53038     },
53039
53040     // private
53041     initValue : Roo.emptyFn,
53042
53043     /**
53044      * Returns the checked state of the checkbox.
53045      * @return {Boolean} True if checked, else false
53046      */
53047     getValue : function(){
53048         return this.el.dom.value;
53049         
53050     },
53051
53052         // private
53053     onClick : function(e){ 
53054         //this.setChecked(!this.checked);
53055         Roo.get(e.target).toggleClass('x-menu-item-checked');
53056         this.refreshValue();
53057         //if(this.el.dom.checked != this.checked){
53058         //    this.setValue(this.el.dom.checked);
53059        // }
53060     },
53061     
53062     // private
53063     refreshValue : function()
53064     {
53065         var val = '';
53066         this.viewEl.select('img',true).each(function(e,i,n)  {
53067             val += e.is(".x-menu-item-checked") ? String(n) : '';
53068         });
53069         this.setValue(val, true);
53070     },
53071
53072     /**
53073      * Sets the checked state of the checkbox.
53074      * On is always based on a string comparison between inputValue and the param.
53075      * @param {Boolean/String} value - the value to set 
53076      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53077      */
53078     setValue : function(v,suppressEvent){
53079         if (!this.el.dom) {
53080             return;
53081         }
53082         var old = this.el.dom.value ;
53083         this.el.dom.value = v;
53084         if (suppressEvent) {
53085             return ;
53086         }
53087          
53088         // update display..
53089         this.viewEl.select('img',true).each(function(e,i,n)  {
53090             
53091             var on = e.is(".x-menu-item-checked");
53092             var newv = v.indexOf(String(n)) > -1;
53093             if (on != newv) {
53094                 e.toggleClass('x-menu-item-checked');
53095             }
53096             
53097         });
53098         
53099         
53100         this.fireEvent('change', this, v, old);
53101         
53102         
53103     },
53104    
53105     // handle setting of hidden value by some other method!!?!?
53106     setFromHidden: function()
53107     {
53108         if(!this.el){
53109             return;
53110         }
53111         //console.log("SET FROM HIDDEN");
53112         //alert('setFrom hidden');
53113         this.setValue(this.el.dom.value);
53114     },
53115     
53116     onDestroy : function()
53117     {
53118         if(this.viewEl){
53119             Roo.get(this.viewEl).remove();
53120         }
53121          
53122         Roo.form.DayPicker.superclass.onDestroy.call(this);
53123     }
53124
53125 });/*
53126  * RooJS Library 1.1.1
53127  * Copyright(c) 2008-2011  Alan Knowles
53128  *
53129  * License - LGPL
53130  */
53131  
53132
53133 /**
53134  * @class Roo.form.ComboCheck
53135  * @extends Roo.form.ComboBox
53136  * A combobox for multiple select items.
53137  *
53138  * FIXME - could do with a reset button..
53139  * 
53140  * @constructor
53141  * Create a new ComboCheck
53142  * @param {Object} config Configuration options
53143  */
53144 Roo.form.ComboCheck = function(config){
53145     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53146     // should verify some data...
53147     // like
53148     // hiddenName = required..
53149     // displayField = required
53150     // valudField == required
53151     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53152     var _t = this;
53153     Roo.each(req, function(e) {
53154         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53155             throw "Roo.form.ComboCheck : missing value for: " + e;
53156         }
53157     });
53158     
53159     
53160 };
53161
53162 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53163      
53164      
53165     editable : false,
53166      
53167     selectedClass: 'x-menu-item-checked', 
53168     
53169     // private
53170     onRender : function(ct, position){
53171         var _t = this;
53172         
53173         
53174         
53175         if(!this.tpl){
53176             var cls = 'x-combo-list';
53177
53178             
53179             this.tpl =  new Roo.Template({
53180                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53181                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53182                    '<span>{' + this.displayField + '}</span>' +
53183                     '</div>' 
53184                 
53185             });
53186         }
53187  
53188         
53189         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53190         this.view.singleSelect = false;
53191         this.view.multiSelect = true;
53192         this.view.toggleSelect = true;
53193         this.pageTb.add(new Roo.Toolbar.Fill(), {
53194             
53195             text: 'Done',
53196             handler: function()
53197             {
53198                 _t.collapse();
53199             }
53200         });
53201     },
53202     
53203     onViewOver : function(e, t){
53204         // do nothing...
53205         return;
53206         
53207     },
53208     
53209     onViewClick : function(doFocus,index){
53210         return;
53211         
53212     },
53213     select: function () {
53214         //Roo.log("SELECT CALLED");
53215     },
53216      
53217     selectByValue : function(xv, scrollIntoView){
53218         var ar = this.getValueArray();
53219         var sels = [];
53220         
53221         Roo.each(ar, function(v) {
53222             if(v === undefined || v === null){
53223                 return;
53224             }
53225             var r = this.findRecord(this.valueField, v);
53226             if(r){
53227                 sels.push(this.store.indexOf(r))
53228                 
53229             }
53230         },this);
53231         this.view.select(sels);
53232         return false;
53233     },
53234     
53235     
53236     
53237     onSelect : function(record, index){
53238        // Roo.log("onselect Called");
53239        // this is only called by the clear button now..
53240         this.view.clearSelections();
53241         this.setValue('[]');
53242         if (this.value != this.valueBefore) {
53243             this.fireEvent('change', this, this.value, this.valueBefore);
53244             this.valueBefore = this.value;
53245         }
53246     },
53247     getValueArray : function()
53248     {
53249         var ar = [] ;
53250         
53251         try {
53252             //Roo.log(this.value);
53253             if (typeof(this.value) == 'undefined') {
53254                 return [];
53255             }
53256             var ar = Roo.decode(this.value);
53257             return  ar instanceof Array ? ar : []; //?? valid?
53258             
53259         } catch(e) {
53260             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53261             return [];
53262         }
53263          
53264     },
53265     expand : function ()
53266     {
53267         
53268         Roo.form.ComboCheck.superclass.expand.call(this);
53269         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53270         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53271         
53272
53273     },
53274     
53275     collapse : function(){
53276         Roo.form.ComboCheck.superclass.collapse.call(this);
53277         var sl = this.view.getSelectedIndexes();
53278         var st = this.store;
53279         var nv = [];
53280         var tv = [];
53281         var r;
53282         Roo.each(sl, function(i) {
53283             r = st.getAt(i);
53284             nv.push(r.get(this.valueField));
53285         },this);
53286         this.setValue(Roo.encode(nv));
53287         if (this.value != this.valueBefore) {
53288
53289             this.fireEvent('change', this, this.value, this.valueBefore);
53290             this.valueBefore = this.value;
53291         }
53292         
53293     },
53294     
53295     setValue : function(v){
53296         // Roo.log(v);
53297         this.value = v;
53298         
53299         var vals = this.getValueArray();
53300         var tv = [];
53301         Roo.each(vals, function(k) {
53302             var r = this.findRecord(this.valueField, k);
53303             if(r){
53304                 tv.push(r.data[this.displayField]);
53305             }else if(this.valueNotFoundText !== undefined){
53306                 tv.push( this.valueNotFoundText );
53307             }
53308         },this);
53309        // Roo.log(tv);
53310         
53311         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53312         this.hiddenField.value = v;
53313         this.value = v;
53314     }
53315     
53316 });/*
53317  * Based on:
53318  * Ext JS Library 1.1.1
53319  * Copyright(c) 2006-2007, Ext JS, LLC.
53320  *
53321  * Originally Released Under LGPL - original licence link has changed is not relivant.
53322  *
53323  * Fork - LGPL
53324  * <script type="text/javascript">
53325  */
53326  
53327 /**
53328  * @class Roo.form.Signature
53329  * @extends Roo.form.Field
53330  * Signature field.  
53331  * @constructor
53332  * 
53333  * @param {Object} config Configuration options
53334  */
53335
53336 Roo.form.Signature = function(config){
53337     Roo.form.Signature.superclass.constructor.call(this, config);
53338     
53339     this.addEvents({// not in used??
53340          /**
53341          * @event confirm
53342          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53343              * @param {Roo.form.Signature} combo This combo box
53344              */
53345         'confirm' : true,
53346         /**
53347          * @event reset
53348          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53349              * @param {Roo.form.ComboBox} combo This combo box
53350              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53351              */
53352         'reset' : true
53353     });
53354 };
53355
53356 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53357     /**
53358      * @cfg {Object} labels Label to use when rendering a form.
53359      * defaults to 
53360      * labels : { 
53361      *      clear : "Clear",
53362      *      confirm : "Confirm"
53363      *  }
53364      */
53365     labels : { 
53366         clear : "Clear",
53367         confirm : "Confirm"
53368     },
53369     /**
53370      * @cfg {Number} width The signature panel width (defaults to 300)
53371      */
53372     width: 300,
53373     /**
53374      * @cfg {Number} height The signature panel height (defaults to 100)
53375      */
53376     height : 100,
53377     /**
53378      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53379      */
53380     allowBlank : false,
53381     
53382     //private
53383     // {Object} signPanel The signature SVG panel element (defaults to {})
53384     signPanel : {},
53385     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53386     isMouseDown : false,
53387     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53388     isConfirmed : false,
53389     // {String} signatureTmp SVG mapping string (defaults to empty string)
53390     signatureTmp : '',
53391     
53392     
53393     defaultAutoCreate : { // modified by initCompnoent..
53394         tag: "input",
53395         type:"hidden"
53396     },
53397
53398     // private
53399     onRender : function(ct, position){
53400         
53401         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53402         
53403         this.wrap = this.el.wrap({
53404             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53405         });
53406         
53407         this.createToolbar(this);
53408         this.signPanel = this.wrap.createChild({
53409                 tag: 'div',
53410                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53411             }, this.el
53412         );
53413             
53414         this.svgID = Roo.id();
53415         this.svgEl = this.signPanel.createChild({
53416               xmlns : 'http://www.w3.org/2000/svg',
53417               tag : 'svg',
53418               id : this.svgID + "-svg",
53419               width: this.width,
53420               height: this.height,
53421               viewBox: '0 0 '+this.width+' '+this.height,
53422               cn : [
53423                 {
53424                     tag: "rect",
53425                     id: this.svgID + "-svg-r",
53426                     width: this.width,
53427                     height: this.height,
53428                     fill: "#ffa"
53429                 },
53430                 {
53431                     tag: "line",
53432                     id: this.svgID + "-svg-l",
53433                     x1: "0", // start
53434                     y1: (this.height*0.8), // start set the line in 80% of height
53435                     x2: this.width, // end
53436                     y2: (this.height*0.8), // end set the line in 80% of height
53437                     'stroke': "#666",
53438                     'stroke-width': "1",
53439                     'stroke-dasharray': "3",
53440                     'shape-rendering': "crispEdges",
53441                     'pointer-events': "none"
53442                 },
53443                 {
53444                     tag: "path",
53445                     id: this.svgID + "-svg-p",
53446                     'stroke': "navy",
53447                     'stroke-width': "3",
53448                     'fill': "none",
53449                     'pointer-events': 'none'
53450                 }
53451               ]
53452         });
53453         this.createSVG();
53454         this.svgBox = this.svgEl.dom.getScreenCTM();
53455     },
53456     createSVG : function(){ 
53457         var svg = this.signPanel;
53458         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53459         var t = this;
53460
53461         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53462         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53463         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53464         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53465         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53466         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53467         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53468         
53469     },
53470     isTouchEvent : function(e){
53471         return e.type.match(/^touch/);
53472     },
53473     getCoords : function (e) {
53474         var pt    = this.svgEl.dom.createSVGPoint();
53475         pt.x = e.clientX; 
53476         pt.y = e.clientY;
53477         if (this.isTouchEvent(e)) {
53478             pt.x =  e.targetTouches[0].clientX;
53479             pt.y = e.targetTouches[0].clientY;
53480         }
53481         var a = this.svgEl.dom.getScreenCTM();
53482         var b = a.inverse();
53483         var mx = pt.matrixTransform(b);
53484         return mx.x + ',' + mx.y;
53485     },
53486     //mouse event headler 
53487     down : function (e) {
53488         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53489         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53490         
53491         this.isMouseDown = true;
53492         
53493         e.preventDefault();
53494     },
53495     move : function (e) {
53496         if (this.isMouseDown) {
53497             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53498             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53499         }
53500         
53501         e.preventDefault();
53502     },
53503     up : function (e) {
53504         this.isMouseDown = false;
53505         var sp = this.signatureTmp.split(' ');
53506         
53507         if(sp.length > 1){
53508             if(!sp[sp.length-2].match(/^L/)){
53509                 sp.pop();
53510                 sp.pop();
53511                 sp.push("");
53512                 this.signatureTmp = sp.join(" ");
53513             }
53514         }
53515         if(this.getValue() != this.signatureTmp){
53516             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53517             this.isConfirmed = false;
53518         }
53519         e.preventDefault();
53520     },
53521     
53522     /**
53523      * Protected method that will not generally be called directly. It
53524      * is called when the editor creates its toolbar. Override this method if you need to
53525      * add custom toolbar buttons.
53526      * @param {HtmlEditor} editor
53527      */
53528     createToolbar : function(editor){
53529          function btn(id, toggle, handler){
53530             var xid = fid + '-'+ id ;
53531             return {
53532                 id : xid,
53533                 cmd : id,
53534                 cls : 'x-btn-icon x-edit-'+id,
53535                 enableToggle:toggle !== false,
53536                 scope: editor, // was editor...
53537                 handler:handler||editor.relayBtnCmd,
53538                 clickEvent:'mousedown',
53539                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53540                 tabIndex:-1
53541             };
53542         }
53543         
53544         
53545         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53546         this.tb = tb;
53547         this.tb.add(
53548            {
53549                 cls : ' x-signature-btn x-signature-'+id,
53550                 scope: editor, // was editor...
53551                 handler: this.reset,
53552                 clickEvent:'mousedown',
53553                 text: this.labels.clear
53554             },
53555             {
53556                  xtype : 'Fill',
53557                  xns: Roo.Toolbar
53558             }, 
53559             {
53560                 cls : '  x-signature-btn x-signature-'+id,
53561                 scope: editor, // was editor...
53562                 handler: this.confirmHandler,
53563                 clickEvent:'mousedown',
53564                 text: this.labels.confirm
53565             }
53566         );
53567     
53568     },
53569     //public
53570     /**
53571      * when user is clicked confirm then show this image.....
53572      * 
53573      * @return {String} Image Data URI
53574      */
53575     getImageDataURI : function(){
53576         var svg = this.svgEl.dom.parentNode.innerHTML;
53577         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53578         return src; 
53579     },
53580     /**
53581      * 
53582      * @return {Boolean} this.isConfirmed
53583      */
53584     getConfirmed : function(){
53585         return this.isConfirmed;
53586     },
53587     /**
53588      * 
53589      * @return {Number} this.width
53590      */
53591     getWidth : function(){
53592         return this.width;
53593     },
53594     /**
53595      * 
53596      * @return {Number} this.height
53597      */
53598     getHeight : function(){
53599         return this.height;
53600     },
53601     // private
53602     getSignature : function(){
53603         return this.signatureTmp;
53604     },
53605     // private
53606     reset : function(){
53607         this.signatureTmp = '';
53608         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53609         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53610         this.isConfirmed = false;
53611         Roo.form.Signature.superclass.reset.call(this);
53612     },
53613     setSignature : function(s){
53614         this.signatureTmp = s;
53615         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53616         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53617         this.setValue(s);
53618         this.isConfirmed = false;
53619         Roo.form.Signature.superclass.reset.call(this);
53620     }, 
53621     test : function(){
53622 //        Roo.log(this.signPanel.dom.contentWindow.up())
53623     },
53624     //private
53625     setConfirmed : function(){
53626         
53627         
53628         
53629 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53630     },
53631     // private
53632     confirmHandler : function(){
53633         if(!this.getSignature()){
53634             return;
53635         }
53636         
53637         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53638         this.setValue(this.getSignature());
53639         this.isConfirmed = true;
53640         
53641         this.fireEvent('confirm', this);
53642     },
53643     // private
53644     // Subclasses should provide the validation implementation by overriding this
53645     validateValue : function(value){
53646         if(this.allowBlank){
53647             return true;
53648         }
53649         
53650         if(this.isConfirmed){
53651             return true;
53652         }
53653         return false;
53654     }
53655 });/*
53656  * Based on:
53657  * Ext JS Library 1.1.1
53658  * Copyright(c) 2006-2007, Ext JS, LLC.
53659  *
53660  * Originally Released Under LGPL - original licence link has changed is not relivant.
53661  *
53662  * Fork - LGPL
53663  * <script type="text/javascript">
53664  */
53665  
53666
53667 /**
53668  * @class Roo.form.ComboBox
53669  * @extends Roo.form.TriggerField
53670  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53671  * @constructor
53672  * Create a new ComboBox.
53673  * @param {Object} config Configuration options
53674  */
53675 Roo.form.Select = function(config){
53676     Roo.form.Select.superclass.constructor.call(this, config);
53677      
53678 };
53679
53680 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53681     /**
53682      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53683      */
53684     /**
53685      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53686      * rendering into an Roo.Editor, defaults to false)
53687      */
53688     /**
53689      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53690      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53691      */
53692     /**
53693      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53694      */
53695     /**
53696      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53697      * the dropdown list (defaults to undefined, with no header element)
53698      */
53699
53700      /**
53701      * @cfg {String/Roo.Template} tpl The template to use to render the output
53702      */
53703      
53704     // private
53705     defaultAutoCreate : {tag: "select"  },
53706     /**
53707      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53708      */
53709     listWidth: undefined,
53710     /**
53711      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53712      * mode = 'remote' or 'text' if mode = 'local')
53713      */
53714     displayField: undefined,
53715     /**
53716      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53717      * mode = 'remote' or 'value' if mode = 'local'). 
53718      * Note: use of a valueField requires the user make a selection
53719      * in order for a value to be mapped.
53720      */
53721     valueField: undefined,
53722     
53723     
53724     /**
53725      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53726      * field's data value (defaults to the underlying DOM element's name)
53727      */
53728     hiddenName: undefined,
53729     /**
53730      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53731      */
53732     listClass: '',
53733     /**
53734      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53735      */
53736     selectedClass: 'x-combo-selected',
53737     /**
53738      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53739      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53740      * which displays a downward arrow icon).
53741      */
53742     triggerClass : 'x-form-arrow-trigger',
53743     /**
53744      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53745      */
53746     shadow:'sides',
53747     /**
53748      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53749      * anchor positions (defaults to 'tl-bl')
53750      */
53751     listAlign: 'tl-bl?',
53752     /**
53753      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53754      */
53755     maxHeight: 300,
53756     /**
53757      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53758      * query specified by the allQuery config option (defaults to 'query')
53759      */
53760     triggerAction: 'query',
53761     /**
53762      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53763      * (defaults to 4, does not apply if editable = false)
53764      */
53765     minChars : 4,
53766     /**
53767      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53768      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53769      */
53770     typeAhead: false,
53771     /**
53772      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53773      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53774      */
53775     queryDelay: 500,
53776     /**
53777      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53778      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53779      */
53780     pageSize: 0,
53781     /**
53782      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53783      * when editable = true (defaults to false)
53784      */
53785     selectOnFocus:false,
53786     /**
53787      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53788      */
53789     queryParam: 'query',
53790     /**
53791      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53792      * when mode = 'remote' (defaults to 'Loading...')
53793      */
53794     loadingText: 'Loading...',
53795     /**
53796      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53797      */
53798     resizable: false,
53799     /**
53800      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53801      */
53802     handleHeight : 8,
53803     /**
53804      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53805      * traditional select (defaults to true)
53806      */
53807     editable: true,
53808     /**
53809      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53810      */
53811     allQuery: '',
53812     /**
53813      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53814      */
53815     mode: 'remote',
53816     /**
53817      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53818      * listWidth has a higher value)
53819      */
53820     minListWidth : 70,
53821     /**
53822      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53823      * allow the user to set arbitrary text into the field (defaults to false)
53824      */
53825     forceSelection:false,
53826     /**
53827      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53828      * if typeAhead = true (defaults to 250)
53829      */
53830     typeAheadDelay : 250,
53831     /**
53832      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53833      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53834      */
53835     valueNotFoundText : undefined,
53836     
53837     /**
53838      * @cfg {String} defaultValue The value displayed after loading the store.
53839      */
53840     defaultValue: '',
53841     
53842     /**
53843      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53844      */
53845     blockFocus : false,
53846     
53847     /**
53848      * @cfg {Boolean} disableClear Disable showing of clear button.
53849      */
53850     disableClear : false,
53851     /**
53852      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53853      */
53854     alwaysQuery : false,
53855     
53856     //private
53857     addicon : false,
53858     editicon: false,
53859     
53860     // element that contains real text value.. (when hidden is used..)
53861      
53862     // private
53863     onRender : function(ct, position){
53864         Roo.form.Field.prototype.onRender.call(this, ct, position);
53865         
53866         if(this.store){
53867             this.store.on('beforeload', this.onBeforeLoad, this);
53868             this.store.on('load', this.onLoad, this);
53869             this.store.on('loadexception', this.onLoadException, this);
53870             this.store.load({});
53871         }
53872         
53873         
53874         
53875     },
53876
53877     // private
53878     initEvents : function(){
53879         //Roo.form.ComboBox.superclass.initEvents.call(this);
53880  
53881     },
53882
53883     onDestroy : function(){
53884        
53885         if(this.store){
53886             this.store.un('beforeload', this.onBeforeLoad, this);
53887             this.store.un('load', this.onLoad, this);
53888             this.store.un('loadexception', this.onLoadException, this);
53889         }
53890         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53891     },
53892
53893     // private
53894     fireKey : function(e){
53895         if(e.isNavKeyPress() && !this.list.isVisible()){
53896             this.fireEvent("specialkey", this, e);
53897         }
53898     },
53899
53900     // private
53901     onResize: function(w, h){
53902         
53903         return; 
53904     
53905         
53906     },
53907
53908     /**
53909      * Allow or prevent the user from directly editing the field text.  If false is passed,
53910      * the user will only be able to select from the items defined in the dropdown list.  This method
53911      * is the runtime equivalent of setting the 'editable' config option at config time.
53912      * @param {Boolean} value True to allow the user to directly edit the field text
53913      */
53914     setEditable : function(value){
53915          
53916     },
53917
53918     // private
53919     onBeforeLoad : function(){
53920         
53921         Roo.log("Select before load");
53922         return;
53923     
53924         this.innerList.update(this.loadingText ?
53925                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53926         //this.restrictHeight();
53927         this.selectedIndex = -1;
53928     },
53929
53930     // private
53931     onLoad : function(){
53932
53933     
53934         var dom = this.el.dom;
53935         dom.innerHTML = '';
53936          var od = dom.ownerDocument;
53937          
53938         if (this.emptyText) {
53939             var op = od.createElement('option');
53940             op.setAttribute('value', '');
53941             op.innerHTML = String.format('{0}', this.emptyText);
53942             dom.appendChild(op);
53943         }
53944         if(this.store.getCount() > 0){
53945            
53946             var vf = this.valueField;
53947             var df = this.displayField;
53948             this.store.data.each(function(r) {
53949                 // which colmsn to use... testing - cdoe / title..
53950                 var op = od.createElement('option');
53951                 op.setAttribute('value', r.data[vf]);
53952                 op.innerHTML = String.format('{0}', r.data[df]);
53953                 dom.appendChild(op);
53954             });
53955             if (typeof(this.defaultValue != 'undefined')) {
53956                 this.setValue(this.defaultValue);
53957             }
53958             
53959              
53960         }else{
53961             //this.onEmptyResults();
53962         }
53963         //this.el.focus();
53964     },
53965     // private
53966     onLoadException : function()
53967     {
53968         dom.innerHTML = '';
53969             
53970         Roo.log("Select on load exception");
53971         return;
53972     
53973         this.collapse();
53974         Roo.log(this.store.reader.jsonData);
53975         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53976             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53977         }
53978         
53979         
53980     },
53981     // private
53982     onTypeAhead : function(){
53983          
53984     },
53985
53986     // private
53987     onSelect : function(record, index){
53988         Roo.log('on select?');
53989         return;
53990         if(this.fireEvent('beforeselect', this, record, index) !== false){
53991             this.setFromData(index > -1 ? record.data : false);
53992             this.collapse();
53993             this.fireEvent('select', this, record, index);
53994         }
53995     },
53996
53997     /**
53998      * Returns the currently selected field value or empty string if no value is set.
53999      * @return {String} value The selected value
54000      */
54001     getValue : function(){
54002         var dom = this.el.dom;
54003         this.value = dom.options[dom.selectedIndex].value;
54004         return this.value;
54005         
54006     },
54007
54008     /**
54009      * Clears any text/value currently set in the field
54010      */
54011     clearValue : function(){
54012         this.value = '';
54013         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54014         
54015     },
54016
54017     /**
54018      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54019      * will be displayed in the field.  If the value does not match the data value of an existing item,
54020      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54021      * Otherwise the field will be blank (although the value will still be set).
54022      * @param {String} value The value to match
54023      */
54024     setValue : function(v){
54025         var d = this.el.dom;
54026         for (var i =0; i < d.options.length;i++) {
54027             if (v == d.options[i].value) {
54028                 d.selectedIndex = i;
54029                 this.value = v;
54030                 return;
54031             }
54032         }
54033         this.clearValue();
54034     },
54035     /**
54036      * @property {Object} the last set data for the element
54037      */
54038     
54039     lastData : false,
54040     /**
54041      * Sets the value of the field based on a object which is related to the record format for the store.
54042      * @param {Object} value the value to set as. or false on reset?
54043      */
54044     setFromData : function(o){
54045         Roo.log('setfrom data?');
54046          
54047         
54048         
54049     },
54050     // private
54051     reset : function(){
54052         this.clearValue();
54053     },
54054     // private
54055     findRecord : function(prop, value){
54056         
54057         return false;
54058     
54059         var record;
54060         if(this.store.getCount() > 0){
54061             this.store.each(function(r){
54062                 if(r.data[prop] == value){
54063                     record = r;
54064                     return false;
54065                 }
54066                 return true;
54067             });
54068         }
54069         return record;
54070     },
54071     
54072     getName: function()
54073     {
54074         // returns hidden if it's set..
54075         if (!this.rendered) {return ''};
54076         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54077         
54078     },
54079      
54080
54081     
54082
54083     // private
54084     onEmptyResults : function(){
54085         Roo.log('empty results');
54086         //this.collapse();
54087     },
54088
54089     /**
54090      * Returns true if the dropdown list is expanded, else false.
54091      */
54092     isExpanded : function(){
54093         return false;
54094     },
54095
54096     /**
54097      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54098      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54099      * @param {String} value The data value of the item to select
54100      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54101      * selected item if it is not currently in view (defaults to true)
54102      * @return {Boolean} True if the value matched an item in the list, else false
54103      */
54104     selectByValue : function(v, scrollIntoView){
54105         Roo.log('select By Value');
54106         return false;
54107     
54108         if(v !== undefined && v !== null){
54109             var r = this.findRecord(this.valueField || this.displayField, v);
54110             if(r){
54111                 this.select(this.store.indexOf(r), scrollIntoView);
54112                 return true;
54113             }
54114         }
54115         return false;
54116     },
54117
54118     /**
54119      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54120      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54121      * @param {Number} index The zero-based index of the list item to select
54122      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54123      * selected item if it is not currently in view (defaults to true)
54124      */
54125     select : function(index, scrollIntoView){
54126         Roo.log('select ');
54127         return  ;
54128         
54129         this.selectedIndex = index;
54130         this.view.select(index);
54131         if(scrollIntoView !== false){
54132             var el = this.view.getNode(index);
54133             if(el){
54134                 this.innerList.scrollChildIntoView(el, false);
54135             }
54136         }
54137     },
54138
54139       
54140
54141     // private
54142     validateBlur : function(){
54143         
54144         return;
54145         
54146     },
54147
54148     // private
54149     initQuery : function(){
54150         this.doQuery(this.getRawValue());
54151     },
54152
54153     // private
54154     doForce : function(){
54155         if(this.el.dom.value.length > 0){
54156             this.el.dom.value =
54157                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54158              
54159         }
54160     },
54161
54162     /**
54163      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54164      * query allowing the query action to be canceled if needed.
54165      * @param {String} query The SQL query to execute
54166      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54167      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54168      * saved in the current store (defaults to false)
54169      */
54170     doQuery : function(q, forceAll){
54171         
54172         Roo.log('doQuery?');
54173         if(q === undefined || q === null){
54174             q = '';
54175         }
54176         var qe = {
54177             query: q,
54178             forceAll: forceAll,
54179             combo: this,
54180             cancel:false
54181         };
54182         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54183             return false;
54184         }
54185         q = qe.query;
54186         forceAll = qe.forceAll;
54187         if(forceAll === true || (q.length >= this.minChars)){
54188             if(this.lastQuery != q || this.alwaysQuery){
54189                 this.lastQuery = q;
54190                 if(this.mode == 'local'){
54191                     this.selectedIndex = -1;
54192                     if(forceAll){
54193                         this.store.clearFilter();
54194                     }else{
54195                         this.store.filter(this.displayField, q);
54196                     }
54197                     this.onLoad();
54198                 }else{
54199                     this.store.baseParams[this.queryParam] = q;
54200                     this.store.load({
54201                         params: this.getParams(q)
54202                     });
54203                     this.expand();
54204                 }
54205             }else{
54206                 this.selectedIndex = -1;
54207                 this.onLoad();   
54208             }
54209         }
54210     },
54211
54212     // private
54213     getParams : function(q){
54214         var p = {};
54215         //p[this.queryParam] = q;
54216         if(this.pageSize){
54217             p.start = 0;
54218             p.limit = this.pageSize;
54219         }
54220         return p;
54221     },
54222
54223     /**
54224      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54225      */
54226     collapse : function(){
54227         
54228     },
54229
54230     // private
54231     collapseIf : function(e){
54232         
54233     },
54234
54235     /**
54236      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54237      */
54238     expand : function(){
54239         
54240     } ,
54241
54242     // private
54243      
54244
54245     /** 
54246     * @cfg {Boolean} grow 
54247     * @hide 
54248     */
54249     /** 
54250     * @cfg {Number} growMin 
54251     * @hide 
54252     */
54253     /** 
54254     * @cfg {Number} growMax 
54255     * @hide 
54256     */
54257     /**
54258      * @hide
54259      * @method autoSize
54260      */
54261     
54262     setWidth : function()
54263     {
54264         
54265     },
54266     getResizeEl : function(){
54267         return this.el;
54268     }
54269 });//<script type="text/javasscript">
54270  
54271
54272 /**
54273  * @class Roo.DDView
54274  * A DnD enabled version of Roo.View.
54275  * @param {Element/String} container The Element in which to create the View.
54276  * @param {String} tpl The template string used to create the markup for each element of the View
54277  * @param {Object} config The configuration properties. These include all the config options of
54278  * {@link Roo.View} plus some specific to this class.<br>
54279  * <p>
54280  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54281  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54282  * <p>
54283  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54284 .x-view-drag-insert-above {
54285         border-top:1px dotted #3366cc;
54286 }
54287 .x-view-drag-insert-below {
54288         border-bottom:1px dotted #3366cc;
54289 }
54290 </code></pre>
54291  * 
54292  */
54293  
54294 Roo.DDView = function(container, tpl, config) {
54295     Roo.DDView.superclass.constructor.apply(this, arguments);
54296     this.getEl().setStyle("outline", "0px none");
54297     this.getEl().unselectable();
54298     if (this.dragGroup) {
54299         this.setDraggable(this.dragGroup.split(","));
54300     }
54301     if (this.dropGroup) {
54302         this.setDroppable(this.dropGroup.split(","));
54303     }
54304     if (this.deletable) {
54305         this.setDeletable();
54306     }
54307     this.isDirtyFlag = false;
54308         this.addEvents({
54309                 "drop" : true
54310         });
54311 };
54312
54313 Roo.extend(Roo.DDView, Roo.View, {
54314 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54315 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54316 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54317 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54318
54319         isFormField: true,
54320
54321         reset: Roo.emptyFn,
54322         
54323         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54324
54325         validate: function() {
54326                 return true;
54327         },
54328         
54329         destroy: function() {
54330                 this.purgeListeners();
54331                 this.getEl.removeAllListeners();
54332                 this.getEl().remove();
54333                 if (this.dragZone) {
54334                         if (this.dragZone.destroy) {
54335                                 this.dragZone.destroy();
54336                         }
54337                 }
54338                 if (this.dropZone) {
54339                         if (this.dropZone.destroy) {
54340                                 this.dropZone.destroy();
54341                         }
54342                 }
54343         },
54344
54345 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54346         getName: function() {
54347                 return this.name;
54348         },
54349
54350 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54351         setValue: function(v) {
54352                 if (!this.store) {
54353                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54354                 }
54355                 var data = {};
54356                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54357                 this.store.proxy = new Roo.data.MemoryProxy(data);
54358                 this.store.load();
54359         },
54360
54361 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54362         getValue: function() {
54363                 var result = '(';
54364                 this.store.each(function(rec) {
54365                         result += rec.id + ',';
54366                 });
54367                 return result.substr(0, result.length - 1) + ')';
54368         },
54369         
54370         getIds: function() {
54371                 var i = 0, result = new Array(this.store.getCount());
54372                 this.store.each(function(rec) {
54373                         result[i++] = rec.id;
54374                 });
54375                 return result;
54376         },
54377         
54378         isDirty: function() {
54379                 return this.isDirtyFlag;
54380         },
54381
54382 /**
54383  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54384  *      whole Element becomes the target, and this causes the drop gesture to append.
54385  */
54386     getTargetFromEvent : function(e) {
54387                 var target = e.getTarget();
54388                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54389                 target = target.parentNode;
54390                 }
54391                 if (!target) {
54392                         target = this.el.dom.lastChild || this.el.dom;
54393                 }
54394                 return target;
54395     },
54396
54397 /**
54398  *      Create the drag data which consists of an object which has the property "ddel" as
54399  *      the drag proxy element. 
54400  */
54401     getDragData : function(e) {
54402         var target = this.findItemFromChild(e.getTarget());
54403                 if(target) {
54404                         this.handleSelection(e);
54405                         var selNodes = this.getSelectedNodes();
54406             var dragData = {
54407                 source: this,
54408                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54409                 nodes: selNodes,
54410                 records: []
54411                         };
54412                         var selectedIndices = this.getSelectedIndexes();
54413                         for (var i = 0; i < selectedIndices.length; i++) {
54414                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54415                         }
54416                         if (selNodes.length == 1) {
54417                                 dragData.ddel = target.cloneNode(true); // the div element
54418                         } else {
54419                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54420                                 div.className = 'multi-proxy';
54421                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54422                                         div.appendChild(selNodes[i].cloneNode(true));
54423                                 }
54424                                 dragData.ddel = div;
54425                         }
54426             //console.log(dragData)
54427             //console.log(dragData.ddel.innerHTML)
54428                         return dragData;
54429                 }
54430         //console.log('nodragData')
54431                 return false;
54432     },
54433     
54434 /**     Specify to which ddGroup items in this DDView may be dragged. */
54435     setDraggable: function(ddGroup) {
54436         if (ddGroup instanceof Array) {
54437                 Roo.each(ddGroup, this.setDraggable, this);
54438                 return;
54439         }
54440         if (this.dragZone) {
54441                 this.dragZone.addToGroup(ddGroup);
54442         } else {
54443                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54444                                 containerScroll: true,
54445                                 ddGroup: ddGroup 
54446
54447                         });
54448 //                      Draggability implies selection. DragZone's mousedown selects the element.
54449                         if (!this.multiSelect) { this.singleSelect = true; }
54450
54451 //                      Wire the DragZone's handlers up to methods in *this*
54452                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54453                 }
54454     },
54455
54456 /**     Specify from which ddGroup this DDView accepts drops. */
54457     setDroppable: function(ddGroup) {
54458         if (ddGroup instanceof Array) {
54459                 Roo.each(ddGroup, this.setDroppable, this);
54460                 return;
54461         }
54462         if (this.dropZone) {
54463                 this.dropZone.addToGroup(ddGroup);
54464         } else {
54465                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54466                                 containerScroll: true,
54467                                 ddGroup: ddGroup
54468                         });
54469
54470 //                      Wire the DropZone's handlers up to methods in *this*
54471                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54472                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54473                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54474                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54475                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54476                 }
54477     },
54478
54479 /**     Decide whether to drop above or below a View node. */
54480     getDropPoint : function(e, n, dd){
54481         if (n == this.el.dom) { return "above"; }
54482                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54483                 var c = t + (b - t) / 2;
54484                 var y = Roo.lib.Event.getPageY(e);
54485                 if(y <= c) {
54486                         return "above";
54487                 }else{
54488                         return "below";
54489                 }
54490     },
54491
54492     onNodeEnter : function(n, dd, e, data){
54493                 return false;
54494     },
54495     
54496     onNodeOver : function(n, dd, e, data){
54497                 var pt = this.getDropPoint(e, n, dd);
54498                 // set the insert point style on the target node
54499                 var dragElClass = this.dropNotAllowed;
54500                 if (pt) {
54501                         var targetElClass;
54502                         if (pt == "above"){
54503                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54504                                 targetElClass = "x-view-drag-insert-above";
54505                         } else {
54506                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54507                                 targetElClass = "x-view-drag-insert-below";
54508                         }
54509                         if (this.lastInsertClass != targetElClass){
54510                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54511                                 this.lastInsertClass = targetElClass;
54512                         }
54513                 }
54514                 return dragElClass;
54515         },
54516
54517     onNodeOut : function(n, dd, e, data){
54518                 this.removeDropIndicators(n);
54519     },
54520
54521     onNodeDrop : function(n, dd, e, data){
54522         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54523                 return false;
54524         }
54525         var pt = this.getDropPoint(e, n, dd);
54526                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54527                 if (pt == "below") { insertAt++; }
54528                 for (var i = 0; i < data.records.length; i++) {
54529                         var r = data.records[i];
54530                         var dup = this.store.getById(r.id);
54531                         if (dup && (dd != this.dragZone)) {
54532                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54533                         } else {
54534                                 if (data.copy) {
54535                                         this.store.insert(insertAt++, r.copy());
54536                                 } else {
54537                                         data.source.isDirtyFlag = true;
54538                                         r.store.remove(r);
54539                                         this.store.insert(insertAt++, r);
54540                                 }
54541                                 this.isDirtyFlag = true;
54542                         }
54543                 }
54544                 this.dragZone.cachedTarget = null;
54545                 return true;
54546     },
54547
54548     removeDropIndicators : function(n){
54549                 if(n){
54550                         Roo.fly(n).removeClass([
54551                                 "x-view-drag-insert-above",
54552                                 "x-view-drag-insert-below"]);
54553                         this.lastInsertClass = "_noclass";
54554                 }
54555     },
54556
54557 /**
54558  *      Utility method. Add a delete option to the DDView's context menu.
54559  *      @param {String} imageUrl The URL of the "delete" icon image.
54560  */
54561         setDeletable: function(imageUrl) {
54562                 if (!this.singleSelect && !this.multiSelect) {
54563                         this.singleSelect = true;
54564                 }
54565                 var c = this.getContextMenu();
54566                 this.contextMenu.on("itemclick", function(item) {
54567                         switch (item.id) {
54568                                 case "delete":
54569                                         this.remove(this.getSelectedIndexes());
54570                                         break;
54571                         }
54572                 }, this);
54573                 this.contextMenu.add({
54574                         icon: imageUrl,
54575                         id: "delete",
54576                         text: 'Delete'
54577                 });
54578         },
54579         
54580 /**     Return the context menu for this DDView. */
54581         getContextMenu: function() {
54582                 if (!this.contextMenu) {
54583 //                      Create the View's context menu
54584                         this.contextMenu = new Roo.menu.Menu({
54585                                 id: this.id + "-contextmenu"
54586                         });
54587                         this.el.on("contextmenu", this.showContextMenu, this);
54588                 }
54589                 return this.contextMenu;
54590         },
54591         
54592         disableContextMenu: function() {
54593                 if (this.contextMenu) {
54594                         this.el.un("contextmenu", this.showContextMenu, this);
54595                 }
54596         },
54597
54598         showContextMenu: function(e, item) {
54599         item = this.findItemFromChild(e.getTarget());
54600                 if (item) {
54601                         e.stopEvent();
54602                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54603                         this.contextMenu.showAt(e.getXY());
54604             }
54605     },
54606
54607 /**
54608  *      Remove {@link Roo.data.Record}s at the specified indices.
54609  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54610  */
54611     remove: function(selectedIndices) {
54612                 selectedIndices = [].concat(selectedIndices);
54613                 for (var i = 0; i < selectedIndices.length; i++) {
54614                         var rec = this.store.getAt(selectedIndices[i]);
54615                         this.store.remove(rec);
54616                 }
54617     },
54618
54619 /**
54620  *      Double click fires the event, but also, if this is draggable, and there is only one other
54621  *      related DropZone, it transfers the selected node.
54622  */
54623     onDblClick : function(e){
54624         var item = this.findItemFromChild(e.getTarget());
54625         if(item){
54626             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54627                 return false;
54628             }
54629             if (this.dragGroup) {
54630                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54631                     while (targets.indexOf(this.dropZone) > -1) {
54632                             targets.remove(this.dropZone);
54633                                 }
54634                     if (targets.length == 1) {
54635                                         this.dragZone.cachedTarget = null;
54636                         var el = Roo.get(targets[0].getEl());
54637                         var box = el.getBox(true);
54638                         targets[0].onNodeDrop(el.dom, {
54639                                 target: el.dom,
54640                                 xy: [box.x, box.y + box.height - 1]
54641                         }, null, this.getDragData(e));
54642                     }
54643                 }
54644         }
54645     },
54646     
54647     handleSelection: function(e) {
54648                 this.dragZone.cachedTarget = null;
54649         var item = this.findItemFromChild(e.getTarget());
54650         if (!item) {
54651                 this.clearSelections(true);
54652                 return;
54653         }
54654                 if (item && (this.multiSelect || this.singleSelect)){
54655                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54656                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54657                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54658                                 this.unselect(item);
54659                         } else {
54660                                 this.select(item, this.multiSelect && e.ctrlKey);
54661                                 this.lastSelection = item;
54662                         }
54663                 }
54664     },
54665
54666     onItemClick : function(item, index, e){
54667                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54668                         return false;
54669                 }
54670                 return true;
54671     },
54672
54673     unselect : function(nodeInfo, suppressEvent){
54674                 var node = this.getNode(nodeInfo);
54675                 if(node && this.isSelected(node)){
54676                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54677                                 Roo.fly(node).removeClass(this.selectedClass);
54678                                 this.selections.remove(node);
54679                                 if(!suppressEvent){
54680                                         this.fireEvent("selectionchange", this, this.selections);
54681                                 }
54682                         }
54683                 }
54684     }
54685 });
54686 /*
54687  * Based on:
54688  * Ext JS Library 1.1.1
54689  * Copyright(c) 2006-2007, Ext JS, LLC.
54690  *
54691  * Originally Released Under LGPL - original licence link has changed is not relivant.
54692  *
54693  * Fork - LGPL
54694  * <script type="text/javascript">
54695  */
54696  
54697 /**
54698  * @class Roo.LayoutManager
54699  * @extends Roo.util.Observable
54700  * Base class for layout managers.
54701  */
54702 Roo.LayoutManager = function(container, config){
54703     Roo.LayoutManager.superclass.constructor.call(this);
54704     this.el = Roo.get(container);
54705     // ie scrollbar fix
54706     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54707         document.body.scroll = "no";
54708     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54709         this.el.position('relative');
54710     }
54711     this.id = this.el.id;
54712     this.el.addClass("x-layout-container");
54713     /** false to disable window resize monitoring @type Boolean */
54714     this.monitorWindowResize = true;
54715     this.regions = {};
54716     this.addEvents({
54717         /**
54718          * @event layout
54719          * Fires when a layout is performed. 
54720          * @param {Roo.LayoutManager} this
54721          */
54722         "layout" : true,
54723         /**
54724          * @event regionresized
54725          * Fires when the user resizes a region. 
54726          * @param {Roo.LayoutRegion} region The resized region
54727          * @param {Number} newSize The new size (width for east/west, height for north/south)
54728          */
54729         "regionresized" : true,
54730         /**
54731          * @event regioncollapsed
54732          * Fires when a region is collapsed. 
54733          * @param {Roo.LayoutRegion} region The collapsed region
54734          */
54735         "regioncollapsed" : true,
54736         /**
54737          * @event regionexpanded
54738          * Fires when a region is expanded.  
54739          * @param {Roo.LayoutRegion} region The expanded region
54740          */
54741         "regionexpanded" : true
54742     });
54743     this.updating = false;
54744     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54745 };
54746
54747 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54748     /**
54749      * Returns true if this layout is currently being updated
54750      * @return {Boolean}
54751      */
54752     isUpdating : function(){
54753         return this.updating; 
54754     },
54755     
54756     /**
54757      * Suspend the LayoutManager from doing auto-layouts while
54758      * making multiple add or remove calls
54759      */
54760     beginUpdate : function(){
54761         this.updating = true;    
54762     },
54763     
54764     /**
54765      * Restore auto-layouts and optionally disable the manager from performing a layout
54766      * @param {Boolean} noLayout true to disable a layout update 
54767      */
54768     endUpdate : function(noLayout){
54769         this.updating = false;
54770         if(!noLayout){
54771             this.layout();
54772         }    
54773     },
54774     
54775     layout: function(){
54776         
54777     },
54778     
54779     onRegionResized : function(region, newSize){
54780         this.fireEvent("regionresized", region, newSize);
54781         this.layout();
54782     },
54783     
54784     onRegionCollapsed : function(region){
54785         this.fireEvent("regioncollapsed", region);
54786     },
54787     
54788     onRegionExpanded : function(region){
54789         this.fireEvent("regionexpanded", region);
54790     },
54791         
54792     /**
54793      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54794      * performs box-model adjustments.
54795      * @return {Object} The size as an object {width: (the width), height: (the height)}
54796      */
54797     getViewSize : function(){
54798         var size;
54799         if(this.el.dom != document.body){
54800             size = this.el.getSize();
54801         }else{
54802             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54803         }
54804         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54805         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54806         return size;
54807     },
54808     
54809     /**
54810      * Returns the Element this layout is bound to.
54811      * @return {Roo.Element}
54812      */
54813     getEl : function(){
54814         return this.el;
54815     },
54816     
54817     /**
54818      * Returns the specified region.
54819      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54820      * @return {Roo.LayoutRegion}
54821      */
54822     getRegion : function(target){
54823         return this.regions[target.toLowerCase()];
54824     },
54825     
54826     onWindowResize : function(){
54827         if(this.monitorWindowResize){
54828             this.layout();
54829         }
54830     }
54831 });/*
54832  * Based on:
54833  * Ext JS Library 1.1.1
54834  * Copyright(c) 2006-2007, Ext JS, LLC.
54835  *
54836  * Originally Released Under LGPL - original licence link has changed is not relivant.
54837  *
54838  * Fork - LGPL
54839  * <script type="text/javascript">
54840  */
54841 /**
54842  * @class Roo.BorderLayout
54843  * @extends Roo.LayoutManager
54844  * @children Roo.ContentPanel
54845  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54846  * please see: <br><br>
54847  * <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>
54848  * <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>
54849  * Example:
54850  <pre><code>
54851  var layout = new Roo.BorderLayout(document.body, {
54852     north: {
54853         initialSize: 25,
54854         titlebar: false
54855     },
54856     west: {
54857         split:true,
54858         initialSize: 200,
54859         minSize: 175,
54860         maxSize: 400,
54861         titlebar: true,
54862         collapsible: true
54863     },
54864     east: {
54865         split:true,
54866         initialSize: 202,
54867         minSize: 175,
54868         maxSize: 400,
54869         titlebar: true,
54870         collapsible: true
54871     },
54872     south: {
54873         split:true,
54874         initialSize: 100,
54875         minSize: 100,
54876         maxSize: 200,
54877         titlebar: true,
54878         collapsible: true
54879     },
54880     center: {
54881         titlebar: true,
54882         autoScroll:true,
54883         resizeTabs: true,
54884         minTabWidth: 50,
54885         preferredTabWidth: 150
54886     }
54887 });
54888
54889 // shorthand
54890 var CP = Roo.ContentPanel;
54891
54892 layout.beginUpdate();
54893 layout.add("north", new CP("north", "North"));
54894 layout.add("south", new CP("south", {title: "South", closable: true}));
54895 layout.add("west", new CP("west", {title: "West"}));
54896 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54897 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54898 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54899 layout.getRegion("center").showPanel("center1");
54900 layout.endUpdate();
54901 </code></pre>
54902
54903 <b>The container the layout is rendered into can be either the body element or any other element.
54904 If it is not the body element, the container needs to either be an absolute positioned element,
54905 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54906 the container size if it is not the body element.</b>
54907
54908 * @constructor
54909 * Create a new BorderLayout
54910 * @param {String/HTMLElement/Element} container The container this layout is bound to
54911 * @param {Object} config Configuration options
54912  */
54913 Roo.BorderLayout = function(container, config){
54914     config = config || {};
54915     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54916     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54917     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54918         var target = this.factory.validRegions[i];
54919         if(config[target]){
54920             this.addRegion(target, config[target]);
54921         }
54922     }
54923 };
54924
54925 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54926         
54927         /**
54928          * @cfg {Roo.LayoutRegion} east
54929          */
54930         /**
54931          * @cfg {Roo.LayoutRegion} west
54932          */
54933         /**
54934          * @cfg {Roo.LayoutRegion} north
54935          */
54936         /**
54937          * @cfg {Roo.LayoutRegion} south
54938          */
54939         /**
54940          * @cfg {Roo.LayoutRegion} center
54941          */
54942     /**
54943      * Creates and adds a new region if it doesn't already exist.
54944      * @param {String} target The target region key (north, south, east, west or center).
54945      * @param {Object} config The regions config object
54946      * @return {BorderLayoutRegion} The new region
54947      */
54948     addRegion : function(target, config){
54949         if(!this.regions[target]){
54950             var r = this.factory.create(target, this, config);
54951             this.bindRegion(target, r);
54952         }
54953         return this.regions[target];
54954     },
54955
54956     // private (kinda)
54957     bindRegion : function(name, r){
54958         this.regions[name] = r;
54959         r.on("visibilitychange", this.layout, this);
54960         r.on("paneladded", this.layout, this);
54961         r.on("panelremoved", this.layout, this);
54962         r.on("invalidated", this.layout, this);
54963         r.on("resized", this.onRegionResized, this);
54964         r.on("collapsed", this.onRegionCollapsed, this);
54965         r.on("expanded", this.onRegionExpanded, this);
54966     },
54967
54968     /**
54969      * Performs a layout update.
54970      */
54971     layout : function(){
54972         if(this.updating) {
54973             return;
54974         }
54975         var size = this.getViewSize();
54976         var w = size.width;
54977         var h = size.height;
54978         var centerW = w;
54979         var centerH = h;
54980         var centerY = 0;
54981         var centerX = 0;
54982         //var x = 0, y = 0;
54983
54984         var rs = this.regions;
54985         var north = rs["north"];
54986         var south = rs["south"]; 
54987         var west = rs["west"];
54988         var east = rs["east"];
54989         var center = rs["center"];
54990         //if(this.hideOnLayout){ // not supported anymore
54991             //c.el.setStyle("display", "none");
54992         //}
54993         if(north && north.isVisible()){
54994             var b = north.getBox();
54995             var m = north.getMargins();
54996             b.width = w - (m.left+m.right);
54997             b.x = m.left;
54998             b.y = m.top;
54999             centerY = b.height + b.y + m.bottom;
55000             centerH -= centerY;
55001             north.updateBox(this.safeBox(b));
55002         }
55003         if(south && south.isVisible()){
55004             var b = south.getBox();
55005             var m = south.getMargins();
55006             b.width = w - (m.left+m.right);
55007             b.x = m.left;
55008             var totalHeight = (b.height + m.top + m.bottom);
55009             b.y = h - totalHeight + m.top;
55010             centerH -= totalHeight;
55011             south.updateBox(this.safeBox(b));
55012         }
55013         if(west && west.isVisible()){
55014             var b = west.getBox();
55015             var m = west.getMargins();
55016             b.height = centerH - (m.top+m.bottom);
55017             b.x = m.left;
55018             b.y = centerY + m.top;
55019             var totalWidth = (b.width + m.left + m.right);
55020             centerX += totalWidth;
55021             centerW -= totalWidth;
55022             west.updateBox(this.safeBox(b));
55023         }
55024         if(east && east.isVisible()){
55025             var b = east.getBox();
55026             var m = east.getMargins();
55027             b.height = centerH - (m.top+m.bottom);
55028             var totalWidth = (b.width + m.left + m.right);
55029             b.x = w - totalWidth + m.left;
55030             b.y = centerY + m.top;
55031             centerW -= totalWidth;
55032             east.updateBox(this.safeBox(b));
55033         }
55034         if(center){
55035             var m = center.getMargins();
55036             var centerBox = {
55037                 x: centerX + m.left,
55038                 y: centerY + m.top,
55039                 width: centerW - (m.left+m.right),
55040                 height: centerH - (m.top+m.bottom)
55041             };
55042             //if(this.hideOnLayout){
55043                 //center.el.setStyle("display", "block");
55044             //}
55045             center.updateBox(this.safeBox(centerBox));
55046         }
55047         this.el.repaint();
55048         this.fireEvent("layout", this);
55049     },
55050
55051     // private
55052     safeBox : function(box){
55053         box.width = Math.max(0, box.width);
55054         box.height = Math.max(0, box.height);
55055         return box;
55056     },
55057
55058     /**
55059      * Adds a ContentPanel (or subclass) to this layout.
55060      * @param {String} target The target region key (north, south, east, west or center).
55061      * @param {Roo.ContentPanel} panel The panel to add
55062      * @return {Roo.ContentPanel} The added panel
55063      */
55064     add : function(target, panel){
55065          
55066         target = target.toLowerCase();
55067         return this.regions[target].add(panel);
55068     },
55069
55070     /**
55071      * Remove a ContentPanel (or subclass) to this layout.
55072      * @param {String} target The target region key (north, south, east, west or center).
55073      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55074      * @return {Roo.ContentPanel} The removed panel
55075      */
55076     remove : function(target, panel){
55077         target = target.toLowerCase();
55078         return this.regions[target].remove(panel);
55079     },
55080
55081     /**
55082      * Searches all regions for a panel with the specified id
55083      * @param {String} panelId
55084      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55085      */
55086     findPanel : function(panelId){
55087         var rs = this.regions;
55088         for(var target in rs){
55089             if(typeof rs[target] != "function"){
55090                 var p = rs[target].getPanel(panelId);
55091                 if(p){
55092                     return p;
55093                 }
55094             }
55095         }
55096         return null;
55097     },
55098
55099     /**
55100      * Searches all regions for a panel with the specified id and activates (shows) it.
55101      * @param {String/ContentPanel} panelId The panels id or the panel itself
55102      * @return {Roo.ContentPanel} The shown panel or null
55103      */
55104     showPanel : function(panelId) {
55105       var rs = this.regions;
55106       for(var target in rs){
55107          var r = rs[target];
55108          if(typeof r != "function"){
55109             if(r.hasPanel(panelId)){
55110                return r.showPanel(panelId);
55111             }
55112          }
55113       }
55114       return null;
55115    },
55116
55117    /**
55118      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55119      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55120      */
55121     restoreState : function(provider){
55122         if(!provider){
55123             provider = Roo.state.Manager;
55124         }
55125         var sm = new Roo.LayoutStateManager();
55126         sm.init(this, provider);
55127     },
55128
55129     /**
55130      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55131      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55132      * a valid ContentPanel config object.  Example:
55133      * <pre><code>
55134 // Create the main layout
55135 var layout = new Roo.BorderLayout('main-ct', {
55136     west: {
55137         split:true,
55138         minSize: 175,
55139         titlebar: true
55140     },
55141     center: {
55142         title:'Components'
55143     }
55144 }, 'main-ct');
55145
55146 // Create and add multiple ContentPanels at once via configs
55147 layout.batchAdd({
55148    west: {
55149        id: 'source-files',
55150        autoCreate:true,
55151        title:'Ext Source Files',
55152        autoScroll:true,
55153        fitToFrame:true
55154    },
55155    center : {
55156        el: cview,
55157        autoScroll:true,
55158        fitToFrame:true,
55159        toolbar: tb,
55160        resizeEl:'cbody'
55161    }
55162 });
55163 </code></pre>
55164      * @param {Object} regions An object containing ContentPanel configs by region name
55165      */
55166     batchAdd : function(regions){
55167         this.beginUpdate();
55168         for(var rname in regions){
55169             var lr = this.regions[rname];
55170             if(lr){
55171                 this.addTypedPanels(lr, regions[rname]);
55172             }
55173         }
55174         this.endUpdate();
55175     },
55176
55177     // private
55178     addTypedPanels : function(lr, ps){
55179         if(typeof ps == 'string'){
55180             lr.add(new Roo.ContentPanel(ps));
55181         }
55182         else if(ps instanceof Array){
55183             for(var i =0, len = ps.length; i < len; i++){
55184                 this.addTypedPanels(lr, ps[i]);
55185             }
55186         }
55187         else if(!ps.events){ // raw config?
55188             var el = ps.el;
55189             delete ps.el; // prevent conflict
55190             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55191         }
55192         else {  // panel object assumed!
55193             lr.add(ps);
55194         }
55195     },
55196     /**
55197      * Adds a xtype elements to the layout.
55198      * <pre><code>
55199
55200 layout.addxtype({
55201        xtype : 'ContentPanel',
55202        region: 'west',
55203        items: [ .... ]
55204    }
55205 );
55206
55207 layout.addxtype({
55208         xtype : 'NestedLayoutPanel',
55209         region: 'west',
55210         layout: {
55211            center: { },
55212            west: { }   
55213         },
55214         items : [ ... list of content panels or nested layout panels.. ]
55215    }
55216 );
55217 </code></pre>
55218      * @param {Object} cfg Xtype definition of item to add.
55219      */
55220     addxtype : function(cfg)
55221     {
55222         // basically accepts a pannel...
55223         // can accept a layout region..!?!?
55224         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55225         
55226         if (!cfg.xtype.match(/Panel$/)) {
55227             return false;
55228         }
55229         var ret = false;
55230         
55231         if (typeof(cfg.region) == 'undefined') {
55232             Roo.log("Failed to add Panel, region was not set");
55233             Roo.log(cfg);
55234             return false;
55235         }
55236         var region = cfg.region;
55237         delete cfg.region;
55238         
55239           
55240         var xitems = [];
55241         if (cfg.items) {
55242             xitems = cfg.items;
55243             delete cfg.items;
55244         }
55245         var nb = false;
55246         
55247         switch(cfg.xtype) 
55248         {
55249             case 'ContentPanel':  // ContentPanel (el, cfg)
55250             case 'ScrollPanel':  // ContentPanel (el, cfg)
55251             case 'ViewPanel': 
55252                 if(cfg.autoCreate) {
55253                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55254                 } else {
55255                     var el = this.el.createChild();
55256                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55257                 }
55258                 
55259                 this.add(region, ret);
55260                 break;
55261             
55262             
55263             case 'TreePanel': // our new panel!
55264                 cfg.el = this.el.createChild();
55265                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55266                 this.add(region, ret);
55267                 break;
55268             
55269             case 'NestedLayoutPanel': 
55270                 // create a new Layout (which is  a Border Layout...
55271                 var el = this.el.createChild();
55272                 var clayout = cfg.layout;
55273                 delete cfg.layout;
55274                 clayout.items   = clayout.items  || [];
55275                 // replace this exitems with the clayout ones..
55276                 xitems = clayout.items;
55277                  
55278                 
55279                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55280                     cfg.background = false;
55281                 }
55282                 var layout = new Roo.BorderLayout(el, clayout);
55283                 
55284                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55285                 //console.log('adding nested layout panel '  + cfg.toSource());
55286                 this.add(region, ret);
55287                 nb = {}; /// find first...
55288                 break;
55289                 
55290             case 'GridPanel': 
55291             
55292                 // needs grid and region
55293                 
55294                 //var el = this.getRegion(region).el.createChild();
55295                 var el = this.el.createChild();
55296                 // create the grid first...
55297                 
55298                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55299                 delete cfg.grid;
55300                 if (region == 'center' && this.active ) {
55301                     cfg.background = false;
55302                 }
55303                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55304                 
55305                 this.add(region, ret);
55306                 if (cfg.background) {
55307                     ret.on('activate', function(gp) {
55308                         if (!gp.grid.rendered) {
55309                             gp.grid.render();
55310                         }
55311                     });
55312                 } else {
55313                     grid.render();
55314                 }
55315                 break;
55316            
55317            
55318            
55319                 
55320                 
55321                 
55322             default:
55323                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55324                     
55325                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55326                     this.add(region, ret);
55327                 } else {
55328                 
55329                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55330                     return null;
55331                 }
55332                 
55333              // GridPanel (grid, cfg)
55334             
55335         }
55336         this.beginUpdate();
55337         // add children..
55338         var region = '';
55339         var abn = {};
55340         Roo.each(xitems, function(i)  {
55341             region = nb && i.region ? i.region : false;
55342             
55343             var add = ret.addxtype(i);
55344            
55345             if (region) {
55346                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55347                 if (!i.background) {
55348                     abn[region] = nb[region] ;
55349                 }
55350             }
55351             
55352         });
55353         this.endUpdate();
55354
55355         // make the last non-background panel active..
55356         //if (nb) { Roo.log(abn); }
55357         if (nb) {
55358             
55359             for(var r in abn) {
55360                 region = this.getRegion(r);
55361                 if (region) {
55362                     // tried using nb[r], but it does not work..
55363                      
55364                     region.showPanel(abn[r]);
55365                    
55366                 }
55367             }
55368         }
55369         return ret;
55370         
55371     }
55372 });
55373
55374 /**
55375  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55376  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55377  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55378  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55379  * <pre><code>
55380 // shorthand
55381 var CP = Roo.ContentPanel;
55382
55383 var layout = Roo.BorderLayout.create({
55384     north: {
55385         initialSize: 25,
55386         titlebar: false,
55387         panels: [new CP("north", "North")]
55388     },
55389     west: {
55390         split:true,
55391         initialSize: 200,
55392         minSize: 175,
55393         maxSize: 400,
55394         titlebar: true,
55395         collapsible: true,
55396         panels: [new CP("west", {title: "West"})]
55397     },
55398     east: {
55399         split:true,
55400         initialSize: 202,
55401         minSize: 175,
55402         maxSize: 400,
55403         titlebar: true,
55404         collapsible: true,
55405         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55406     },
55407     south: {
55408         split:true,
55409         initialSize: 100,
55410         minSize: 100,
55411         maxSize: 200,
55412         titlebar: true,
55413         collapsible: true,
55414         panels: [new CP("south", {title: "South", closable: true})]
55415     },
55416     center: {
55417         titlebar: true,
55418         autoScroll:true,
55419         resizeTabs: true,
55420         minTabWidth: 50,
55421         preferredTabWidth: 150,
55422         panels: [
55423             new CP("center1", {title: "Close Me", closable: true}),
55424             new CP("center2", {title: "Center Panel", closable: false})
55425         ]
55426     }
55427 }, document.body);
55428
55429 layout.getRegion("center").showPanel("center1");
55430 </code></pre>
55431  * @param config
55432  * @param targetEl
55433  */
55434 Roo.BorderLayout.create = function(config, targetEl){
55435     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55436     layout.beginUpdate();
55437     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55438     for(var j = 0, jlen = regions.length; j < jlen; j++){
55439         var lr = regions[j];
55440         if(layout.regions[lr] && config[lr].panels){
55441             var r = layout.regions[lr];
55442             var ps = config[lr].panels;
55443             layout.addTypedPanels(r, ps);
55444         }
55445     }
55446     layout.endUpdate();
55447     return layout;
55448 };
55449
55450 // private
55451 Roo.BorderLayout.RegionFactory = {
55452     // private
55453     validRegions : ["north","south","east","west","center"],
55454
55455     // private
55456     create : function(target, mgr, config){
55457         target = target.toLowerCase();
55458         if(config.lightweight || config.basic){
55459             return new Roo.BasicLayoutRegion(mgr, config, target);
55460         }
55461         switch(target){
55462             case "north":
55463                 return new Roo.NorthLayoutRegion(mgr, config);
55464             case "south":
55465                 return new Roo.SouthLayoutRegion(mgr, config);
55466             case "east":
55467                 return new Roo.EastLayoutRegion(mgr, config);
55468             case "west":
55469                 return new Roo.WestLayoutRegion(mgr, config);
55470             case "center":
55471                 return new Roo.CenterLayoutRegion(mgr, config);
55472         }
55473         throw 'Layout region "'+target+'" not supported.';
55474     }
55475 };/*
55476  * Based on:
55477  * Ext JS Library 1.1.1
55478  * Copyright(c) 2006-2007, Ext JS, LLC.
55479  *
55480  * Originally Released Under LGPL - original licence link has changed is not relivant.
55481  *
55482  * Fork - LGPL
55483  * <script type="text/javascript">
55484  */
55485  
55486 /**
55487  * @class Roo.BasicLayoutRegion
55488  * @extends Roo.util.Observable
55489  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55490  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55491  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55492  */
55493 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55494     this.mgr = mgr;
55495     this.position  = pos;
55496     this.events = {
55497         /**
55498          * @scope Roo.BasicLayoutRegion
55499          */
55500         
55501         /**
55502          * @event beforeremove
55503          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55504          * @param {Roo.LayoutRegion} this
55505          * @param {Roo.ContentPanel} panel The panel
55506          * @param {Object} e The cancel event object
55507          */
55508         "beforeremove" : true,
55509         /**
55510          * @event invalidated
55511          * Fires when the layout for this region is changed.
55512          * @param {Roo.LayoutRegion} this
55513          */
55514         "invalidated" : true,
55515         /**
55516          * @event visibilitychange
55517          * Fires when this region is shown or hidden 
55518          * @param {Roo.LayoutRegion} this
55519          * @param {Boolean} visibility true or false
55520          */
55521         "visibilitychange" : true,
55522         /**
55523          * @event paneladded
55524          * Fires when a panel is added. 
55525          * @param {Roo.LayoutRegion} this
55526          * @param {Roo.ContentPanel} panel The panel
55527          */
55528         "paneladded" : true,
55529         /**
55530          * @event panelremoved
55531          * Fires when a panel is removed. 
55532          * @param {Roo.LayoutRegion} this
55533          * @param {Roo.ContentPanel} panel The panel
55534          */
55535         "panelremoved" : true,
55536         /**
55537          * @event beforecollapse
55538          * Fires when this region before collapse.
55539          * @param {Roo.LayoutRegion} this
55540          */
55541         "beforecollapse" : true,
55542         /**
55543          * @event collapsed
55544          * Fires when this region is collapsed.
55545          * @param {Roo.LayoutRegion} this
55546          */
55547         "collapsed" : true,
55548         /**
55549          * @event expanded
55550          * Fires when this region is expanded.
55551          * @param {Roo.LayoutRegion} this
55552          */
55553         "expanded" : true,
55554         /**
55555          * @event slideshow
55556          * Fires when this region is slid into view.
55557          * @param {Roo.LayoutRegion} this
55558          */
55559         "slideshow" : true,
55560         /**
55561          * @event slidehide
55562          * Fires when this region slides out of view. 
55563          * @param {Roo.LayoutRegion} this
55564          */
55565         "slidehide" : true,
55566         /**
55567          * @event panelactivated
55568          * Fires when a panel is activated. 
55569          * @param {Roo.LayoutRegion} this
55570          * @param {Roo.ContentPanel} panel The activated panel
55571          */
55572         "panelactivated" : true,
55573         /**
55574          * @event resized
55575          * Fires when the user resizes this region. 
55576          * @param {Roo.LayoutRegion} this
55577          * @param {Number} newSize The new size (width for east/west, height for north/south)
55578          */
55579         "resized" : true
55580     };
55581     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55582     this.panels = new Roo.util.MixedCollection();
55583     this.panels.getKey = this.getPanelId.createDelegate(this);
55584     this.box = null;
55585     this.activePanel = null;
55586     // ensure listeners are added...
55587     
55588     if (config.listeners || config.events) {
55589         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55590             listeners : config.listeners || {},
55591             events : config.events || {}
55592         });
55593     }
55594     
55595     if(skipConfig !== true){
55596         this.applyConfig(config);
55597     }
55598 };
55599
55600 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55601     getPanelId : function(p){
55602         return p.getId();
55603     },
55604     
55605     applyConfig : function(config){
55606         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55607         this.config = config;
55608         
55609     },
55610     
55611     /**
55612      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55613      * the width, for horizontal (north, south) the height.
55614      * @param {Number} newSize The new width or height
55615      */
55616     resizeTo : function(newSize){
55617         var el = this.el ? this.el :
55618                  (this.activePanel ? this.activePanel.getEl() : null);
55619         if(el){
55620             switch(this.position){
55621                 case "east":
55622                 case "west":
55623                     el.setWidth(newSize);
55624                     this.fireEvent("resized", this, newSize);
55625                 break;
55626                 case "north":
55627                 case "south":
55628                     el.setHeight(newSize);
55629                     this.fireEvent("resized", this, newSize);
55630                 break;                
55631             }
55632         }
55633     },
55634     
55635     getBox : function(){
55636         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55637     },
55638     
55639     getMargins : function(){
55640         return this.margins;
55641     },
55642     
55643     updateBox : function(box){
55644         this.box = box;
55645         var el = this.activePanel.getEl();
55646         el.dom.style.left = box.x + "px";
55647         el.dom.style.top = box.y + "px";
55648         this.activePanel.setSize(box.width, box.height);
55649     },
55650     
55651     /**
55652      * Returns the container element for this region.
55653      * @return {Roo.Element}
55654      */
55655     getEl : function(){
55656         return this.activePanel;
55657     },
55658     
55659     /**
55660      * Returns true if this region is currently visible.
55661      * @return {Boolean}
55662      */
55663     isVisible : function(){
55664         return this.activePanel ? true : false;
55665     },
55666     
55667     setActivePanel : function(panel){
55668         panel = this.getPanel(panel);
55669         if(this.activePanel && this.activePanel != panel){
55670             this.activePanel.setActiveState(false);
55671             this.activePanel.getEl().setLeftTop(-10000,-10000);
55672         }
55673         this.activePanel = panel;
55674         panel.setActiveState(true);
55675         if(this.box){
55676             panel.setSize(this.box.width, this.box.height);
55677         }
55678         this.fireEvent("panelactivated", this, panel);
55679         this.fireEvent("invalidated");
55680     },
55681     
55682     /**
55683      * Show the specified panel.
55684      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55685      * @return {Roo.ContentPanel} The shown panel or null
55686      */
55687     showPanel : function(panel){
55688         if(panel = this.getPanel(panel)){
55689             this.setActivePanel(panel);
55690         }
55691         return panel;
55692     },
55693     
55694     /**
55695      * Get the active panel for this region.
55696      * @return {Roo.ContentPanel} The active panel or null
55697      */
55698     getActivePanel : function(){
55699         return this.activePanel;
55700     },
55701     
55702     /**
55703      * Add the passed ContentPanel(s)
55704      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55705      * @return {Roo.ContentPanel} The panel added (if only one was added)
55706      */
55707     add : function(panel){
55708         if(arguments.length > 1){
55709             for(var i = 0, len = arguments.length; i < len; i++) {
55710                 this.add(arguments[i]);
55711             }
55712             return null;
55713         }
55714         if(this.hasPanel(panel)){
55715             this.showPanel(panel);
55716             return panel;
55717         }
55718         var el = panel.getEl();
55719         if(el.dom.parentNode != this.mgr.el.dom){
55720             this.mgr.el.dom.appendChild(el.dom);
55721         }
55722         if(panel.setRegion){
55723             panel.setRegion(this);
55724         }
55725         this.panels.add(panel);
55726         el.setStyle("position", "absolute");
55727         if(!panel.background){
55728             this.setActivePanel(panel);
55729             if(this.config.initialSize && this.panels.getCount()==1){
55730                 this.resizeTo(this.config.initialSize);
55731             }
55732         }
55733         this.fireEvent("paneladded", this, panel);
55734         return panel;
55735     },
55736     
55737     /**
55738      * Returns true if the panel is in this region.
55739      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55740      * @return {Boolean}
55741      */
55742     hasPanel : function(panel){
55743         if(typeof panel == "object"){ // must be panel obj
55744             panel = panel.getId();
55745         }
55746         return this.getPanel(panel) ? true : false;
55747     },
55748     
55749     /**
55750      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55751      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55752      * @param {Boolean} preservePanel Overrides the config preservePanel option
55753      * @return {Roo.ContentPanel} The panel that was removed
55754      */
55755     remove : function(panel, preservePanel){
55756         panel = this.getPanel(panel);
55757         if(!panel){
55758             return null;
55759         }
55760         var e = {};
55761         this.fireEvent("beforeremove", this, panel, e);
55762         if(e.cancel === true){
55763             return null;
55764         }
55765         var panelId = panel.getId();
55766         this.panels.removeKey(panelId);
55767         return panel;
55768     },
55769     
55770     /**
55771      * Returns the panel specified or null if it's not in this region.
55772      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55773      * @return {Roo.ContentPanel}
55774      */
55775     getPanel : function(id){
55776         if(typeof id == "object"){ // must be panel obj
55777             return id;
55778         }
55779         return this.panels.get(id);
55780     },
55781     
55782     /**
55783      * Returns this regions position (north/south/east/west/center).
55784      * @return {String} 
55785      */
55786     getPosition: function(){
55787         return this.position;    
55788     }
55789 });/*
55790  * Based on:
55791  * Ext JS Library 1.1.1
55792  * Copyright(c) 2006-2007, Ext JS, LLC.
55793  *
55794  * Originally Released Under LGPL - original licence link has changed is not relivant.
55795  *
55796  * Fork - LGPL
55797  * <script type="text/javascript">
55798  */
55799  
55800 /**
55801  * @class Roo.LayoutRegion
55802  * @extends Roo.BasicLayoutRegion
55803  * This class represents a region in a layout manager.
55804  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55805  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55806  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55807  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55808  * @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})
55809  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55810  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55811  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55812  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55813  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55814  * @cfg {String}    title           The title for the region (overrides panel titles)
55815  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55816  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55817  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55818  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55819  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55820  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55821  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55822  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55823  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55824  * @cfg {Boolean}   showPin         True to show a pin button
55825  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55826  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55827  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55828  * @cfg {Number}    width           For East/West panels
55829  * @cfg {Number}    height          For North/South panels
55830  * @cfg {Boolean}   split           To show the splitter
55831  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55832  */
55833 Roo.LayoutRegion = function(mgr, config, pos){
55834     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55835     var dh = Roo.DomHelper;
55836     /** This region's container element 
55837     * @type Roo.Element */
55838     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55839     /** This region's title element 
55840     * @type Roo.Element */
55841
55842     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55843         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55844         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55845     ]}, true);
55846     this.titleEl.enableDisplayMode();
55847     /** This region's title text element 
55848     * @type HTMLElement */
55849     this.titleTextEl = this.titleEl.dom.firstChild;
55850     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55851     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55852     this.closeBtn.enableDisplayMode();
55853     this.closeBtn.on("click", this.closeClicked, this);
55854     this.closeBtn.hide();
55855
55856     this.createBody(config);
55857     this.visible = true;
55858     this.collapsed = false;
55859
55860     if(config.hideWhenEmpty){
55861         this.hide();
55862         this.on("paneladded", this.validateVisibility, this);
55863         this.on("panelremoved", this.validateVisibility, this);
55864     }
55865     this.applyConfig(config);
55866 };
55867
55868 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55869
55870     createBody : function(){
55871         /** This region's body element 
55872         * @type Roo.Element */
55873         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55874     },
55875
55876     applyConfig : function(c){
55877         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55878             var dh = Roo.DomHelper;
55879             if(c.titlebar !== false){
55880                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55881                 this.collapseBtn.on("click", this.collapse, this);
55882                 this.collapseBtn.enableDisplayMode();
55883
55884                 if(c.showPin === true || this.showPin){
55885                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55886                     this.stickBtn.enableDisplayMode();
55887                     this.stickBtn.on("click", this.expand, this);
55888                     this.stickBtn.hide();
55889                 }
55890             }
55891             /** This region's collapsed element
55892             * @type Roo.Element */
55893             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55894                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55895             ]}, true);
55896             if(c.floatable !== false){
55897                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55898                this.collapsedEl.on("click", this.collapseClick, this);
55899             }
55900
55901             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55902                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55903                    id: "message", unselectable: "on", style:{"float":"left"}});
55904                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55905              }
55906             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55907             this.expandBtn.on("click", this.expand, this);
55908         }
55909         if(this.collapseBtn){
55910             this.collapseBtn.setVisible(c.collapsible == true);
55911         }
55912         this.cmargins = c.cmargins || this.cmargins ||
55913                          (this.position == "west" || this.position == "east" ?
55914                              {top: 0, left: 2, right:2, bottom: 0} :
55915                              {top: 2, left: 0, right:0, bottom: 2});
55916         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55917         this.bottomTabs = c.tabPosition != "top";
55918         this.autoScroll = c.autoScroll || false;
55919         if(this.autoScroll){
55920             this.bodyEl.setStyle("overflow", "auto");
55921         }else{
55922             this.bodyEl.setStyle("overflow", "hidden");
55923         }
55924         //if(c.titlebar !== false){
55925             if((!c.titlebar && !c.title) || c.titlebar === false){
55926                 this.titleEl.hide();
55927             }else{
55928                 this.titleEl.show();
55929                 if(c.title){
55930                     this.titleTextEl.innerHTML = c.title;
55931                 }
55932             }
55933         //}
55934         this.duration = c.duration || .30;
55935         this.slideDuration = c.slideDuration || .45;
55936         this.config = c;
55937         if(c.collapsed){
55938             this.collapse(true);
55939         }
55940         if(c.hidden){
55941             this.hide();
55942         }
55943     },
55944     /**
55945      * Returns true if this region is currently visible.
55946      * @return {Boolean}
55947      */
55948     isVisible : function(){
55949         return this.visible;
55950     },
55951
55952     /**
55953      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55954      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55955      */
55956     setCollapsedTitle : function(title){
55957         title = title || "&#160;";
55958         if(this.collapsedTitleTextEl){
55959             this.collapsedTitleTextEl.innerHTML = title;
55960         }
55961     },
55962
55963     getBox : function(){
55964         var b;
55965         if(!this.collapsed){
55966             b = this.el.getBox(false, true);
55967         }else{
55968             b = this.collapsedEl.getBox(false, true);
55969         }
55970         return b;
55971     },
55972
55973     getMargins : function(){
55974         return this.collapsed ? this.cmargins : this.margins;
55975     },
55976
55977     highlight : function(){
55978         this.el.addClass("x-layout-panel-dragover");
55979     },
55980
55981     unhighlight : function(){
55982         this.el.removeClass("x-layout-panel-dragover");
55983     },
55984
55985     updateBox : function(box){
55986         this.box = box;
55987         if(!this.collapsed){
55988             this.el.dom.style.left = box.x + "px";
55989             this.el.dom.style.top = box.y + "px";
55990             this.updateBody(box.width, box.height);
55991         }else{
55992             this.collapsedEl.dom.style.left = box.x + "px";
55993             this.collapsedEl.dom.style.top = box.y + "px";
55994             this.collapsedEl.setSize(box.width, box.height);
55995         }
55996         if(this.tabs){
55997             this.tabs.autoSizeTabs();
55998         }
55999     },
56000
56001     updateBody : function(w, h){
56002         if(w !== null){
56003             this.el.setWidth(w);
56004             w -= this.el.getBorderWidth("rl");
56005             if(this.config.adjustments){
56006                 w += this.config.adjustments[0];
56007             }
56008         }
56009         if(h !== null){
56010             this.el.setHeight(h);
56011             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56012             h -= this.el.getBorderWidth("tb");
56013             if(this.config.adjustments){
56014                 h += this.config.adjustments[1];
56015             }
56016             this.bodyEl.setHeight(h);
56017             if(this.tabs){
56018                 h = this.tabs.syncHeight(h);
56019             }
56020         }
56021         if(this.panelSize){
56022             w = w !== null ? w : this.panelSize.width;
56023             h = h !== null ? h : this.panelSize.height;
56024         }
56025         if(this.activePanel){
56026             var el = this.activePanel.getEl();
56027             w = w !== null ? w : el.getWidth();
56028             h = h !== null ? h : el.getHeight();
56029             this.panelSize = {width: w, height: h};
56030             this.activePanel.setSize(w, h);
56031         }
56032         if(Roo.isIE && this.tabs){
56033             this.tabs.el.repaint();
56034         }
56035     },
56036
56037     /**
56038      * Returns the container element for this region.
56039      * @return {Roo.Element}
56040      */
56041     getEl : function(){
56042         return this.el;
56043     },
56044
56045     /**
56046      * Hides this region.
56047      */
56048     hide : function(){
56049         if(!this.collapsed){
56050             this.el.dom.style.left = "-2000px";
56051             this.el.hide();
56052         }else{
56053             this.collapsedEl.dom.style.left = "-2000px";
56054             this.collapsedEl.hide();
56055         }
56056         this.visible = false;
56057         this.fireEvent("visibilitychange", this, false);
56058     },
56059
56060     /**
56061      * Shows this region if it was previously hidden.
56062      */
56063     show : function(){
56064         if(!this.collapsed){
56065             this.el.show();
56066         }else{
56067             this.collapsedEl.show();
56068         }
56069         this.visible = true;
56070         this.fireEvent("visibilitychange", this, true);
56071     },
56072
56073     closeClicked : function(){
56074         if(this.activePanel){
56075             this.remove(this.activePanel);
56076         }
56077     },
56078
56079     collapseClick : function(e){
56080         if(this.isSlid){
56081            e.stopPropagation();
56082            this.slideIn();
56083         }else{
56084            e.stopPropagation();
56085            this.slideOut();
56086         }
56087     },
56088
56089     /**
56090      * Collapses this region.
56091      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56092      */
56093     collapse : function(skipAnim, skipCheck){
56094         if(this.collapsed) {
56095             return;
56096         }
56097         
56098         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56099             
56100             this.collapsed = true;
56101             if(this.split){
56102                 this.split.el.hide();
56103             }
56104             if(this.config.animate && skipAnim !== true){
56105                 this.fireEvent("invalidated", this);
56106                 this.animateCollapse();
56107             }else{
56108                 this.el.setLocation(-20000,-20000);
56109                 this.el.hide();
56110                 this.collapsedEl.show();
56111                 this.fireEvent("collapsed", this);
56112                 this.fireEvent("invalidated", this);
56113             }
56114         }
56115         
56116     },
56117
56118     animateCollapse : function(){
56119         // overridden
56120     },
56121
56122     /**
56123      * Expands this region if it was previously collapsed.
56124      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56125      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56126      */
56127     expand : function(e, skipAnim){
56128         if(e) {
56129             e.stopPropagation();
56130         }
56131         if(!this.collapsed || this.el.hasActiveFx()) {
56132             return;
56133         }
56134         if(this.isSlid){
56135             this.afterSlideIn();
56136             skipAnim = true;
56137         }
56138         this.collapsed = false;
56139         if(this.config.animate && skipAnim !== true){
56140             this.animateExpand();
56141         }else{
56142             this.el.show();
56143             if(this.split){
56144                 this.split.el.show();
56145             }
56146             this.collapsedEl.setLocation(-2000,-2000);
56147             this.collapsedEl.hide();
56148             this.fireEvent("invalidated", this);
56149             this.fireEvent("expanded", this);
56150         }
56151     },
56152
56153     animateExpand : function(){
56154         // overridden
56155     },
56156
56157     initTabs : function()
56158     {
56159         this.bodyEl.setStyle("overflow", "hidden");
56160         var ts = new Roo.TabPanel(
56161                 this.bodyEl.dom,
56162                 {
56163                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56164                     disableTooltips: this.config.disableTabTips,
56165                     toolbar : this.config.toolbar
56166                 }
56167         );
56168         if(this.config.hideTabs){
56169             ts.stripWrap.setDisplayed(false);
56170         }
56171         this.tabs = ts;
56172         ts.resizeTabs = this.config.resizeTabs === true;
56173         ts.minTabWidth = this.config.minTabWidth || 40;
56174         ts.maxTabWidth = this.config.maxTabWidth || 250;
56175         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56176         ts.monitorResize = false;
56177         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56178         ts.bodyEl.addClass('x-layout-tabs-body');
56179         this.panels.each(this.initPanelAsTab, this);
56180     },
56181
56182     initPanelAsTab : function(panel){
56183         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56184                     this.config.closeOnTab && panel.isClosable());
56185         if(panel.tabTip !== undefined){
56186             ti.setTooltip(panel.tabTip);
56187         }
56188         ti.on("activate", function(){
56189               this.setActivePanel(panel);
56190         }, this);
56191         if(this.config.closeOnTab){
56192             ti.on("beforeclose", function(t, e){
56193                 e.cancel = true;
56194                 this.remove(panel);
56195             }, this);
56196         }
56197         return ti;
56198     },
56199
56200     updatePanelTitle : function(panel, title){
56201         if(this.activePanel == panel){
56202             this.updateTitle(title);
56203         }
56204         if(this.tabs){
56205             var ti = this.tabs.getTab(panel.getEl().id);
56206             ti.setText(title);
56207             if(panel.tabTip !== undefined){
56208                 ti.setTooltip(panel.tabTip);
56209             }
56210         }
56211     },
56212
56213     updateTitle : function(title){
56214         if(this.titleTextEl && !this.config.title){
56215             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56216         }
56217     },
56218
56219     setActivePanel : function(panel){
56220         panel = this.getPanel(panel);
56221         if(this.activePanel && this.activePanel != panel){
56222             this.activePanel.setActiveState(false);
56223         }
56224         this.activePanel = panel;
56225         panel.setActiveState(true);
56226         if(this.panelSize){
56227             panel.setSize(this.panelSize.width, this.panelSize.height);
56228         }
56229         if(this.closeBtn){
56230             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56231         }
56232         this.updateTitle(panel.getTitle());
56233         if(this.tabs){
56234             this.fireEvent("invalidated", this);
56235         }
56236         this.fireEvent("panelactivated", this, panel);
56237     },
56238
56239     /**
56240      * Shows the specified panel.
56241      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56242      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56243      */
56244     showPanel : function(panel)
56245     {
56246         panel = this.getPanel(panel);
56247         if(panel){
56248             if(this.tabs){
56249                 var tab = this.tabs.getTab(panel.getEl().id);
56250                 if(tab.isHidden()){
56251                     this.tabs.unhideTab(tab.id);
56252                 }
56253                 tab.activate();
56254             }else{
56255                 this.setActivePanel(panel);
56256             }
56257         }
56258         return panel;
56259     },
56260
56261     /**
56262      * Get the active panel for this region.
56263      * @return {Roo.ContentPanel} The active panel or null
56264      */
56265     getActivePanel : function(){
56266         return this.activePanel;
56267     },
56268
56269     validateVisibility : function(){
56270         if(this.panels.getCount() < 1){
56271             this.updateTitle("&#160;");
56272             this.closeBtn.hide();
56273             this.hide();
56274         }else{
56275             if(!this.isVisible()){
56276                 this.show();
56277             }
56278         }
56279     },
56280
56281     /**
56282      * Adds the passed ContentPanel(s) to this region.
56283      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56284      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56285      */
56286     add : function(panel){
56287         if(arguments.length > 1){
56288             for(var i = 0, len = arguments.length; i < len; i++) {
56289                 this.add(arguments[i]);
56290             }
56291             return null;
56292         }
56293         if(this.hasPanel(panel)){
56294             this.showPanel(panel);
56295             return panel;
56296         }
56297         panel.setRegion(this);
56298         this.panels.add(panel);
56299         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56300             this.bodyEl.dom.appendChild(panel.getEl().dom);
56301             if(panel.background !== true){
56302                 this.setActivePanel(panel);
56303             }
56304             this.fireEvent("paneladded", this, panel);
56305             return panel;
56306         }
56307         if(!this.tabs){
56308             this.initTabs();
56309         }else{
56310             this.initPanelAsTab(panel);
56311         }
56312         if(panel.background !== true){
56313             this.tabs.activate(panel.getEl().id);
56314         }
56315         this.fireEvent("paneladded", this, panel);
56316         return panel;
56317     },
56318
56319     /**
56320      * Hides the tab for the specified panel.
56321      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56322      */
56323     hidePanel : function(panel){
56324         if(this.tabs && (panel = this.getPanel(panel))){
56325             this.tabs.hideTab(panel.getEl().id);
56326         }
56327     },
56328
56329     /**
56330      * Unhides the tab for a previously hidden panel.
56331      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56332      */
56333     unhidePanel : function(panel){
56334         if(this.tabs && (panel = this.getPanel(panel))){
56335             this.tabs.unhideTab(panel.getEl().id);
56336         }
56337     },
56338
56339     clearPanels : function(){
56340         while(this.panels.getCount() > 0){
56341              this.remove(this.panels.first());
56342         }
56343     },
56344
56345     /**
56346      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56347      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56348      * @param {Boolean} preservePanel Overrides the config preservePanel option
56349      * @return {Roo.ContentPanel} The panel that was removed
56350      */
56351     remove : function(panel, preservePanel){
56352         panel = this.getPanel(panel);
56353         if(!panel){
56354             return null;
56355         }
56356         var e = {};
56357         this.fireEvent("beforeremove", this, panel, e);
56358         if(e.cancel === true){
56359             return null;
56360         }
56361         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56362         var panelId = panel.getId();
56363         this.panels.removeKey(panelId);
56364         if(preservePanel){
56365             document.body.appendChild(panel.getEl().dom);
56366         }
56367         if(this.tabs){
56368             this.tabs.removeTab(panel.getEl().id);
56369         }else if (!preservePanel){
56370             this.bodyEl.dom.removeChild(panel.getEl().dom);
56371         }
56372         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56373             var p = this.panels.first();
56374             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56375             tempEl.appendChild(p.getEl().dom);
56376             this.bodyEl.update("");
56377             this.bodyEl.dom.appendChild(p.getEl().dom);
56378             tempEl = null;
56379             this.updateTitle(p.getTitle());
56380             this.tabs = null;
56381             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56382             this.setActivePanel(p);
56383         }
56384         panel.setRegion(null);
56385         if(this.activePanel == panel){
56386             this.activePanel = null;
56387         }
56388         if(this.config.autoDestroy !== false && preservePanel !== true){
56389             try{panel.destroy();}catch(e){}
56390         }
56391         this.fireEvent("panelremoved", this, panel);
56392         return panel;
56393     },
56394
56395     /**
56396      * Returns the TabPanel component used by this region
56397      * @return {Roo.TabPanel}
56398      */
56399     getTabs : function(){
56400         return this.tabs;
56401     },
56402
56403     createTool : function(parentEl, className){
56404         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56405             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56406         btn.addClassOnOver("x-layout-tools-button-over");
56407         return btn;
56408     }
56409 });/*
56410  * Based on:
56411  * Ext JS Library 1.1.1
56412  * Copyright(c) 2006-2007, Ext JS, LLC.
56413  *
56414  * Originally Released Under LGPL - original licence link has changed is not relivant.
56415  *
56416  * Fork - LGPL
56417  * <script type="text/javascript">
56418  */
56419  
56420
56421
56422 /**
56423  * @class Roo.SplitLayoutRegion
56424  * @extends Roo.LayoutRegion
56425  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56426  */
56427 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56428     this.cursor = cursor;
56429     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56430 };
56431
56432 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56433     splitTip : "Drag to resize.",
56434     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56435     useSplitTips : false,
56436
56437     applyConfig : function(config){
56438         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56439         if(config.split){
56440             if(!this.split){
56441                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56442                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56443                 /** The SplitBar for this region 
56444                 * @type Roo.SplitBar */
56445                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56446                 this.split.on("moved", this.onSplitMove, this);
56447                 this.split.useShim = config.useShim === true;
56448                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56449                 if(this.useSplitTips){
56450                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56451                 }
56452                 if(config.collapsible){
56453                     this.split.el.on("dblclick", this.collapse,  this);
56454                 }
56455             }
56456             if(typeof config.minSize != "undefined"){
56457                 this.split.minSize = config.minSize;
56458             }
56459             if(typeof config.maxSize != "undefined"){
56460                 this.split.maxSize = config.maxSize;
56461             }
56462             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56463                 this.hideSplitter();
56464             }
56465         }
56466     },
56467
56468     getHMaxSize : function(){
56469          var cmax = this.config.maxSize || 10000;
56470          var center = this.mgr.getRegion("center");
56471          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56472     },
56473
56474     getVMaxSize : function(){
56475          var cmax = this.config.maxSize || 10000;
56476          var center = this.mgr.getRegion("center");
56477          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56478     },
56479
56480     onSplitMove : function(split, newSize){
56481         this.fireEvent("resized", this, newSize);
56482     },
56483     
56484     /** 
56485      * Returns the {@link Roo.SplitBar} for this region.
56486      * @return {Roo.SplitBar}
56487      */
56488     getSplitBar : function(){
56489         return this.split;
56490     },
56491     
56492     hide : function(){
56493         this.hideSplitter();
56494         Roo.SplitLayoutRegion.superclass.hide.call(this);
56495     },
56496
56497     hideSplitter : function(){
56498         if(this.split){
56499             this.split.el.setLocation(-2000,-2000);
56500             this.split.el.hide();
56501         }
56502     },
56503
56504     show : function(){
56505         if(this.split){
56506             this.split.el.show();
56507         }
56508         Roo.SplitLayoutRegion.superclass.show.call(this);
56509     },
56510     
56511     beforeSlide: function(){
56512         if(Roo.isGecko){// firefox overflow auto bug workaround
56513             this.bodyEl.clip();
56514             if(this.tabs) {
56515                 this.tabs.bodyEl.clip();
56516             }
56517             if(this.activePanel){
56518                 this.activePanel.getEl().clip();
56519                 
56520                 if(this.activePanel.beforeSlide){
56521                     this.activePanel.beforeSlide();
56522                 }
56523             }
56524         }
56525     },
56526     
56527     afterSlide : function(){
56528         if(Roo.isGecko){// firefox overflow auto bug workaround
56529             this.bodyEl.unclip();
56530             if(this.tabs) {
56531                 this.tabs.bodyEl.unclip();
56532             }
56533             if(this.activePanel){
56534                 this.activePanel.getEl().unclip();
56535                 if(this.activePanel.afterSlide){
56536                     this.activePanel.afterSlide();
56537                 }
56538             }
56539         }
56540     },
56541
56542     initAutoHide : function(){
56543         if(this.autoHide !== false){
56544             if(!this.autoHideHd){
56545                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56546                 this.autoHideHd = {
56547                     "mouseout": function(e){
56548                         if(!e.within(this.el, true)){
56549                             st.delay(500);
56550                         }
56551                     },
56552                     "mouseover" : function(e){
56553                         st.cancel();
56554                     },
56555                     scope : this
56556                 };
56557             }
56558             this.el.on(this.autoHideHd);
56559         }
56560     },
56561
56562     clearAutoHide : function(){
56563         if(this.autoHide !== false){
56564             this.el.un("mouseout", this.autoHideHd.mouseout);
56565             this.el.un("mouseover", this.autoHideHd.mouseover);
56566         }
56567     },
56568
56569     clearMonitor : function(){
56570         Roo.get(document).un("click", this.slideInIf, this);
56571     },
56572
56573     // these names are backwards but not changed for compat
56574     slideOut : function(){
56575         if(this.isSlid || this.el.hasActiveFx()){
56576             return;
56577         }
56578         this.isSlid = true;
56579         if(this.collapseBtn){
56580             this.collapseBtn.hide();
56581         }
56582         this.closeBtnState = this.closeBtn.getStyle('display');
56583         this.closeBtn.hide();
56584         if(this.stickBtn){
56585             this.stickBtn.show();
56586         }
56587         this.el.show();
56588         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56589         this.beforeSlide();
56590         this.el.setStyle("z-index", 10001);
56591         this.el.slideIn(this.getSlideAnchor(), {
56592             callback: function(){
56593                 this.afterSlide();
56594                 this.initAutoHide();
56595                 Roo.get(document).on("click", this.slideInIf, this);
56596                 this.fireEvent("slideshow", this);
56597             },
56598             scope: this,
56599             block: true
56600         });
56601     },
56602
56603     afterSlideIn : function(){
56604         this.clearAutoHide();
56605         this.isSlid = false;
56606         this.clearMonitor();
56607         this.el.setStyle("z-index", "");
56608         if(this.collapseBtn){
56609             this.collapseBtn.show();
56610         }
56611         this.closeBtn.setStyle('display', this.closeBtnState);
56612         if(this.stickBtn){
56613             this.stickBtn.hide();
56614         }
56615         this.fireEvent("slidehide", this);
56616     },
56617
56618     slideIn : function(cb){
56619         if(!this.isSlid || this.el.hasActiveFx()){
56620             Roo.callback(cb);
56621             return;
56622         }
56623         this.isSlid = false;
56624         this.beforeSlide();
56625         this.el.slideOut(this.getSlideAnchor(), {
56626             callback: function(){
56627                 this.el.setLeftTop(-10000, -10000);
56628                 this.afterSlide();
56629                 this.afterSlideIn();
56630                 Roo.callback(cb);
56631             },
56632             scope: this,
56633             block: true
56634         });
56635     },
56636     
56637     slideInIf : function(e){
56638         if(!e.within(this.el)){
56639             this.slideIn();
56640         }
56641     },
56642
56643     animateCollapse : function(){
56644         this.beforeSlide();
56645         this.el.setStyle("z-index", 20000);
56646         var anchor = this.getSlideAnchor();
56647         this.el.slideOut(anchor, {
56648             callback : function(){
56649                 this.el.setStyle("z-index", "");
56650                 this.collapsedEl.slideIn(anchor, {duration:.3});
56651                 this.afterSlide();
56652                 this.el.setLocation(-10000,-10000);
56653                 this.el.hide();
56654                 this.fireEvent("collapsed", this);
56655             },
56656             scope: this,
56657             block: true
56658         });
56659     },
56660
56661     animateExpand : function(){
56662         this.beforeSlide();
56663         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56664         this.el.setStyle("z-index", 20000);
56665         this.collapsedEl.hide({
56666             duration:.1
56667         });
56668         this.el.slideIn(this.getSlideAnchor(), {
56669             callback : function(){
56670                 this.el.setStyle("z-index", "");
56671                 this.afterSlide();
56672                 if(this.split){
56673                     this.split.el.show();
56674                 }
56675                 this.fireEvent("invalidated", this);
56676                 this.fireEvent("expanded", this);
56677             },
56678             scope: this,
56679             block: true
56680         });
56681     },
56682
56683     anchors : {
56684         "west" : "left",
56685         "east" : "right",
56686         "north" : "top",
56687         "south" : "bottom"
56688     },
56689
56690     sanchors : {
56691         "west" : "l",
56692         "east" : "r",
56693         "north" : "t",
56694         "south" : "b"
56695     },
56696
56697     canchors : {
56698         "west" : "tl-tr",
56699         "east" : "tr-tl",
56700         "north" : "tl-bl",
56701         "south" : "bl-tl"
56702     },
56703
56704     getAnchor : function(){
56705         return this.anchors[this.position];
56706     },
56707
56708     getCollapseAnchor : function(){
56709         return this.canchors[this.position];
56710     },
56711
56712     getSlideAnchor : function(){
56713         return this.sanchors[this.position];
56714     },
56715
56716     getAlignAdj : function(){
56717         var cm = this.cmargins;
56718         switch(this.position){
56719             case "west":
56720                 return [0, 0];
56721             break;
56722             case "east":
56723                 return [0, 0];
56724             break;
56725             case "north":
56726                 return [0, 0];
56727             break;
56728             case "south":
56729                 return [0, 0];
56730             break;
56731         }
56732     },
56733
56734     getExpandAdj : function(){
56735         var c = this.collapsedEl, cm = this.cmargins;
56736         switch(this.position){
56737             case "west":
56738                 return [-(cm.right+c.getWidth()+cm.left), 0];
56739             break;
56740             case "east":
56741                 return [cm.right+c.getWidth()+cm.left, 0];
56742             break;
56743             case "north":
56744                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56745             break;
56746             case "south":
56747                 return [0, cm.top+cm.bottom+c.getHeight()];
56748             break;
56749         }
56750     }
56751 });/*
56752  * Based on:
56753  * Ext JS Library 1.1.1
56754  * Copyright(c) 2006-2007, Ext JS, LLC.
56755  *
56756  * Originally Released Under LGPL - original licence link has changed is not relivant.
56757  *
56758  * Fork - LGPL
56759  * <script type="text/javascript">
56760  */
56761 /*
56762  * These classes are private internal classes
56763  */
56764 Roo.CenterLayoutRegion = function(mgr, config){
56765     Roo.LayoutRegion.call(this, mgr, config, "center");
56766     this.visible = true;
56767     this.minWidth = config.minWidth || 20;
56768     this.minHeight = config.minHeight || 20;
56769 };
56770
56771 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56772     hide : function(){
56773         // center panel can't be hidden
56774     },
56775     
56776     show : function(){
56777         // center panel can't be hidden
56778     },
56779     
56780     getMinWidth: function(){
56781         return this.minWidth;
56782     },
56783     
56784     getMinHeight: function(){
56785         return this.minHeight;
56786     }
56787 });
56788
56789
56790 Roo.NorthLayoutRegion = function(mgr, config){
56791     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56792     if(this.split){
56793         this.split.placement = Roo.SplitBar.TOP;
56794         this.split.orientation = Roo.SplitBar.VERTICAL;
56795         this.split.el.addClass("x-layout-split-v");
56796     }
56797     var size = config.initialSize || config.height;
56798     if(typeof size != "undefined"){
56799         this.el.setHeight(size);
56800     }
56801 };
56802 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56803     orientation: Roo.SplitBar.VERTICAL,
56804     getBox : function(){
56805         if(this.collapsed){
56806             return this.collapsedEl.getBox();
56807         }
56808         var box = this.el.getBox();
56809         if(this.split){
56810             box.height += this.split.el.getHeight();
56811         }
56812         return box;
56813     },
56814     
56815     updateBox : function(box){
56816         if(this.split && !this.collapsed){
56817             box.height -= this.split.el.getHeight();
56818             this.split.el.setLeft(box.x);
56819             this.split.el.setTop(box.y+box.height);
56820             this.split.el.setWidth(box.width);
56821         }
56822         if(this.collapsed){
56823             this.updateBody(box.width, null);
56824         }
56825         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56826     }
56827 });
56828
56829 Roo.SouthLayoutRegion = function(mgr, config){
56830     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56831     if(this.split){
56832         this.split.placement = Roo.SplitBar.BOTTOM;
56833         this.split.orientation = Roo.SplitBar.VERTICAL;
56834         this.split.el.addClass("x-layout-split-v");
56835     }
56836     var size = config.initialSize || config.height;
56837     if(typeof size != "undefined"){
56838         this.el.setHeight(size);
56839     }
56840 };
56841 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56842     orientation: Roo.SplitBar.VERTICAL,
56843     getBox : function(){
56844         if(this.collapsed){
56845             return this.collapsedEl.getBox();
56846         }
56847         var box = this.el.getBox();
56848         if(this.split){
56849             var sh = this.split.el.getHeight();
56850             box.height += sh;
56851             box.y -= sh;
56852         }
56853         return box;
56854     },
56855     
56856     updateBox : function(box){
56857         if(this.split && !this.collapsed){
56858             var sh = this.split.el.getHeight();
56859             box.height -= sh;
56860             box.y += sh;
56861             this.split.el.setLeft(box.x);
56862             this.split.el.setTop(box.y-sh);
56863             this.split.el.setWidth(box.width);
56864         }
56865         if(this.collapsed){
56866             this.updateBody(box.width, null);
56867         }
56868         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56869     }
56870 });
56871
56872 Roo.EastLayoutRegion = function(mgr, config){
56873     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56874     if(this.split){
56875         this.split.placement = Roo.SplitBar.RIGHT;
56876         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56877         this.split.el.addClass("x-layout-split-h");
56878     }
56879     var size = config.initialSize || config.width;
56880     if(typeof size != "undefined"){
56881         this.el.setWidth(size);
56882     }
56883 };
56884 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56885     orientation: Roo.SplitBar.HORIZONTAL,
56886     getBox : function(){
56887         if(this.collapsed){
56888             return this.collapsedEl.getBox();
56889         }
56890         var box = this.el.getBox();
56891         if(this.split){
56892             var sw = this.split.el.getWidth();
56893             box.width += sw;
56894             box.x -= sw;
56895         }
56896         return box;
56897     },
56898
56899     updateBox : function(box){
56900         if(this.split && !this.collapsed){
56901             var sw = this.split.el.getWidth();
56902             box.width -= sw;
56903             this.split.el.setLeft(box.x);
56904             this.split.el.setTop(box.y);
56905             this.split.el.setHeight(box.height);
56906             box.x += sw;
56907         }
56908         if(this.collapsed){
56909             this.updateBody(null, box.height);
56910         }
56911         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56912     }
56913 });
56914
56915 Roo.WestLayoutRegion = function(mgr, config){
56916     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56917     if(this.split){
56918         this.split.placement = Roo.SplitBar.LEFT;
56919         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56920         this.split.el.addClass("x-layout-split-h");
56921     }
56922     var size = config.initialSize || config.width;
56923     if(typeof size != "undefined"){
56924         this.el.setWidth(size);
56925     }
56926 };
56927 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56928     orientation: Roo.SplitBar.HORIZONTAL,
56929     getBox : function(){
56930         if(this.collapsed){
56931             return this.collapsedEl.getBox();
56932         }
56933         var box = this.el.getBox();
56934         if(this.split){
56935             box.width += this.split.el.getWidth();
56936         }
56937         return box;
56938     },
56939     
56940     updateBox : function(box){
56941         if(this.split && !this.collapsed){
56942             var sw = this.split.el.getWidth();
56943             box.width -= sw;
56944             this.split.el.setLeft(box.x+box.width);
56945             this.split.el.setTop(box.y);
56946             this.split.el.setHeight(box.height);
56947         }
56948         if(this.collapsed){
56949             this.updateBody(null, box.height);
56950         }
56951         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56952     }
56953 });
56954 /*
56955  * Based on:
56956  * Ext JS Library 1.1.1
56957  * Copyright(c) 2006-2007, Ext JS, LLC.
56958  *
56959  * Originally Released Under LGPL - original licence link has changed is not relivant.
56960  *
56961  * Fork - LGPL
56962  * <script type="text/javascript">
56963  */
56964  
56965  
56966 /*
56967  * Private internal class for reading and applying state
56968  */
56969 Roo.LayoutStateManager = function(layout){
56970      // default empty state
56971      this.state = {
56972         north: {},
56973         south: {},
56974         east: {},
56975         west: {}       
56976     };
56977 };
56978
56979 Roo.LayoutStateManager.prototype = {
56980     init : function(layout, provider){
56981         this.provider = provider;
56982         var state = provider.get(layout.id+"-layout-state");
56983         if(state){
56984             var wasUpdating = layout.isUpdating();
56985             if(!wasUpdating){
56986                 layout.beginUpdate();
56987             }
56988             for(var key in state){
56989                 if(typeof state[key] != "function"){
56990                     var rstate = state[key];
56991                     var r = layout.getRegion(key);
56992                     if(r && rstate){
56993                         if(rstate.size){
56994                             r.resizeTo(rstate.size);
56995                         }
56996                         if(rstate.collapsed == true){
56997                             r.collapse(true);
56998                         }else{
56999                             r.expand(null, true);
57000                         }
57001                     }
57002                 }
57003             }
57004             if(!wasUpdating){
57005                 layout.endUpdate();
57006             }
57007             this.state = state; 
57008         }
57009         this.layout = layout;
57010         layout.on("regionresized", this.onRegionResized, this);
57011         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57012         layout.on("regionexpanded", this.onRegionExpanded, this);
57013     },
57014     
57015     storeState : function(){
57016         this.provider.set(this.layout.id+"-layout-state", this.state);
57017     },
57018     
57019     onRegionResized : function(region, newSize){
57020         this.state[region.getPosition()].size = newSize;
57021         this.storeState();
57022     },
57023     
57024     onRegionCollapsed : function(region){
57025         this.state[region.getPosition()].collapsed = true;
57026         this.storeState();
57027     },
57028     
57029     onRegionExpanded : function(region){
57030         this.state[region.getPosition()].collapsed = false;
57031         this.storeState();
57032     }
57033 };/*
57034  * Based on:
57035  * Ext JS Library 1.1.1
57036  * Copyright(c) 2006-2007, Ext JS, LLC.
57037  *
57038  * Originally Released Under LGPL - original licence link has changed is not relivant.
57039  *
57040  * Fork - LGPL
57041  * <script type="text/javascript">
57042  */
57043 /**
57044  * @class Roo.ContentPanel
57045  * @extends Roo.util.Observable
57046  * @children Roo.form.Form Roo.JsonView Roo.View
57047  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57048  * A basic ContentPanel element.
57049  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57050  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57051  * @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
57052  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57053  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57054  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57055  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57056  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57057  * @cfg {String} title          The title for this panel
57058  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57059  * @cfg {String} url            Calls {@link #setUrl} with this value
57060  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57061  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57062  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57063  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57064  * @cfg {String}    style  Extra style to add to the content panel
57065  * @cfg {Roo.menu.Menu} menu  popup menu
57066
57067  * @constructor
57068  * Create a new ContentPanel.
57069  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57070  * @param {String/Object} config A string to set only the title or a config object
57071  * @param {String} content (optional) Set the HTML content for this panel
57072  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57073  */
57074 Roo.ContentPanel = function(el, config, content){
57075     
57076      
57077     /*
57078     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57079         config = el;
57080         el = Roo.id();
57081     }
57082     if (config && config.parentLayout) { 
57083         el = config.parentLayout.el.createChild(); 
57084     }
57085     */
57086     if(el.autoCreate){ // xtype is available if this is called from factory
57087         config = el;
57088         el = Roo.id();
57089     }
57090     this.el = Roo.get(el);
57091     if(!this.el && config && config.autoCreate){
57092         if(typeof config.autoCreate == "object"){
57093             if(!config.autoCreate.id){
57094                 config.autoCreate.id = config.id||el;
57095             }
57096             this.el = Roo.DomHelper.append(document.body,
57097                         config.autoCreate, true);
57098         }else{
57099             this.el = Roo.DomHelper.append(document.body,
57100                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57101         }
57102     }
57103     
57104     
57105     this.closable = false;
57106     this.loaded = false;
57107     this.active = false;
57108     if(typeof config == "string"){
57109         this.title = config;
57110     }else{
57111         Roo.apply(this, config);
57112     }
57113     
57114     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57115         this.wrapEl = this.el.wrap();
57116         this.toolbar.container = this.el.insertSibling(false, 'before');
57117         this.toolbar = new Roo.Toolbar(this.toolbar);
57118     }
57119     
57120     // xtype created footer. - not sure if will work as we normally have to render first..
57121     if (this.footer && !this.footer.el && this.footer.xtype) {
57122         if (!this.wrapEl) {
57123             this.wrapEl = this.el.wrap();
57124         }
57125     
57126         this.footer.container = this.wrapEl.createChild();
57127          
57128         this.footer = Roo.factory(this.footer, Roo);
57129         
57130     }
57131     
57132     if(this.resizeEl){
57133         this.resizeEl = Roo.get(this.resizeEl, true);
57134     }else{
57135         this.resizeEl = this.el;
57136     }
57137     // handle view.xtype
57138     
57139  
57140     
57141     
57142     this.addEvents({
57143         /**
57144          * @event activate
57145          * Fires when this panel is activated. 
57146          * @param {Roo.ContentPanel} this
57147          */
57148         "activate" : true,
57149         /**
57150          * @event deactivate
57151          * Fires when this panel is activated. 
57152          * @param {Roo.ContentPanel} this
57153          */
57154         "deactivate" : true,
57155
57156         /**
57157          * @event resize
57158          * Fires when this panel is resized if fitToFrame is true.
57159          * @param {Roo.ContentPanel} this
57160          * @param {Number} width The width after any component adjustments
57161          * @param {Number} height The height after any component adjustments
57162          */
57163         "resize" : true,
57164         
57165          /**
57166          * @event render
57167          * Fires when this tab is created
57168          * @param {Roo.ContentPanel} this
57169          */
57170         "render" : true
57171          
57172         
57173     });
57174     
57175
57176     
57177     
57178     if(this.autoScroll){
57179         this.resizeEl.setStyle("overflow", "auto");
57180     } else {
57181         // fix randome scrolling
57182         this.el.on('scroll', function() {
57183             Roo.log('fix random scolling');
57184             this.scrollTo('top',0); 
57185         });
57186     }
57187     content = content || this.content;
57188     if(content){
57189         this.setContent(content);
57190     }
57191     if(config && config.url){
57192         this.setUrl(this.url, this.params, this.loadOnce);
57193     }
57194     
57195     
57196     
57197     Roo.ContentPanel.superclass.constructor.call(this);
57198     
57199     if (this.view && typeof(this.view.xtype) != 'undefined') {
57200         this.view.el = this.el.appendChild(document.createElement("div"));
57201         this.view = Roo.factory(this.view); 
57202         this.view.render  &&  this.view.render(false, '');  
57203     }
57204     
57205     
57206     this.fireEvent('render', this);
57207 };
57208
57209 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57210     tabTip:'',
57211     setRegion : function(region){
57212         this.region = region;
57213         if(region){
57214            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57215         }else{
57216            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57217         } 
57218     },
57219     
57220     /**
57221      * Returns the toolbar for this Panel if one was configured. 
57222      * @return {Roo.Toolbar} 
57223      */
57224     getToolbar : function(){
57225         return this.toolbar;
57226     },
57227     
57228     setActiveState : function(active){
57229         this.active = active;
57230         if(!active){
57231             this.fireEvent("deactivate", this);
57232         }else{
57233             this.fireEvent("activate", this);
57234         }
57235     },
57236     /**
57237      * Updates this panel's element
57238      * @param {String} content The new content
57239      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57240     */
57241     setContent : function(content, loadScripts){
57242         this.el.update(content, loadScripts);
57243     },
57244
57245     ignoreResize : function(w, h){
57246         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57247             return true;
57248         }else{
57249             this.lastSize = {width: w, height: h};
57250             return false;
57251         }
57252     },
57253     /**
57254      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57255      * @return {Roo.UpdateManager} The UpdateManager
57256      */
57257     getUpdateManager : function(){
57258         return this.el.getUpdateManager();
57259     },
57260      /**
57261      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57262      * @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:
57263 <pre><code>
57264 panel.load({
57265     url: "your-url.php",
57266     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57267     callback: yourFunction,
57268     scope: yourObject, //(optional scope)
57269     discardUrl: false,
57270     nocache: false,
57271     text: "Loading...",
57272     timeout: 30,
57273     scripts: false
57274 });
57275 </code></pre>
57276      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57277      * 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.
57278      * @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}
57279      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57280      * @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.
57281      * @return {Roo.ContentPanel} this
57282      */
57283     load : function(){
57284         var um = this.el.getUpdateManager();
57285         um.update.apply(um, arguments);
57286         return this;
57287     },
57288
57289
57290     /**
57291      * 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.
57292      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57293      * @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)
57294      * @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)
57295      * @return {Roo.UpdateManager} The UpdateManager
57296      */
57297     setUrl : function(url, params, loadOnce){
57298         if(this.refreshDelegate){
57299             this.removeListener("activate", this.refreshDelegate);
57300         }
57301         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57302         this.on("activate", this.refreshDelegate);
57303         return this.el.getUpdateManager();
57304     },
57305     
57306     _handleRefresh : function(url, params, loadOnce){
57307         if(!loadOnce || !this.loaded){
57308             var updater = this.el.getUpdateManager();
57309             updater.update(url, params, this._setLoaded.createDelegate(this));
57310         }
57311     },
57312     
57313     _setLoaded : function(){
57314         this.loaded = true;
57315     }, 
57316     
57317     /**
57318      * Returns this panel's id
57319      * @return {String} 
57320      */
57321     getId : function(){
57322         return this.el.id;
57323     },
57324     
57325     /** 
57326      * Returns this panel's element - used by regiosn to add.
57327      * @return {Roo.Element} 
57328      */
57329     getEl : function(){
57330         return this.wrapEl || this.el;
57331     },
57332     
57333     adjustForComponents : function(width, height)
57334     {
57335         //Roo.log('adjustForComponents ');
57336         if(this.resizeEl != this.el){
57337             width -= this.el.getFrameWidth('lr');
57338             height -= this.el.getFrameWidth('tb');
57339         }
57340         if(this.toolbar){
57341             var te = this.toolbar.getEl();
57342             height -= te.getHeight();
57343             te.setWidth(width);
57344         }
57345         if(this.footer){
57346             var te = this.footer.getEl();
57347             //Roo.log("footer:" + te.getHeight());
57348             
57349             height -= te.getHeight();
57350             te.setWidth(width);
57351         }
57352         
57353         
57354         if(this.adjustments){
57355             width += this.adjustments[0];
57356             height += this.adjustments[1];
57357         }
57358         return {"width": width, "height": height};
57359     },
57360     
57361     setSize : function(width, height){
57362         if(this.fitToFrame && !this.ignoreResize(width, height)){
57363             if(this.fitContainer && this.resizeEl != this.el){
57364                 this.el.setSize(width, height);
57365             }
57366             var size = this.adjustForComponents(width, height);
57367             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57368             this.fireEvent('resize', this, size.width, size.height);
57369         }
57370     },
57371     
57372     /**
57373      * Returns this panel's title
57374      * @return {String} 
57375      */
57376     getTitle : function(){
57377         return this.title;
57378     },
57379     
57380     /**
57381      * Set this panel's title
57382      * @param {String} title
57383      */
57384     setTitle : function(title){
57385         this.title = title;
57386         if(this.region){
57387             this.region.updatePanelTitle(this, title);
57388         }
57389     },
57390     
57391     /**
57392      * Returns true is this panel was configured to be closable
57393      * @return {Boolean} 
57394      */
57395     isClosable : function(){
57396         return this.closable;
57397     },
57398     
57399     beforeSlide : function(){
57400         this.el.clip();
57401         this.resizeEl.clip();
57402     },
57403     
57404     afterSlide : function(){
57405         this.el.unclip();
57406         this.resizeEl.unclip();
57407     },
57408     
57409     /**
57410      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57411      *   Will fail silently if the {@link #setUrl} method has not been called.
57412      *   This does not activate the panel, just updates its content.
57413      */
57414     refresh : function(){
57415         if(this.refreshDelegate){
57416            this.loaded = false;
57417            this.refreshDelegate();
57418         }
57419     },
57420     
57421     /**
57422      * Destroys this panel
57423      */
57424     destroy : function(){
57425         this.el.removeAllListeners();
57426         var tempEl = document.createElement("span");
57427         tempEl.appendChild(this.el.dom);
57428         tempEl.innerHTML = "";
57429         this.el.remove();
57430         this.el = null;
57431     },
57432     
57433     /**
57434      * form - if the content panel contains a form - this is a reference to it.
57435      * @type {Roo.form.Form}
57436      */
57437     form : false,
57438     /**
57439      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57440      *    This contains a reference to it.
57441      * @type {Roo.View}
57442      */
57443     view : false,
57444     
57445       /**
57446      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57447      * <pre><code>
57448
57449 layout.addxtype({
57450        xtype : 'Form',
57451        items: [ .... ]
57452    }
57453 );
57454
57455 </code></pre>
57456      * @param {Object} cfg Xtype definition of item to add.
57457      */
57458     
57459     addxtype : function(cfg) {
57460         // add form..
57461         if (cfg.xtype.match(/^Form$/)) {
57462             
57463             var el;
57464             //if (this.footer) {
57465             //    el = this.footer.container.insertSibling(false, 'before');
57466             //} else {
57467                 el = this.el.createChild();
57468             //}
57469
57470             this.form = new  Roo.form.Form(cfg);
57471             
57472             
57473             if ( this.form.allItems.length) {
57474                 this.form.render(el.dom);
57475             }
57476             return this.form;
57477         }
57478         // should only have one of theses..
57479         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57480             // views.. should not be just added - used named prop 'view''
57481             
57482             cfg.el = this.el.appendChild(document.createElement("div"));
57483             // factory?
57484             
57485             var ret = new Roo.factory(cfg);
57486              
57487              ret.render && ret.render(false, ''); // render blank..
57488             this.view = ret;
57489             return ret;
57490         }
57491         return false;
57492     }
57493 });
57494
57495 /**
57496  * @class Roo.GridPanel
57497  * @extends Roo.ContentPanel
57498  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57499  * @constructor
57500  * Create a new GridPanel.
57501  * @cfg {Roo.grid.Grid} grid The grid for this panel
57502  */
57503 Roo.GridPanel = function(grid, config){
57504     
57505     // universal ctor...
57506     if (typeof(grid.grid) != 'undefined') {
57507         config = grid;
57508         grid = config.grid;
57509     }
57510     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57511         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57512         
57513     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57514     
57515     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57516     
57517     if(this.toolbar){
57518         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57519     }
57520     // xtype created footer. - not sure if will work as we normally have to render first..
57521     if (this.footer && !this.footer.el && this.footer.xtype) {
57522         
57523         this.footer.container = this.grid.getView().getFooterPanel(true);
57524         this.footer.dataSource = this.grid.dataSource;
57525         this.footer = Roo.factory(this.footer, Roo);
57526         
57527     }
57528     
57529     grid.monitorWindowResize = false; // turn off autosizing
57530     grid.autoHeight = false;
57531     grid.autoWidth = false;
57532     this.grid = grid;
57533     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57534 };
57535
57536 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57537     getId : function(){
57538         return this.grid.id;
57539     },
57540     
57541     /**
57542      * Returns the grid for this panel
57543      * @return {Roo.grid.Grid} 
57544      */
57545     getGrid : function(){
57546         return this.grid;    
57547     },
57548     
57549     setSize : function(width, height){
57550         if(!this.ignoreResize(width, height)){
57551             var grid = this.grid;
57552             var size = this.adjustForComponents(width, height);
57553             grid.getGridEl().setSize(size.width, size.height);
57554             grid.autoSize();
57555         }
57556     },
57557     
57558     beforeSlide : function(){
57559         this.grid.getView().scroller.clip();
57560     },
57561     
57562     afterSlide : function(){
57563         this.grid.getView().scroller.unclip();
57564     },
57565     
57566     destroy : function(){
57567         this.grid.destroy();
57568         delete this.grid;
57569         Roo.GridPanel.superclass.destroy.call(this); 
57570     }
57571 });
57572
57573
57574 /**
57575  * @class Roo.NestedLayoutPanel
57576  * @extends Roo.ContentPanel
57577  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57578  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57579  *
57580  * 
57581  * @constructor
57582  * Create a new NestedLayoutPanel.
57583  * 
57584  * 
57585  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57586  * @param {String/Object} config A string to set only the title or a config object
57587  */
57588 Roo.NestedLayoutPanel = function(layout, config)
57589 {
57590     // construct with only one argument..
57591     /* FIXME - implement nicer consturctors
57592     if (layout.layout) {
57593         config = layout;
57594         layout = config.layout;
57595         delete config.layout;
57596     }
57597     if (layout.xtype && !layout.getEl) {
57598         // then layout needs constructing..
57599         layout = Roo.factory(layout, Roo);
57600     }
57601     */
57602     
57603     
57604     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57605     
57606     layout.monitorWindowResize = false; // turn off autosizing
57607     this.layout = layout;
57608     this.layout.getEl().addClass("x-layout-nested-layout");
57609     
57610     
57611     
57612     
57613 };
57614
57615 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57616
57617     setSize : function(width, height){
57618         if(!this.ignoreResize(width, height)){
57619             var size = this.adjustForComponents(width, height);
57620             var el = this.layout.getEl();
57621             el.setSize(size.width, size.height);
57622             var touch = el.dom.offsetWidth;
57623             this.layout.layout();
57624             // ie requires a double layout on the first pass
57625             if(Roo.isIE && !this.initialized){
57626                 this.initialized = true;
57627                 this.layout.layout();
57628             }
57629         }
57630     },
57631     
57632     // activate all subpanels if not currently active..
57633     
57634     setActiveState : function(active){
57635         this.active = active;
57636         if(!active){
57637             this.fireEvent("deactivate", this);
57638             return;
57639         }
57640         
57641         this.fireEvent("activate", this);
57642         // not sure if this should happen before or after..
57643         if (!this.layout) {
57644             return; // should not happen..
57645         }
57646         var reg = false;
57647         for (var r in this.layout.regions) {
57648             reg = this.layout.getRegion(r);
57649             if (reg.getActivePanel()) {
57650                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57651                 reg.setActivePanel(reg.getActivePanel());
57652                 continue;
57653             }
57654             if (!reg.panels.length) {
57655                 continue;
57656             }
57657             reg.showPanel(reg.getPanel(0));
57658         }
57659         
57660         
57661         
57662         
57663     },
57664     
57665     /**
57666      * Returns the nested BorderLayout for this panel
57667      * @return {Roo.BorderLayout} 
57668      */
57669     getLayout : function(){
57670         return this.layout;
57671     },
57672     
57673      /**
57674      * Adds a xtype elements to the layout of the nested panel
57675      * <pre><code>
57676
57677 panel.addxtype({
57678        xtype : 'ContentPanel',
57679        region: 'west',
57680        items: [ .... ]
57681    }
57682 );
57683
57684 panel.addxtype({
57685         xtype : 'NestedLayoutPanel',
57686         region: 'west',
57687         layout: {
57688            center: { },
57689            west: { }   
57690         },
57691         items : [ ... list of content panels or nested layout panels.. ]
57692    }
57693 );
57694 </code></pre>
57695      * @param {Object} cfg Xtype definition of item to add.
57696      */
57697     addxtype : function(cfg) {
57698         return this.layout.addxtype(cfg);
57699     
57700     }
57701 });
57702
57703 Roo.ScrollPanel = function(el, config, content){
57704     config = config || {};
57705     config.fitToFrame = true;
57706     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57707     
57708     this.el.dom.style.overflow = "hidden";
57709     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57710     this.el.removeClass("x-layout-inactive-content");
57711     this.el.on("mousewheel", this.onWheel, this);
57712
57713     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57714     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57715     up.unselectable(); down.unselectable();
57716     up.on("click", this.scrollUp, this);
57717     down.on("click", this.scrollDown, this);
57718     up.addClassOnOver("x-scroller-btn-over");
57719     down.addClassOnOver("x-scroller-btn-over");
57720     up.addClassOnClick("x-scroller-btn-click");
57721     down.addClassOnClick("x-scroller-btn-click");
57722     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57723
57724     this.resizeEl = this.el;
57725     this.el = wrap; this.up = up; this.down = down;
57726 };
57727
57728 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57729     increment : 100,
57730     wheelIncrement : 5,
57731     scrollUp : function(){
57732         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57733     },
57734
57735     scrollDown : function(){
57736         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57737     },
57738
57739     afterScroll : function(){
57740         var el = this.resizeEl;
57741         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57742         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57743         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57744     },
57745
57746     setSize : function(){
57747         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57748         this.afterScroll();
57749     },
57750
57751     onWheel : function(e){
57752         var d = e.getWheelDelta();
57753         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57754         this.afterScroll();
57755         e.stopEvent();
57756     },
57757
57758     setContent : function(content, loadScripts){
57759         this.resizeEl.update(content, loadScripts);
57760     }
57761
57762 });
57763
57764
57765
57766 /**
57767  * @class Roo.TreePanel
57768  * @extends Roo.ContentPanel
57769  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57770  * Treepanel component
57771  * 
57772  * @constructor
57773  * Create a new TreePanel. - defaults to fit/scoll contents.
57774  * @param {String/Object} config A string to set only the panel's title, or a config object
57775  */
57776 Roo.TreePanel = function(config){
57777     var el = config.el;
57778     var tree = config.tree;
57779     delete config.tree; 
57780     delete config.el; // hopefull!
57781     
57782     // wrapper for IE7 strict & safari scroll issue
57783     
57784     var treeEl = el.createChild();
57785     config.resizeEl = treeEl;
57786     
57787     
57788     
57789     Roo.TreePanel.superclass.constructor.call(this, el, config);
57790  
57791  
57792     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57793     //console.log(tree);
57794     this.on('activate', function()
57795     {
57796         if (this.tree.rendered) {
57797             return;
57798         }
57799         //console.log('render tree');
57800         this.tree.render();
57801     });
57802     // this should not be needed.. - it's actually the 'el' that resizes?
57803     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57804     
57805     //this.on('resize',  function (cp, w, h) {
57806     //        this.tree.innerCt.setWidth(w);
57807     //        this.tree.innerCt.setHeight(h);
57808     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57809     //});
57810
57811         
57812     
57813 };
57814
57815 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57816     fitToFrame : true,
57817     autoScroll : true,
57818     /*
57819      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57820      */
57821     tree : false
57822
57823 });
57824
57825
57826
57827
57828
57829
57830
57831
57832
57833
57834
57835 /*
57836  * Based on:
57837  * Ext JS Library 1.1.1
57838  * Copyright(c) 2006-2007, Ext JS, LLC.
57839  *
57840  * Originally Released Under LGPL - original licence link has changed is not relivant.
57841  *
57842  * Fork - LGPL
57843  * <script type="text/javascript">
57844  */
57845  
57846
57847 /**
57848  * @class Roo.ReaderLayout
57849  * @extends Roo.BorderLayout
57850  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57851  * center region containing two nested regions (a top one for a list view and one for item preview below),
57852  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57853  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57854  * expedites the setup of the overall layout and regions for this common application style.
57855  * Example:
57856  <pre><code>
57857 var reader = new Roo.ReaderLayout();
57858 var CP = Roo.ContentPanel;  // shortcut for adding
57859
57860 reader.beginUpdate();
57861 reader.add("north", new CP("north", "North"));
57862 reader.add("west", new CP("west", {title: "West"}));
57863 reader.add("east", new CP("east", {title: "East"}));
57864
57865 reader.regions.listView.add(new CP("listView", "List"));
57866 reader.regions.preview.add(new CP("preview", "Preview"));
57867 reader.endUpdate();
57868 </code></pre>
57869 * @constructor
57870 * Create a new ReaderLayout
57871 * @param {Object} config Configuration options
57872 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57873 * document.body if omitted)
57874 */
57875 Roo.ReaderLayout = function(config, renderTo){
57876     var c = config || {size:{}};
57877     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57878         north: c.north !== false ? Roo.apply({
57879             split:false,
57880             initialSize: 32,
57881             titlebar: false
57882         }, c.north) : false,
57883         west: c.west !== false ? Roo.apply({
57884             split:true,
57885             initialSize: 200,
57886             minSize: 175,
57887             maxSize: 400,
57888             titlebar: true,
57889             collapsible: true,
57890             animate: true,
57891             margins:{left:5,right:0,bottom:5,top:5},
57892             cmargins:{left:5,right:5,bottom:5,top:5}
57893         }, c.west) : false,
57894         east: c.east !== false ? Roo.apply({
57895             split:true,
57896             initialSize: 200,
57897             minSize: 175,
57898             maxSize: 400,
57899             titlebar: true,
57900             collapsible: true,
57901             animate: true,
57902             margins:{left:0,right:5,bottom:5,top:5},
57903             cmargins:{left:5,right:5,bottom:5,top:5}
57904         }, c.east) : false,
57905         center: Roo.apply({
57906             tabPosition: 'top',
57907             autoScroll:false,
57908             closeOnTab: true,
57909             titlebar:false,
57910             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57911         }, c.center)
57912     });
57913
57914     this.el.addClass('x-reader');
57915
57916     this.beginUpdate();
57917
57918     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57919         south: c.preview !== false ? Roo.apply({
57920             split:true,
57921             initialSize: 200,
57922             minSize: 100,
57923             autoScroll:true,
57924             collapsible:true,
57925             titlebar: true,
57926             cmargins:{top:5,left:0, right:0, bottom:0}
57927         }, c.preview) : false,
57928         center: Roo.apply({
57929             autoScroll:false,
57930             titlebar:false,
57931             minHeight:200
57932         }, c.listView)
57933     });
57934     this.add('center', new Roo.NestedLayoutPanel(inner,
57935             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57936
57937     this.endUpdate();
57938
57939     this.regions.preview = inner.getRegion('south');
57940     this.regions.listView = inner.getRegion('center');
57941 };
57942
57943 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57944  * Based on:
57945  * Ext JS Library 1.1.1
57946  * Copyright(c) 2006-2007, Ext JS, LLC.
57947  *
57948  * Originally Released Under LGPL - original licence link has changed is not relivant.
57949  *
57950  * Fork - LGPL
57951  * <script type="text/javascript">
57952  */
57953  
57954 /**
57955  * @class Roo.grid.Grid
57956  * @extends Roo.util.Observable
57957  * This class represents the primary interface of a component based grid control.
57958  * <br><br>Usage:<pre><code>
57959  var grid = new Roo.grid.Grid("my-container-id", {
57960      ds: myDataStore,
57961      cm: myColModel,
57962      selModel: mySelectionModel,
57963      autoSizeColumns: true,
57964      monitorWindowResize: false,
57965      trackMouseOver: true
57966  });
57967  // set any options
57968  grid.render();
57969  * </code></pre>
57970  * <b>Common Problems:</b><br/>
57971  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57972  * element will correct this<br/>
57973  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57974  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57975  * are unpredictable.<br/>
57976  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57977  * grid to calculate dimensions/offsets.<br/>
57978   * @constructor
57979  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57980  * The container MUST have some type of size defined for the grid to fill. The container will be
57981  * automatically set to position relative if it isn't already.
57982  * @param {Object} config A config object that sets properties on this grid.
57983  */
57984 Roo.grid.Grid = function(container, config){
57985         // initialize the container
57986         this.container = Roo.get(container);
57987         this.container.update("");
57988         this.container.setStyle("overflow", "hidden");
57989     this.container.addClass('x-grid-container');
57990
57991     this.id = this.container.id;
57992
57993     Roo.apply(this, config);
57994     // check and correct shorthanded configs
57995     if(this.ds){
57996         this.dataSource = this.ds;
57997         delete this.ds;
57998     }
57999     if(this.cm){
58000         this.colModel = this.cm;
58001         delete this.cm;
58002     }
58003     if(this.sm){
58004         this.selModel = this.sm;
58005         delete this.sm;
58006     }
58007
58008     if (this.selModel) {
58009         this.selModel = Roo.factory(this.selModel, Roo.grid);
58010         this.sm = this.selModel;
58011         this.sm.xmodule = this.xmodule || false;
58012     }
58013     if (typeof(this.colModel.config) == 'undefined') {
58014         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58015         this.cm = this.colModel;
58016         this.cm.xmodule = this.xmodule || false;
58017     }
58018     if (this.dataSource) {
58019         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58020         this.ds = this.dataSource;
58021         this.ds.xmodule = this.xmodule || false;
58022          
58023     }
58024     
58025     
58026     
58027     if(this.width){
58028         this.container.setWidth(this.width);
58029     }
58030
58031     if(this.height){
58032         this.container.setHeight(this.height);
58033     }
58034     /** @private */
58035         this.addEvents({
58036         // raw events
58037         /**
58038          * @event click
58039          * The raw click event for the entire grid.
58040          * @param {Roo.EventObject} e
58041          */
58042         "click" : true,
58043         /**
58044          * @event dblclick
58045          * The raw dblclick event for the entire grid.
58046          * @param {Roo.EventObject} e
58047          */
58048         "dblclick" : true,
58049         /**
58050          * @event contextmenu
58051          * The raw contextmenu event for the entire grid.
58052          * @param {Roo.EventObject} e
58053          */
58054         "contextmenu" : true,
58055         /**
58056          * @event mousedown
58057          * The raw mousedown event for the entire grid.
58058          * @param {Roo.EventObject} e
58059          */
58060         "mousedown" : true,
58061         /**
58062          * @event mouseup
58063          * The raw mouseup event for the entire grid.
58064          * @param {Roo.EventObject} e
58065          */
58066         "mouseup" : true,
58067         /**
58068          * @event mouseover
58069          * The raw mouseover event for the entire grid.
58070          * @param {Roo.EventObject} e
58071          */
58072         "mouseover" : true,
58073         /**
58074          * @event mouseout
58075          * The raw mouseout event for the entire grid.
58076          * @param {Roo.EventObject} e
58077          */
58078         "mouseout" : true,
58079         /**
58080          * @event keypress
58081          * The raw keypress event for the entire grid.
58082          * @param {Roo.EventObject} e
58083          */
58084         "keypress" : true,
58085         /**
58086          * @event keydown
58087          * The raw keydown event for the entire grid.
58088          * @param {Roo.EventObject} e
58089          */
58090         "keydown" : true,
58091
58092         // custom events
58093
58094         /**
58095          * @event cellclick
58096          * Fires when a cell is clicked
58097          * @param {Grid} this
58098          * @param {Number} rowIndex
58099          * @param {Number} columnIndex
58100          * @param {Roo.EventObject} e
58101          */
58102         "cellclick" : true,
58103         /**
58104          * @event celldblclick
58105          * Fires when a cell is double clicked
58106          * @param {Grid} this
58107          * @param {Number} rowIndex
58108          * @param {Number} columnIndex
58109          * @param {Roo.EventObject} e
58110          */
58111         "celldblclick" : true,
58112         /**
58113          * @event rowclick
58114          * Fires when a row is clicked
58115          * @param {Grid} this
58116          * @param {Number} rowIndex
58117          * @param {Roo.EventObject} e
58118          */
58119         "rowclick" : true,
58120         /**
58121          * @event rowdblclick
58122          * Fires when a row is double clicked
58123          * @param {Grid} this
58124          * @param {Number} rowIndex
58125          * @param {Roo.EventObject} e
58126          */
58127         "rowdblclick" : true,
58128         /**
58129          * @event headerclick
58130          * Fires when a header is clicked
58131          * @param {Grid} this
58132          * @param {Number} columnIndex
58133          * @param {Roo.EventObject} e
58134          */
58135         "headerclick" : true,
58136         /**
58137          * @event headerdblclick
58138          * Fires when a header cell is double clicked
58139          * @param {Grid} this
58140          * @param {Number} columnIndex
58141          * @param {Roo.EventObject} e
58142          */
58143         "headerdblclick" : true,
58144         /**
58145          * @event rowcontextmenu
58146          * Fires when a row is right clicked
58147          * @param {Grid} this
58148          * @param {Number} rowIndex
58149          * @param {Roo.EventObject} e
58150          */
58151         "rowcontextmenu" : true,
58152         /**
58153          * @event cellcontextmenu
58154          * Fires when a cell is right clicked
58155          * @param {Grid} this
58156          * @param {Number} rowIndex
58157          * @param {Number} cellIndex
58158          * @param {Roo.EventObject} e
58159          */
58160          "cellcontextmenu" : true,
58161         /**
58162          * @event headercontextmenu
58163          * Fires when a header is right clicked
58164          * @param {Grid} this
58165          * @param {Number} columnIndex
58166          * @param {Roo.EventObject} e
58167          */
58168         "headercontextmenu" : true,
58169         /**
58170          * @event bodyscroll
58171          * Fires when the body element is scrolled
58172          * @param {Number} scrollLeft
58173          * @param {Number} scrollTop
58174          */
58175         "bodyscroll" : true,
58176         /**
58177          * @event columnresize
58178          * Fires when the user resizes a column
58179          * @param {Number} columnIndex
58180          * @param {Number} newSize
58181          */
58182         "columnresize" : true,
58183         /**
58184          * @event columnmove
58185          * Fires when the user moves a column
58186          * @param {Number} oldIndex
58187          * @param {Number} newIndex
58188          */
58189         "columnmove" : true,
58190         /**
58191          * @event startdrag
58192          * Fires when row(s) start being dragged
58193          * @param {Grid} this
58194          * @param {Roo.GridDD} dd The drag drop object
58195          * @param {event} e The raw browser event
58196          */
58197         "startdrag" : true,
58198         /**
58199          * @event enddrag
58200          * Fires when a drag operation is complete
58201          * @param {Grid} this
58202          * @param {Roo.GridDD} dd The drag drop object
58203          * @param {event} e The raw browser event
58204          */
58205         "enddrag" : true,
58206         /**
58207          * @event dragdrop
58208          * Fires when dragged row(s) are dropped on a valid DD target
58209          * @param {Grid} this
58210          * @param {Roo.GridDD} dd The drag drop object
58211          * @param {String} targetId The target drag drop object
58212          * @param {event} e The raw browser event
58213          */
58214         "dragdrop" : true,
58215         /**
58216          * @event dragover
58217          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58218          * @param {Grid} this
58219          * @param {Roo.GridDD} dd The drag drop object
58220          * @param {String} targetId The target drag drop object
58221          * @param {event} e The raw browser event
58222          */
58223         "dragover" : true,
58224         /**
58225          * @event dragenter
58226          *  Fires when the dragged row(s) first cross another DD target while being dragged
58227          * @param {Grid} this
58228          * @param {Roo.GridDD} dd The drag drop object
58229          * @param {String} targetId The target drag drop object
58230          * @param {event} e The raw browser event
58231          */
58232         "dragenter" : true,
58233         /**
58234          * @event dragout
58235          * Fires when the dragged row(s) leave another DD target while being dragged
58236          * @param {Grid} this
58237          * @param {Roo.GridDD} dd The drag drop object
58238          * @param {String} targetId The target drag drop object
58239          * @param {event} e The raw browser event
58240          */
58241         "dragout" : true,
58242         /**
58243          * @event rowclass
58244          * Fires when a row is rendered, so you can change add a style to it.
58245          * @param {GridView} gridview   The grid view
58246          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58247          */
58248         'rowclass' : true,
58249
58250         /**
58251          * @event render
58252          * Fires when the grid is rendered
58253          * @param {Grid} grid
58254          */
58255         'render' : true
58256     });
58257
58258     Roo.grid.Grid.superclass.constructor.call(this);
58259 };
58260 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58261     
58262     /**
58263          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58264          */
58265         /**
58266          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58267          */
58268         /**
58269          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58270          */
58271         /**
58272          * @cfg {Roo.grid.Store} ds The data store for the grid
58273          */
58274         /**
58275          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58276          */
58277         /**
58278      * @cfg {String} ddGroup - drag drop group.
58279      */
58280       /**
58281      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58282      */
58283
58284     /**
58285      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58286      */
58287     minColumnWidth : 25,
58288
58289     /**
58290      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58291      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58292      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58293      */
58294     autoSizeColumns : false,
58295
58296     /**
58297      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58298      */
58299     autoSizeHeaders : true,
58300
58301     /**
58302      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58303      */
58304     monitorWindowResize : true,
58305
58306     /**
58307      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58308      * rows measured to get a columns size. Default is 0 (all rows).
58309      */
58310     maxRowsToMeasure : 0,
58311
58312     /**
58313      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58314      */
58315     trackMouseOver : true,
58316
58317     /**
58318     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58319     */
58320       /**
58321     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58322     */
58323     
58324     /**
58325     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58326     */
58327     enableDragDrop : false,
58328     
58329     /**
58330     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58331     */
58332     enableColumnMove : true,
58333     
58334     /**
58335     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58336     */
58337     enableColumnHide : true,
58338     
58339     /**
58340     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58341     */
58342     enableRowHeightSync : false,
58343     
58344     /**
58345     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58346     */
58347     stripeRows : true,
58348     
58349     /**
58350     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58351     */
58352     autoHeight : false,
58353
58354     /**
58355      * @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.
58356      */
58357     autoExpandColumn : false,
58358
58359     /**
58360     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58361     * Default is 50.
58362     */
58363     autoExpandMin : 50,
58364
58365     /**
58366     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58367     */
58368     autoExpandMax : 1000,
58369
58370     /**
58371     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58372     */
58373     view : null,
58374
58375     /**
58376     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58377     */
58378     loadMask : false,
58379     /**
58380     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58381     */
58382     dropTarget: false,
58383      /**
58384     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58385     */ 
58386     sortColMenu : false,
58387     
58388     // private
58389     rendered : false,
58390
58391     /**
58392     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58393     * of a fixed width. Default is false.
58394     */
58395     /**
58396     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58397     */
58398     
58399     
58400     /**
58401     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58402     * %0 is replaced with the number of selected rows.
58403     */
58404     ddText : "{0} selected row{1}",
58405     
58406     
58407     /**
58408      * Called once after all setup has been completed and the grid is ready to be rendered.
58409      * @return {Roo.grid.Grid} this
58410      */
58411     render : function()
58412     {
58413         var c = this.container;
58414         // try to detect autoHeight/width mode
58415         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58416             this.autoHeight = true;
58417         }
58418         var view = this.getView();
58419         view.init(this);
58420
58421         c.on("click", this.onClick, this);
58422         c.on("dblclick", this.onDblClick, this);
58423         c.on("contextmenu", this.onContextMenu, this);
58424         c.on("keydown", this.onKeyDown, this);
58425         if (Roo.isTouch) {
58426             c.on("touchstart", this.onTouchStart, this);
58427         }
58428
58429         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58430
58431         this.getSelectionModel().init(this);
58432
58433         view.render();
58434
58435         if(this.loadMask){
58436             this.loadMask = new Roo.LoadMask(this.container,
58437                     Roo.apply({store:this.dataSource}, this.loadMask));
58438         }
58439         
58440         
58441         if (this.toolbar && this.toolbar.xtype) {
58442             this.toolbar.container = this.getView().getHeaderPanel(true);
58443             this.toolbar = new Roo.Toolbar(this.toolbar);
58444         }
58445         if (this.footer && this.footer.xtype) {
58446             this.footer.dataSource = this.getDataSource();
58447             this.footer.container = this.getView().getFooterPanel(true);
58448             this.footer = Roo.factory(this.footer, Roo);
58449         }
58450         if (this.dropTarget && this.dropTarget.xtype) {
58451             delete this.dropTarget.xtype;
58452             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58453         }
58454         
58455         
58456         this.rendered = true;
58457         this.fireEvent('render', this);
58458         return this;
58459     },
58460
58461     /**
58462      * Reconfigures the grid to use a different Store and Column Model.
58463      * The View will be bound to the new objects and refreshed.
58464      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58465      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58466      */
58467     reconfigure : function(dataSource, colModel){
58468         if(this.loadMask){
58469             this.loadMask.destroy();
58470             this.loadMask = new Roo.LoadMask(this.container,
58471                     Roo.apply({store:dataSource}, this.loadMask));
58472         }
58473         this.view.bind(dataSource, colModel);
58474         this.dataSource = dataSource;
58475         this.colModel = colModel;
58476         this.view.refresh(true);
58477     },
58478     /**
58479      * addColumns
58480      * Add's a column, default at the end..
58481      
58482      * @param {int} position to add (default end)
58483      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58484      */
58485     addColumns : function(pos, ar)
58486     {
58487         
58488         for (var i =0;i< ar.length;i++) {
58489             var cfg = ar[i];
58490             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58491             this.cm.lookup[cfg.id] = cfg;
58492         }
58493         
58494         
58495         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58496             pos = this.cm.config.length; //this.cm.config.push(cfg);
58497         } 
58498         pos = Math.max(0,pos);
58499         ar.unshift(0);
58500         ar.unshift(pos);
58501         this.cm.config.splice.apply(this.cm.config, ar);
58502         
58503         
58504         
58505         this.view.generateRules(this.cm);
58506         this.view.refresh(true);
58507         
58508     },
58509     
58510     
58511     
58512     
58513     // private
58514     onKeyDown : function(e){
58515         this.fireEvent("keydown", e);
58516     },
58517
58518     /**
58519      * Destroy this grid.
58520      * @param {Boolean} removeEl True to remove the element
58521      */
58522     destroy : function(removeEl, keepListeners){
58523         if(this.loadMask){
58524             this.loadMask.destroy();
58525         }
58526         var c = this.container;
58527         c.removeAllListeners();
58528         this.view.destroy();
58529         this.colModel.purgeListeners();
58530         if(!keepListeners){
58531             this.purgeListeners();
58532         }
58533         c.update("");
58534         if(removeEl === true){
58535             c.remove();
58536         }
58537     },
58538
58539     // private
58540     processEvent : function(name, e){
58541         // does this fire select???
58542         //Roo.log('grid:processEvent '  + name);
58543         
58544         if (name != 'touchstart' ) {
58545             this.fireEvent(name, e);    
58546         }
58547         
58548         var t = e.getTarget();
58549         var v = this.view;
58550         var header = v.findHeaderIndex(t);
58551         if(header !== false){
58552             var ename = name == 'touchstart' ? 'click' : name;
58553              
58554             this.fireEvent("header" + ename, this, header, e);
58555         }else{
58556             var row = v.findRowIndex(t);
58557             var cell = v.findCellIndex(t);
58558             if (name == 'touchstart') {
58559                 // first touch is always a click.
58560                 // hopefull this happens after selection is updated.?
58561                 name = false;
58562                 
58563                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58564                     var cs = this.selModel.getSelectedCell();
58565                     if (row == cs[0] && cell == cs[1]){
58566                         name = 'dblclick';
58567                     }
58568                 }
58569                 if (typeof(this.selModel.getSelections) != 'undefined') {
58570                     var cs = this.selModel.getSelections();
58571                     var ds = this.dataSource;
58572                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58573                         name = 'dblclick';
58574                     }
58575                 }
58576                 if (!name) {
58577                     return;
58578                 }
58579             }
58580             
58581             
58582             if(row !== false){
58583                 this.fireEvent("row" + name, this, row, e);
58584                 if(cell !== false){
58585                     this.fireEvent("cell" + name, this, row, cell, e);
58586                 }
58587             }
58588         }
58589     },
58590
58591     // private
58592     onClick : function(e){
58593         this.processEvent("click", e);
58594     },
58595    // private
58596     onTouchStart : function(e){
58597         this.processEvent("touchstart", e);
58598     },
58599
58600     // private
58601     onContextMenu : function(e, t){
58602         this.processEvent("contextmenu", e);
58603     },
58604
58605     // private
58606     onDblClick : function(e){
58607         this.processEvent("dblclick", e);
58608     },
58609
58610     // private
58611     walkCells : function(row, col, step, fn, scope){
58612         var cm = this.colModel, clen = cm.getColumnCount();
58613         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58614         if(step < 0){
58615             if(col < 0){
58616                 row--;
58617                 first = false;
58618             }
58619             while(row >= 0){
58620                 if(!first){
58621                     col = clen-1;
58622                 }
58623                 first = false;
58624                 while(col >= 0){
58625                     if(fn.call(scope || this, row, col, cm) === true){
58626                         return [row, col];
58627                     }
58628                     col--;
58629                 }
58630                 row--;
58631             }
58632         } else {
58633             if(col >= clen){
58634                 row++;
58635                 first = false;
58636             }
58637             while(row < rlen){
58638                 if(!first){
58639                     col = 0;
58640                 }
58641                 first = false;
58642                 while(col < clen){
58643                     if(fn.call(scope || this, row, col, cm) === true){
58644                         return [row, col];
58645                     }
58646                     col++;
58647                 }
58648                 row++;
58649             }
58650         }
58651         return null;
58652     },
58653
58654     // private
58655     getSelections : function(){
58656         return this.selModel.getSelections();
58657     },
58658
58659     /**
58660      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58661      * but if manual update is required this method will initiate it.
58662      */
58663     autoSize : function(){
58664         if(this.rendered){
58665             this.view.layout();
58666             if(this.view.adjustForScroll){
58667                 this.view.adjustForScroll();
58668             }
58669         }
58670     },
58671
58672     /**
58673      * Returns the grid's underlying element.
58674      * @return {Element} The element
58675      */
58676     getGridEl : function(){
58677         return this.container;
58678     },
58679
58680     // private for compatibility, overridden by editor grid
58681     stopEditing : function(){},
58682
58683     /**
58684      * Returns the grid's SelectionModel.
58685      * @return {SelectionModel}
58686      */
58687     getSelectionModel : function(){
58688         if(!this.selModel){
58689             this.selModel = new Roo.grid.RowSelectionModel();
58690         }
58691         return this.selModel;
58692     },
58693
58694     /**
58695      * Returns the grid's DataSource.
58696      * @return {DataSource}
58697      */
58698     getDataSource : function(){
58699         return this.dataSource;
58700     },
58701
58702     /**
58703      * Returns the grid's ColumnModel.
58704      * @return {ColumnModel}
58705      */
58706     getColumnModel : function(){
58707         return this.colModel;
58708     },
58709
58710     /**
58711      * Returns the grid's GridView object.
58712      * @return {GridView}
58713      */
58714     getView : function(){
58715         if(!this.view){
58716             this.view = new Roo.grid.GridView(this.viewConfig);
58717             this.relayEvents(this.view, [
58718                 "beforerowremoved", "beforerowsinserted",
58719                 "beforerefresh", "rowremoved",
58720                 "rowsinserted", "rowupdated" ,"refresh"
58721             ]);
58722         }
58723         return this.view;
58724     },
58725     /**
58726      * Called to get grid's drag proxy text, by default returns this.ddText.
58727      * Override this to put something different in the dragged text.
58728      * @return {String}
58729      */
58730     getDragDropText : function(){
58731         var count = this.selModel.getCount();
58732         return String.format(this.ddText, count, count == 1 ? '' : 's');
58733     }
58734 });
58735 /*
58736  * Based on:
58737  * Ext JS Library 1.1.1
58738  * Copyright(c) 2006-2007, Ext JS, LLC.
58739  *
58740  * Originally Released Under LGPL - original licence link has changed is not relivant.
58741  *
58742  * Fork - LGPL
58743  * <script type="text/javascript">
58744  */
58745  /**
58746  * @class Roo.grid.AbstractGridView
58747  * @extends Roo.util.Observable
58748  * @abstract
58749  * Abstract base class for grid Views
58750  * @constructor
58751  */
58752 Roo.grid.AbstractGridView = function(){
58753         this.grid = null;
58754         
58755         this.events = {
58756             "beforerowremoved" : true,
58757             "beforerowsinserted" : true,
58758             "beforerefresh" : true,
58759             "rowremoved" : true,
58760             "rowsinserted" : true,
58761             "rowupdated" : true,
58762             "refresh" : true
58763         };
58764     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58765 };
58766
58767 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58768     rowClass : "x-grid-row",
58769     cellClass : "x-grid-cell",
58770     tdClass : "x-grid-td",
58771     hdClass : "x-grid-hd",
58772     splitClass : "x-grid-hd-split",
58773     
58774     init: function(grid){
58775         this.grid = grid;
58776                 var cid = this.grid.getGridEl().id;
58777         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58778         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58779         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58780         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58781         },
58782         
58783     getColumnRenderers : function(){
58784         var renderers = [];
58785         var cm = this.grid.colModel;
58786         var colCount = cm.getColumnCount();
58787         for(var i = 0; i < colCount; i++){
58788             renderers[i] = cm.getRenderer(i);
58789         }
58790         return renderers;
58791     },
58792     
58793     getColumnIds : function(){
58794         var ids = [];
58795         var cm = this.grid.colModel;
58796         var colCount = cm.getColumnCount();
58797         for(var i = 0; i < colCount; i++){
58798             ids[i] = cm.getColumnId(i);
58799         }
58800         return ids;
58801     },
58802     
58803     getDataIndexes : function(){
58804         if(!this.indexMap){
58805             this.indexMap = this.buildIndexMap();
58806         }
58807         return this.indexMap.colToData;
58808     },
58809     
58810     getColumnIndexByDataIndex : function(dataIndex){
58811         if(!this.indexMap){
58812             this.indexMap = this.buildIndexMap();
58813         }
58814         return this.indexMap.dataToCol[dataIndex];
58815     },
58816     
58817     /**
58818      * Set a css style for a column dynamically. 
58819      * @param {Number} colIndex The index of the column
58820      * @param {String} name The css property name
58821      * @param {String} value The css value
58822      */
58823     setCSSStyle : function(colIndex, name, value){
58824         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58825         Roo.util.CSS.updateRule(selector, name, value);
58826     },
58827     
58828     generateRules : function(cm){
58829         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58830         Roo.util.CSS.removeStyleSheet(rulesId);
58831         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58832             var cid = cm.getColumnId(i);
58833             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58834                          this.tdSelector, cid, " {\n}\n",
58835                          this.hdSelector, cid, " {\n}\n",
58836                          this.splitSelector, cid, " {\n}\n");
58837         }
58838         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58839     }
58840 });/*
58841  * Based on:
58842  * Ext JS Library 1.1.1
58843  * Copyright(c) 2006-2007, Ext JS, LLC.
58844  *
58845  * Originally Released Under LGPL - original licence link has changed is not relivant.
58846  *
58847  * Fork - LGPL
58848  * <script type="text/javascript">
58849  */
58850
58851 // private
58852 // This is a support class used internally by the Grid components
58853 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58854     this.grid = grid;
58855     this.view = grid.getView();
58856     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58857     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58858     if(hd2){
58859         this.setHandleElId(Roo.id(hd));
58860         this.setOuterHandleElId(Roo.id(hd2));
58861     }
58862     this.scroll = false;
58863 };
58864 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58865     maxDragWidth: 120,
58866     getDragData : function(e){
58867         var t = Roo.lib.Event.getTarget(e);
58868         var h = this.view.findHeaderCell(t);
58869         if(h){
58870             return {ddel: h.firstChild, header:h};
58871         }
58872         return false;
58873     },
58874
58875     onInitDrag : function(e){
58876         this.view.headersDisabled = true;
58877         var clone = this.dragData.ddel.cloneNode(true);
58878         clone.id = Roo.id();
58879         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58880         this.proxy.update(clone);
58881         return true;
58882     },
58883
58884     afterValidDrop : function(){
58885         var v = this.view;
58886         setTimeout(function(){
58887             v.headersDisabled = false;
58888         }, 50);
58889     },
58890
58891     afterInvalidDrop : function(){
58892         var v = this.view;
58893         setTimeout(function(){
58894             v.headersDisabled = false;
58895         }, 50);
58896     }
58897 });
58898 /*
58899  * Based on:
58900  * Ext JS Library 1.1.1
58901  * Copyright(c) 2006-2007, Ext JS, LLC.
58902  *
58903  * Originally Released Under LGPL - original licence link has changed is not relivant.
58904  *
58905  * Fork - LGPL
58906  * <script type="text/javascript">
58907  */
58908 // private
58909 // This is a support class used internally by the Grid components
58910 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58911     this.grid = grid;
58912     this.view = grid.getView();
58913     // split the proxies so they don't interfere with mouse events
58914     this.proxyTop = Roo.DomHelper.append(document.body, {
58915         cls:"col-move-top", html:"&#160;"
58916     }, true);
58917     this.proxyBottom = Roo.DomHelper.append(document.body, {
58918         cls:"col-move-bottom", html:"&#160;"
58919     }, true);
58920     this.proxyTop.hide = this.proxyBottom.hide = function(){
58921         this.setLeftTop(-100,-100);
58922         this.setStyle("visibility", "hidden");
58923     };
58924     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58925     // temporarily disabled
58926     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58927     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58928 };
58929 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58930     proxyOffsets : [-4, -9],
58931     fly: Roo.Element.fly,
58932
58933     getTargetFromEvent : function(e){
58934         var t = Roo.lib.Event.getTarget(e);
58935         var cindex = this.view.findCellIndex(t);
58936         if(cindex !== false){
58937             return this.view.getHeaderCell(cindex);
58938         }
58939         return null;
58940     },
58941
58942     nextVisible : function(h){
58943         var v = this.view, cm = this.grid.colModel;
58944         h = h.nextSibling;
58945         while(h){
58946             if(!cm.isHidden(v.getCellIndex(h))){
58947                 return h;
58948             }
58949             h = h.nextSibling;
58950         }
58951         return null;
58952     },
58953
58954     prevVisible : function(h){
58955         var v = this.view, cm = this.grid.colModel;
58956         h = h.prevSibling;
58957         while(h){
58958             if(!cm.isHidden(v.getCellIndex(h))){
58959                 return h;
58960             }
58961             h = h.prevSibling;
58962         }
58963         return null;
58964     },
58965
58966     positionIndicator : function(h, n, e){
58967         var x = Roo.lib.Event.getPageX(e);
58968         var r = Roo.lib.Dom.getRegion(n.firstChild);
58969         var px, pt, py = r.top + this.proxyOffsets[1];
58970         if((r.right - x) <= (r.right-r.left)/2){
58971             px = r.right+this.view.borderWidth;
58972             pt = "after";
58973         }else{
58974             px = r.left;
58975             pt = "before";
58976         }
58977         var oldIndex = this.view.getCellIndex(h);
58978         var newIndex = this.view.getCellIndex(n);
58979
58980         if(this.grid.colModel.isFixed(newIndex)){
58981             return false;
58982         }
58983
58984         var locked = this.grid.colModel.isLocked(newIndex);
58985
58986         if(pt == "after"){
58987             newIndex++;
58988         }
58989         if(oldIndex < newIndex){
58990             newIndex--;
58991         }
58992         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58993             return false;
58994         }
58995         px +=  this.proxyOffsets[0];
58996         this.proxyTop.setLeftTop(px, py);
58997         this.proxyTop.show();
58998         if(!this.bottomOffset){
58999             this.bottomOffset = this.view.mainHd.getHeight();
59000         }
59001         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59002         this.proxyBottom.show();
59003         return pt;
59004     },
59005
59006     onNodeEnter : function(n, dd, e, data){
59007         if(data.header != n){
59008             this.positionIndicator(data.header, n, e);
59009         }
59010     },
59011
59012     onNodeOver : function(n, dd, e, data){
59013         var result = false;
59014         if(data.header != n){
59015             result = this.positionIndicator(data.header, n, e);
59016         }
59017         if(!result){
59018             this.proxyTop.hide();
59019             this.proxyBottom.hide();
59020         }
59021         return result ? this.dropAllowed : this.dropNotAllowed;
59022     },
59023
59024     onNodeOut : function(n, dd, e, data){
59025         this.proxyTop.hide();
59026         this.proxyBottom.hide();
59027     },
59028
59029     onNodeDrop : function(n, dd, e, data){
59030         var h = data.header;
59031         if(h != n){
59032             var cm = this.grid.colModel;
59033             var x = Roo.lib.Event.getPageX(e);
59034             var r = Roo.lib.Dom.getRegion(n.firstChild);
59035             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59036             var oldIndex = this.view.getCellIndex(h);
59037             var newIndex = this.view.getCellIndex(n);
59038             var locked = cm.isLocked(newIndex);
59039             if(pt == "after"){
59040                 newIndex++;
59041             }
59042             if(oldIndex < newIndex){
59043                 newIndex--;
59044             }
59045             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59046                 return false;
59047             }
59048             cm.setLocked(oldIndex, locked, true);
59049             cm.moveColumn(oldIndex, newIndex);
59050             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59051             return true;
59052         }
59053         return false;
59054     }
59055 });
59056 /*
59057  * Based on:
59058  * Ext JS Library 1.1.1
59059  * Copyright(c) 2006-2007, Ext JS, LLC.
59060  *
59061  * Originally Released Under LGPL - original licence link has changed is not relivant.
59062  *
59063  * Fork - LGPL
59064  * <script type="text/javascript">
59065  */
59066   
59067 /**
59068  * @class Roo.grid.GridView
59069  * @extends Roo.util.Observable
59070  *
59071  * @constructor
59072  * @param {Object} config
59073  */
59074 Roo.grid.GridView = function(config){
59075     Roo.grid.GridView.superclass.constructor.call(this);
59076     this.el = null;
59077
59078     Roo.apply(this, config);
59079 };
59080
59081 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59082
59083     unselectable :  'unselectable="on"',
59084     unselectableCls :  'x-unselectable',
59085     
59086     
59087     rowClass : "x-grid-row",
59088
59089     cellClass : "x-grid-col",
59090
59091     tdClass : "x-grid-td",
59092
59093     hdClass : "x-grid-hd",
59094
59095     splitClass : "x-grid-split",
59096
59097     sortClasses : ["sort-asc", "sort-desc"],
59098
59099     enableMoveAnim : false,
59100
59101     hlColor: "C3DAF9",
59102
59103     dh : Roo.DomHelper,
59104
59105     fly : Roo.Element.fly,
59106
59107     css : Roo.util.CSS,
59108
59109     borderWidth: 1,
59110
59111     splitOffset: 3,
59112
59113     scrollIncrement : 22,
59114
59115     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59116
59117     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59118
59119     bind : function(ds, cm){
59120         if(this.ds){
59121             this.ds.un("load", this.onLoad, this);
59122             this.ds.un("datachanged", this.onDataChange, this);
59123             this.ds.un("add", this.onAdd, this);
59124             this.ds.un("remove", this.onRemove, this);
59125             this.ds.un("update", this.onUpdate, this);
59126             this.ds.un("clear", this.onClear, this);
59127         }
59128         if(ds){
59129             ds.on("load", this.onLoad, this);
59130             ds.on("datachanged", this.onDataChange, this);
59131             ds.on("add", this.onAdd, this);
59132             ds.on("remove", this.onRemove, this);
59133             ds.on("update", this.onUpdate, this);
59134             ds.on("clear", this.onClear, this);
59135         }
59136         this.ds = ds;
59137
59138         if(this.cm){
59139             this.cm.un("widthchange", this.onColWidthChange, this);
59140             this.cm.un("headerchange", this.onHeaderChange, this);
59141             this.cm.un("hiddenchange", this.onHiddenChange, this);
59142             this.cm.un("columnmoved", this.onColumnMove, this);
59143             this.cm.un("columnlockchange", this.onColumnLock, this);
59144         }
59145         if(cm){
59146             this.generateRules(cm);
59147             cm.on("widthchange", this.onColWidthChange, this);
59148             cm.on("headerchange", this.onHeaderChange, this);
59149             cm.on("hiddenchange", this.onHiddenChange, this);
59150             cm.on("columnmoved", this.onColumnMove, this);
59151             cm.on("columnlockchange", this.onColumnLock, this);
59152         }
59153         this.cm = cm;
59154     },
59155
59156     init: function(grid){
59157         Roo.grid.GridView.superclass.init.call(this, grid);
59158
59159         this.bind(grid.dataSource, grid.colModel);
59160
59161         grid.on("headerclick", this.handleHeaderClick, this);
59162
59163         if(grid.trackMouseOver){
59164             grid.on("mouseover", this.onRowOver, this);
59165             grid.on("mouseout", this.onRowOut, this);
59166         }
59167         grid.cancelTextSelection = function(){};
59168         this.gridId = grid.id;
59169
59170         var tpls = this.templates || {};
59171
59172         if(!tpls.master){
59173             tpls.master = new Roo.Template(
59174                '<div class="x-grid" hidefocus="true">',
59175                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59176                   '<div class="x-grid-topbar"></div>',
59177                   '<div class="x-grid-scroller"><div></div></div>',
59178                   '<div class="x-grid-locked">',
59179                       '<div class="x-grid-header">{lockedHeader}</div>',
59180                       '<div class="x-grid-body">{lockedBody}</div>',
59181                   "</div>",
59182                   '<div class="x-grid-viewport">',
59183                       '<div class="x-grid-header">{header}</div>',
59184                       '<div class="x-grid-body">{body}</div>',
59185                   "</div>",
59186                   '<div class="x-grid-bottombar"></div>',
59187                  
59188                   '<div class="x-grid-resize-proxy">&#160;</div>',
59189                "</div>"
59190             );
59191             tpls.master.disableformats = true;
59192         }
59193
59194         if(!tpls.header){
59195             tpls.header = new Roo.Template(
59196                '<table border="0" cellspacing="0" cellpadding="0">',
59197                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59198                "</table>{splits}"
59199             );
59200             tpls.header.disableformats = true;
59201         }
59202         tpls.header.compile();
59203
59204         if(!tpls.hcell){
59205             tpls.hcell = new Roo.Template(
59206                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59207                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59208                 "</div></td>"
59209              );
59210              tpls.hcell.disableFormats = true;
59211         }
59212         tpls.hcell.compile();
59213
59214         if(!tpls.hsplit){
59215             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59216                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59217             tpls.hsplit.disableFormats = true;
59218         }
59219         tpls.hsplit.compile();
59220
59221         if(!tpls.body){
59222             tpls.body = new Roo.Template(
59223                '<table border="0" cellspacing="0" cellpadding="0">',
59224                "<tbody>{rows}</tbody>",
59225                "</table>"
59226             );
59227             tpls.body.disableFormats = true;
59228         }
59229         tpls.body.compile();
59230
59231         if(!tpls.row){
59232             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59233             tpls.row.disableFormats = true;
59234         }
59235         tpls.row.compile();
59236
59237         if(!tpls.cell){
59238             tpls.cell = new Roo.Template(
59239                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59240                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59241                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59242                 "</td>"
59243             );
59244             tpls.cell.disableFormats = true;
59245         }
59246         tpls.cell.compile();
59247
59248         this.templates = tpls;
59249     },
59250
59251     // remap these for backwards compat
59252     onColWidthChange : function(){
59253         this.updateColumns.apply(this, arguments);
59254     },
59255     onHeaderChange : function(){
59256         this.updateHeaders.apply(this, arguments);
59257     }, 
59258     onHiddenChange : function(){
59259         this.handleHiddenChange.apply(this, arguments);
59260     },
59261     onColumnMove : function(){
59262         this.handleColumnMove.apply(this, arguments);
59263     },
59264     onColumnLock : function(){
59265         this.handleLockChange.apply(this, arguments);
59266     },
59267
59268     onDataChange : function(){
59269         this.refresh();
59270         this.updateHeaderSortState();
59271     },
59272
59273     onClear : function(){
59274         this.refresh();
59275     },
59276
59277     onUpdate : function(ds, record){
59278         this.refreshRow(record);
59279     },
59280
59281     refreshRow : function(record){
59282         var ds = this.ds, index;
59283         if(typeof record == 'number'){
59284             index = record;
59285             record = ds.getAt(index);
59286         }else{
59287             index = ds.indexOf(record);
59288         }
59289         this.insertRows(ds, index, index, true);
59290         this.onRemove(ds, record, index+1, true);
59291         this.syncRowHeights(index, index);
59292         this.layout();
59293         this.fireEvent("rowupdated", this, index, record);
59294     },
59295
59296     onAdd : function(ds, records, index){
59297         this.insertRows(ds, index, index + (records.length-1));
59298     },
59299
59300     onRemove : function(ds, record, index, isUpdate){
59301         if(isUpdate !== true){
59302             this.fireEvent("beforerowremoved", this, index, record);
59303         }
59304         var bt = this.getBodyTable(), lt = this.getLockedTable();
59305         if(bt.rows[index]){
59306             bt.firstChild.removeChild(bt.rows[index]);
59307         }
59308         if(lt.rows[index]){
59309             lt.firstChild.removeChild(lt.rows[index]);
59310         }
59311         if(isUpdate !== true){
59312             this.stripeRows(index);
59313             this.syncRowHeights(index, index);
59314             this.layout();
59315             this.fireEvent("rowremoved", this, index, record);
59316         }
59317     },
59318
59319     onLoad : function(){
59320         this.scrollToTop();
59321     },
59322
59323     /**
59324      * Scrolls the grid to the top
59325      */
59326     scrollToTop : function(){
59327         if(this.scroller){
59328             this.scroller.dom.scrollTop = 0;
59329             this.syncScroll();
59330         }
59331     },
59332
59333     /**
59334      * Gets a panel in the header of the grid that can be used for toolbars etc.
59335      * After modifying the contents of this panel a call to grid.autoSize() may be
59336      * required to register any changes in size.
59337      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59338      * @return Roo.Element
59339      */
59340     getHeaderPanel : function(doShow){
59341         if(doShow){
59342             this.headerPanel.show();
59343         }
59344         return this.headerPanel;
59345     },
59346
59347     /**
59348      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59349      * After modifying the contents of this panel a call to grid.autoSize() may be
59350      * required to register any changes in size.
59351      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59352      * @return Roo.Element
59353      */
59354     getFooterPanel : function(doShow){
59355         if(doShow){
59356             this.footerPanel.show();
59357         }
59358         return this.footerPanel;
59359     },
59360
59361     initElements : function(){
59362         var E = Roo.Element;
59363         var el = this.grid.getGridEl().dom.firstChild;
59364         var cs = el.childNodes;
59365
59366         this.el = new E(el);
59367         
59368          this.focusEl = new E(el.firstChild);
59369         this.focusEl.swallowEvent("click", true);
59370         
59371         this.headerPanel = new E(cs[1]);
59372         this.headerPanel.enableDisplayMode("block");
59373
59374         this.scroller = new E(cs[2]);
59375         this.scrollSizer = new E(this.scroller.dom.firstChild);
59376
59377         this.lockedWrap = new E(cs[3]);
59378         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59379         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59380
59381         this.mainWrap = new E(cs[4]);
59382         this.mainHd = new E(this.mainWrap.dom.firstChild);
59383         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59384
59385         this.footerPanel = new E(cs[5]);
59386         this.footerPanel.enableDisplayMode("block");
59387
59388         this.resizeProxy = new E(cs[6]);
59389
59390         this.headerSelector = String.format(
59391            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59392            this.lockedHd.id, this.mainHd.id
59393         );
59394
59395         this.splitterSelector = String.format(
59396            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59397            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59398         );
59399     },
59400     idToCssName : function(s)
59401     {
59402         return s.replace(/[^a-z0-9]+/ig, '-');
59403     },
59404
59405     getHeaderCell : function(index){
59406         return Roo.DomQuery.select(this.headerSelector)[index];
59407     },
59408
59409     getHeaderCellMeasure : function(index){
59410         return this.getHeaderCell(index).firstChild;
59411     },
59412
59413     getHeaderCellText : function(index){
59414         return this.getHeaderCell(index).firstChild.firstChild;
59415     },
59416
59417     getLockedTable : function(){
59418         return this.lockedBody.dom.firstChild;
59419     },
59420
59421     getBodyTable : function(){
59422         return this.mainBody.dom.firstChild;
59423     },
59424
59425     getLockedRow : function(index){
59426         return this.getLockedTable().rows[index];
59427     },
59428
59429     getRow : function(index){
59430         return this.getBodyTable().rows[index];
59431     },
59432
59433     getRowComposite : function(index){
59434         if(!this.rowEl){
59435             this.rowEl = new Roo.CompositeElementLite();
59436         }
59437         var els = [], lrow, mrow;
59438         if(lrow = this.getLockedRow(index)){
59439             els.push(lrow);
59440         }
59441         if(mrow = this.getRow(index)){
59442             els.push(mrow);
59443         }
59444         this.rowEl.elements = els;
59445         return this.rowEl;
59446     },
59447     /**
59448      * Gets the 'td' of the cell
59449      * 
59450      * @param {Integer} rowIndex row to select
59451      * @param {Integer} colIndex column to select
59452      * 
59453      * @return {Object} 
59454      */
59455     getCell : function(rowIndex, colIndex){
59456         var locked = this.cm.getLockedCount();
59457         var source;
59458         if(colIndex < locked){
59459             source = this.lockedBody.dom.firstChild;
59460         }else{
59461             source = this.mainBody.dom.firstChild;
59462             colIndex -= locked;
59463         }
59464         return source.rows[rowIndex].childNodes[colIndex];
59465     },
59466
59467     getCellText : function(rowIndex, colIndex){
59468         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59469     },
59470
59471     getCellBox : function(cell){
59472         var b = this.fly(cell).getBox();
59473         if(Roo.isOpera){ // opera fails to report the Y
59474             b.y = cell.offsetTop + this.mainBody.getY();
59475         }
59476         return b;
59477     },
59478
59479     getCellIndex : function(cell){
59480         var id = String(cell.className).match(this.cellRE);
59481         if(id){
59482             return parseInt(id[1], 10);
59483         }
59484         return 0;
59485     },
59486
59487     findHeaderIndex : function(n){
59488         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59489         return r ? this.getCellIndex(r) : false;
59490     },
59491
59492     findHeaderCell : function(n){
59493         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59494         return r ? r : false;
59495     },
59496
59497     findRowIndex : function(n){
59498         if(!n){
59499             return false;
59500         }
59501         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59502         return r ? r.rowIndex : false;
59503     },
59504
59505     findCellIndex : function(node){
59506         var stop = this.el.dom;
59507         while(node && node != stop){
59508             if(this.findRE.test(node.className)){
59509                 return this.getCellIndex(node);
59510             }
59511             node = node.parentNode;
59512         }
59513         return false;
59514     },
59515
59516     getColumnId : function(index){
59517         return this.cm.getColumnId(index);
59518     },
59519
59520     getSplitters : function()
59521     {
59522         if(this.splitterSelector){
59523            return Roo.DomQuery.select(this.splitterSelector);
59524         }else{
59525             return null;
59526       }
59527     },
59528
59529     getSplitter : function(index){
59530         return this.getSplitters()[index];
59531     },
59532
59533     onRowOver : function(e, t){
59534         var row;
59535         if((row = this.findRowIndex(t)) !== false){
59536             this.getRowComposite(row).addClass("x-grid-row-over");
59537         }
59538     },
59539
59540     onRowOut : function(e, t){
59541         var row;
59542         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59543             this.getRowComposite(row).removeClass("x-grid-row-over");
59544         }
59545     },
59546
59547     renderHeaders : function(){
59548         var cm = this.cm;
59549         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59550         var cb = [], lb = [], sb = [], lsb = [], p = {};
59551         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59552             p.cellId = "x-grid-hd-0-" + i;
59553             p.splitId = "x-grid-csplit-0-" + i;
59554             p.id = cm.getColumnId(i);
59555             p.value = cm.getColumnHeader(i) || "";
59556             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59557             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59558             if(!cm.isLocked(i)){
59559                 cb[cb.length] = ct.apply(p);
59560                 sb[sb.length] = st.apply(p);
59561             }else{
59562                 lb[lb.length] = ct.apply(p);
59563                 lsb[lsb.length] = st.apply(p);
59564             }
59565         }
59566         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59567                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59568     },
59569
59570     updateHeaders : function(){
59571         var html = this.renderHeaders();
59572         this.lockedHd.update(html[0]);
59573         this.mainHd.update(html[1]);
59574     },
59575
59576     /**
59577      * Focuses the specified row.
59578      * @param {Number} row The row index
59579      */
59580     focusRow : function(row)
59581     {
59582         //Roo.log('GridView.focusRow');
59583         var x = this.scroller.dom.scrollLeft;
59584         this.focusCell(row, 0, false);
59585         this.scroller.dom.scrollLeft = x;
59586     },
59587
59588     /**
59589      * Focuses the specified cell.
59590      * @param {Number} row The row index
59591      * @param {Number} col The column index
59592      * @param {Boolean} hscroll false to disable horizontal scrolling
59593      */
59594     focusCell : function(row, col, hscroll)
59595     {
59596         //Roo.log('GridView.focusCell');
59597         var el = this.ensureVisible(row, col, hscroll);
59598         this.focusEl.alignTo(el, "tl-tl");
59599         if(Roo.isGecko){
59600             this.focusEl.focus();
59601         }else{
59602             this.focusEl.focus.defer(1, this.focusEl);
59603         }
59604     },
59605
59606     /**
59607      * Scrolls the specified cell into view
59608      * @param {Number} row The row index
59609      * @param {Number} col The column index
59610      * @param {Boolean} hscroll false to disable horizontal scrolling
59611      */
59612     ensureVisible : function(row, col, hscroll)
59613     {
59614         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59615         //return null; //disable for testing.
59616         if(typeof row != "number"){
59617             row = row.rowIndex;
59618         }
59619         if(row < 0 && row >= this.ds.getCount()){
59620             return  null;
59621         }
59622         col = (col !== undefined ? col : 0);
59623         var cm = this.grid.colModel;
59624         while(cm.isHidden(col)){
59625             col++;
59626         }
59627
59628         var el = this.getCell(row, col);
59629         if(!el){
59630             return null;
59631         }
59632         var c = this.scroller.dom;
59633
59634         var ctop = parseInt(el.offsetTop, 10);
59635         var cleft = parseInt(el.offsetLeft, 10);
59636         var cbot = ctop + el.offsetHeight;
59637         var cright = cleft + el.offsetWidth;
59638         
59639         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59640         var stop = parseInt(c.scrollTop, 10);
59641         var sleft = parseInt(c.scrollLeft, 10);
59642         var sbot = stop + ch;
59643         var sright = sleft + c.clientWidth;
59644         /*
59645         Roo.log('GridView.ensureVisible:' +
59646                 ' ctop:' + ctop +
59647                 ' c.clientHeight:' + c.clientHeight +
59648                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59649                 ' stop:' + stop +
59650                 ' cbot:' + cbot +
59651                 ' sbot:' + sbot +
59652                 ' ch:' + ch  
59653                 );
59654         */
59655         if(ctop < stop){
59656             c.scrollTop = ctop;
59657             //Roo.log("set scrolltop to ctop DISABLE?");
59658         }else if(cbot > sbot){
59659             //Roo.log("set scrolltop to cbot-ch");
59660             c.scrollTop = cbot-ch;
59661         }
59662         
59663         if(hscroll !== false){
59664             if(cleft < sleft){
59665                 c.scrollLeft = cleft;
59666             }else if(cright > sright){
59667                 c.scrollLeft = cright-c.clientWidth;
59668             }
59669         }
59670          
59671         return el;
59672     },
59673
59674     updateColumns : function(){
59675         this.grid.stopEditing();
59676         var cm = this.grid.colModel, colIds = this.getColumnIds();
59677         //var totalWidth = cm.getTotalWidth();
59678         var pos = 0;
59679         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59680             //if(cm.isHidden(i)) continue;
59681             var w = cm.getColumnWidth(i);
59682             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59683             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59684         }
59685         this.updateSplitters();
59686     },
59687
59688     generateRules : function(cm){
59689         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59690         Roo.util.CSS.removeStyleSheet(rulesId);
59691         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59692             var cid = cm.getColumnId(i);
59693             var align = '';
59694             if(cm.config[i].align){
59695                 align = 'text-align:'+cm.config[i].align+';';
59696             }
59697             var hidden = '';
59698             if(cm.isHidden(i)){
59699                 hidden = 'display:none;';
59700             }
59701             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59702             ruleBuf.push(
59703                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59704                     this.hdSelector, cid, " {\n", align, width, "}\n",
59705                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59706                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59707         }
59708         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59709     },
59710
59711     updateSplitters : function(){
59712         var cm = this.cm, s = this.getSplitters();
59713         if(s){ // splitters not created yet
59714             var pos = 0, locked = true;
59715             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59716                 if(cm.isHidden(i)) {
59717                     continue;
59718                 }
59719                 var w = cm.getColumnWidth(i); // make sure it's a number
59720                 if(!cm.isLocked(i) && locked){
59721                     pos = 0;
59722                     locked = false;
59723                 }
59724                 pos += w;
59725                 s[i].style.left = (pos-this.splitOffset) + "px";
59726             }
59727         }
59728     },
59729
59730     handleHiddenChange : function(colModel, colIndex, hidden){
59731         if(hidden){
59732             this.hideColumn(colIndex);
59733         }else{
59734             this.unhideColumn(colIndex);
59735         }
59736     },
59737
59738     hideColumn : function(colIndex){
59739         var cid = this.getColumnId(colIndex);
59740         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59741         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59742         if(Roo.isSafari){
59743             this.updateHeaders();
59744         }
59745         this.updateSplitters();
59746         this.layout();
59747     },
59748
59749     unhideColumn : function(colIndex){
59750         var cid = this.getColumnId(colIndex);
59751         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59752         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59753
59754         if(Roo.isSafari){
59755             this.updateHeaders();
59756         }
59757         this.updateSplitters();
59758         this.layout();
59759     },
59760
59761     insertRows : function(dm, firstRow, lastRow, isUpdate){
59762         if(firstRow == 0 && lastRow == dm.getCount()-1){
59763             this.refresh();
59764         }else{
59765             if(!isUpdate){
59766                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59767             }
59768             var s = this.getScrollState();
59769             var markup = this.renderRows(firstRow, lastRow);
59770             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59771             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59772             this.restoreScroll(s);
59773             if(!isUpdate){
59774                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59775                 this.syncRowHeights(firstRow, lastRow);
59776                 this.stripeRows(firstRow);
59777                 this.layout();
59778             }
59779         }
59780     },
59781
59782     bufferRows : function(markup, target, index){
59783         var before = null, trows = target.rows, tbody = target.tBodies[0];
59784         if(index < trows.length){
59785             before = trows[index];
59786         }
59787         var b = document.createElement("div");
59788         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59789         var rows = b.firstChild.rows;
59790         for(var i = 0, len = rows.length; i < len; i++){
59791             if(before){
59792                 tbody.insertBefore(rows[0], before);
59793             }else{
59794                 tbody.appendChild(rows[0]);
59795             }
59796         }
59797         b.innerHTML = "";
59798         b = null;
59799     },
59800
59801     deleteRows : function(dm, firstRow, lastRow){
59802         if(dm.getRowCount()<1){
59803             this.fireEvent("beforerefresh", this);
59804             this.mainBody.update("");
59805             this.lockedBody.update("");
59806             this.fireEvent("refresh", this);
59807         }else{
59808             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59809             var bt = this.getBodyTable();
59810             var tbody = bt.firstChild;
59811             var rows = bt.rows;
59812             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59813                 tbody.removeChild(rows[firstRow]);
59814             }
59815             this.stripeRows(firstRow);
59816             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59817         }
59818     },
59819
59820     updateRows : function(dataSource, firstRow, lastRow){
59821         var s = this.getScrollState();
59822         this.refresh();
59823         this.restoreScroll(s);
59824     },
59825
59826     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59827         if(!noRefresh){
59828            this.refresh();
59829         }
59830         this.updateHeaderSortState();
59831     },
59832
59833     getScrollState : function(){
59834         
59835         var sb = this.scroller.dom;
59836         return {left: sb.scrollLeft, top: sb.scrollTop};
59837     },
59838
59839     stripeRows : function(startRow){
59840         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59841             return;
59842         }
59843         startRow = startRow || 0;
59844         var rows = this.getBodyTable().rows;
59845         var lrows = this.getLockedTable().rows;
59846         var cls = ' x-grid-row-alt ';
59847         for(var i = startRow, len = rows.length; i < len; i++){
59848             var row = rows[i], lrow = lrows[i];
59849             var isAlt = ((i+1) % 2 == 0);
59850             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59851             if(isAlt == hasAlt){
59852                 continue;
59853             }
59854             if(isAlt){
59855                 row.className += " x-grid-row-alt";
59856             }else{
59857                 row.className = row.className.replace("x-grid-row-alt", "");
59858             }
59859             if(lrow){
59860                 lrow.className = row.className;
59861             }
59862         }
59863     },
59864
59865     restoreScroll : function(state){
59866         //Roo.log('GridView.restoreScroll');
59867         var sb = this.scroller.dom;
59868         sb.scrollLeft = state.left;
59869         sb.scrollTop = state.top;
59870         this.syncScroll();
59871     },
59872
59873     syncScroll : function(){
59874         //Roo.log('GridView.syncScroll');
59875         var sb = this.scroller.dom;
59876         var sh = this.mainHd.dom;
59877         var bs = this.mainBody.dom;
59878         var lv = this.lockedBody.dom;
59879         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59880         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59881     },
59882
59883     handleScroll : function(e){
59884         this.syncScroll();
59885         var sb = this.scroller.dom;
59886         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59887         e.stopEvent();
59888     },
59889
59890     handleWheel : function(e){
59891         var d = e.getWheelDelta();
59892         this.scroller.dom.scrollTop -= d*22;
59893         // set this here to prevent jumpy scrolling on large tables
59894         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59895         e.stopEvent();
59896     },
59897
59898     renderRows : function(startRow, endRow){
59899         // pull in all the crap needed to render rows
59900         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59901         var colCount = cm.getColumnCount();
59902
59903         if(ds.getCount() < 1){
59904             return ["", ""];
59905         }
59906
59907         // build a map for all the columns
59908         var cs = [];
59909         for(var i = 0; i < colCount; i++){
59910             var name = cm.getDataIndex(i);
59911             cs[i] = {
59912                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59913                 renderer : cm.getRenderer(i),
59914                 id : cm.getColumnId(i),
59915                 locked : cm.isLocked(i),
59916                 has_editor : cm.isCellEditable(i)
59917             };
59918         }
59919
59920         startRow = startRow || 0;
59921         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59922
59923         // records to render
59924         var rs = ds.getRange(startRow, endRow);
59925
59926         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59927     },
59928
59929     // As much as I hate to duplicate code, this was branched because FireFox really hates
59930     // [].join("") on strings. The performance difference was substantial enough to
59931     // branch this function
59932     doRender : Roo.isGecko ?
59933             function(cs, rs, ds, startRow, colCount, stripe){
59934                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59935                 // buffers
59936                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59937                 
59938                 var hasListener = this.grid.hasListener('rowclass');
59939                 var rowcfg = {};
59940                 for(var j = 0, len = rs.length; j < len; j++){
59941                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59942                     for(var i = 0; i < colCount; i++){
59943                         c = cs[i];
59944                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59945                         p.id = c.id;
59946                         p.css = p.attr = "";
59947                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59948                         if(p.value == undefined || p.value === "") {
59949                             p.value = "&#160;";
59950                         }
59951                         if(c.has_editor){
59952                             p.css += ' x-grid-editable-cell';
59953                         }
59954                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59955                             p.css +=  ' x-grid-dirty-cell';
59956                         }
59957                         var markup = ct.apply(p);
59958                         if(!c.locked){
59959                             cb+= markup;
59960                         }else{
59961                             lcb+= markup;
59962                         }
59963                     }
59964                     var alt = [];
59965                     if(stripe && ((rowIndex+1) % 2 == 0)){
59966                         alt.push("x-grid-row-alt")
59967                     }
59968                     if(r.dirty){
59969                         alt.push(  " x-grid-dirty-row");
59970                     }
59971                     rp.cells = lcb;
59972                     if(this.getRowClass){
59973                         alt.push(this.getRowClass(r, rowIndex));
59974                     }
59975                     if (hasListener) {
59976                         rowcfg = {
59977                              
59978                             record: r,
59979                             rowIndex : rowIndex,
59980                             rowClass : ''
59981                         };
59982                         this.grid.fireEvent('rowclass', this, rowcfg);
59983                         alt.push(rowcfg.rowClass);
59984                     }
59985                     rp.alt = alt.join(" ");
59986                     lbuf+= rt.apply(rp);
59987                     rp.cells = cb;
59988                     buf+=  rt.apply(rp);
59989                 }
59990                 return [lbuf, buf];
59991             } :
59992             function(cs, rs, ds, startRow, colCount, stripe){
59993                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59994                 // buffers
59995                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59996                 var hasListener = this.grid.hasListener('rowclass');
59997  
59998                 var rowcfg = {};
59999                 for(var j = 0, len = rs.length; j < len; j++){
60000                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60001                     for(var i = 0; i < colCount; i++){
60002                         c = cs[i];
60003                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60004                         p.id = c.id;
60005                         p.css = p.attr = "";
60006                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60007                         if(p.value == undefined || p.value === "") {
60008                             p.value = "&#160;";
60009                         }
60010                         //Roo.log(c);
60011                          if(c.has_editor){
60012                             p.css += ' x-grid-editable-cell';
60013                         }
60014                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60015                             p.css += ' x-grid-dirty-cell' 
60016                         }
60017                         
60018                         var markup = ct.apply(p);
60019                         if(!c.locked){
60020                             cb[cb.length] = markup;
60021                         }else{
60022                             lcb[lcb.length] = markup;
60023                         }
60024                     }
60025                     var alt = [];
60026                     if(stripe && ((rowIndex+1) % 2 == 0)){
60027                         alt.push( "x-grid-row-alt");
60028                     }
60029                     if(r.dirty){
60030                         alt.push(" x-grid-dirty-row");
60031                     }
60032                     rp.cells = lcb;
60033                     if(this.getRowClass){
60034                         alt.push( this.getRowClass(r, rowIndex));
60035                     }
60036                     if (hasListener) {
60037                         rowcfg = {
60038                              
60039                             record: r,
60040                             rowIndex : rowIndex,
60041                             rowClass : ''
60042                         };
60043                         this.grid.fireEvent('rowclass', this, rowcfg);
60044                         alt.push(rowcfg.rowClass);
60045                     }
60046                     
60047                     rp.alt = alt.join(" ");
60048                     rp.cells = lcb.join("");
60049                     lbuf[lbuf.length] = rt.apply(rp);
60050                     rp.cells = cb.join("");
60051                     buf[buf.length] =  rt.apply(rp);
60052                 }
60053                 return [lbuf.join(""), buf.join("")];
60054             },
60055
60056     renderBody : function(){
60057         var markup = this.renderRows();
60058         var bt = this.templates.body;
60059         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60060     },
60061
60062     /**
60063      * Refreshes the grid
60064      * @param {Boolean} headersToo
60065      */
60066     refresh : function(headersToo){
60067         this.fireEvent("beforerefresh", this);
60068         this.grid.stopEditing();
60069         var result = this.renderBody();
60070         this.lockedBody.update(result[0]);
60071         this.mainBody.update(result[1]);
60072         if(headersToo === true){
60073             this.updateHeaders();
60074             this.updateColumns();
60075             this.updateSplitters();
60076             this.updateHeaderSortState();
60077         }
60078         this.syncRowHeights();
60079         this.layout();
60080         this.fireEvent("refresh", this);
60081     },
60082
60083     handleColumnMove : function(cm, oldIndex, newIndex){
60084         this.indexMap = null;
60085         var s = this.getScrollState();
60086         this.refresh(true);
60087         this.restoreScroll(s);
60088         this.afterMove(newIndex);
60089     },
60090
60091     afterMove : function(colIndex){
60092         if(this.enableMoveAnim && Roo.enableFx){
60093             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60094         }
60095         // if multisort - fix sortOrder, and reload..
60096         if (this.grid.dataSource.multiSort) {
60097             // the we can call sort again..
60098             var dm = this.grid.dataSource;
60099             var cm = this.grid.colModel;
60100             var so = [];
60101             for(var i = 0; i < cm.config.length; i++ ) {
60102                 
60103                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60104                     continue; // dont' bother, it's not in sort list or being set.
60105                 }
60106                 
60107                 so.push(cm.config[i].dataIndex);
60108             };
60109             dm.sortOrder = so;
60110             dm.load(dm.lastOptions);
60111             
60112             
60113         }
60114         
60115     },
60116
60117     updateCell : function(dm, rowIndex, dataIndex){
60118         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60119         if(typeof colIndex == "undefined"){ // not present in grid
60120             return;
60121         }
60122         var cm = this.grid.colModel;
60123         var cell = this.getCell(rowIndex, colIndex);
60124         var cellText = this.getCellText(rowIndex, colIndex);
60125
60126         var p = {
60127             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60128             id : cm.getColumnId(colIndex),
60129             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60130         };
60131         var renderer = cm.getRenderer(colIndex);
60132         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60133         if(typeof val == "undefined" || val === "") {
60134             val = "&#160;";
60135         }
60136         cellText.innerHTML = val;
60137         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60138         this.syncRowHeights(rowIndex, rowIndex);
60139     },
60140
60141     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60142         var maxWidth = 0;
60143         if(this.grid.autoSizeHeaders){
60144             var h = this.getHeaderCellMeasure(colIndex);
60145             maxWidth = Math.max(maxWidth, h.scrollWidth);
60146         }
60147         var tb, index;
60148         if(this.cm.isLocked(colIndex)){
60149             tb = this.getLockedTable();
60150             index = colIndex;
60151         }else{
60152             tb = this.getBodyTable();
60153             index = colIndex - this.cm.getLockedCount();
60154         }
60155         if(tb && tb.rows){
60156             var rows = tb.rows;
60157             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60158             for(var i = 0; i < stopIndex; i++){
60159                 var cell = rows[i].childNodes[index].firstChild;
60160                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60161             }
60162         }
60163         return maxWidth + /*margin for error in IE*/ 5;
60164     },
60165     /**
60166      * Autofit a column to its content.
60167      * @param {Number} colIndex
60168      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60169      */
60170      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60171          if(this.cm.isHidden(colIndex)){
60172              return; // can't calc a hidden column
60173          }
60174         if(forceMinSize){
60175             var cid = this.cm.getColumnId(colIndex);
60176             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60177            if(this.grid.autoSizeHeaders){
60178                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60179            }
60180         }
60181         var newWidth = this.calcColumnWidth(colIndex);
60182         this.cm.setColumnWidth(colIndex,
60183             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60184         if(!suppressEvent){
60185             this.grid.fireEvent("columnresize", colIndex, newWidth);
60186         }
60187     },
60188
60189     /**
60190      * Autofits all columns to their content and then expands to fit any extra space in the grid
60191      */
60192      autoSizeColumns : function(){
60193         var cm = this.grid.colModel;
60194         var colCount = cm.getColumnCount();
60195         for(var i = 0; i < colCount; i++){
60196             this.autoSizeColumn(i, true, true);
60197         }
60198         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60199             this.fitColumns();
60200         }else{
60201             this.updateColumns();
60202             this.layout();
60203         }
60204     },
60205
60206     /**
60207      * Autofits all columns to the grid's width proportionate with their current size
60208      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60209      */
60210     fitColumns : function(reserveScrollSpace){
60211         var cm = this.grid.colModel;
60212         var colCount = cm.getColumnCount();
60213         var cols = [];
60214         var width = 0;
60215         var i, w;
60216         for (i = 0; i < colCount; i++){
60217             if(!cm.isHidden(i) && !cm.isFixed(i)){
60218                 w = cm.getColumnWidth(i);
60219                 cols.push(i);
60220                 cols.push(w);
60221                 width += w;
60222             }
60223         }
60224         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60225         if(reserveScrollSpace){
60226             avail -= 17;
60227         }
60228         var frac = (avail - cm.getTotalWidth())/width;
60229         while (cols.length){
60230             w = cols.pop();
60231             i = cols.pop();
60232             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60233         }
60234         this.updateColumns();
60235         this.layout();
60236     },
60237
60238     onRowSelect : function(rowIndex){
60239         var row = this.getRowComposite(rowIndex);
60240         row.addClass("x-grid-row-selected");
60241     },
60242
60243     onRowDeselect : function(rowIndex){
60244         var row = this.getRowComposite(rowIndex);
60245         row.removeClass("x-grid-row-selected");
60246     },
60247
60248     onCellSelect : function(row, col){
60249         var cell = this.getCell(row, col);
60250         if(cell){
60251             Roo.fly(cell).addClass("x-grid-cell-selected");
60252         }
60253     },
60254
60255     onCellDeselect : function(row, col){
60256         var cell = this.getCell(row, col);
60257         if(cell){
60258             Roo.fly(cell).removeClass("x-grid-cell-selected");
60259         }
60260     },
60261
60262     updateHeaderSortState : function(){
60263         
60264         // sort state can be single { field: xxx, direction : yyy}
60265         // or   { xxx=>ASC , yyy : DESC ..... }
60266         
60267         var mstate = {};
60268         if (!this.ds.multiSort) { 
60269             var state = this.ds.getSortState();
60270             if(!state){
60271                 return;
60272             }
60273             mstate[state.field] = state.direction;
60274             // FIXME... - this is not used here.. but might be elsewhere..
60275             this.sortState = state;
60276             
60277         } else {
60278             mstate = this.ds.sortToggle;
60279         }
60280         //remove existing sort classes..
60281         
60282         var sc = this.sortClasses;
60283         var hds = this.el.select(this.headerSelector).removeClass(sc);
60284         
60285         for(var f in mstate) {
60286         
60287             var sortColumn = this.cm.findColumnIndex(f);
60288             
60289             if(sortColumn != -1){
60290                 var sortDir = mstate[f];        
60291                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60292             }
60293         }
60294         
60295          
60296         
60297     },
60298
60299
60300     handleHeaderClick : function(g, index,e){
60301         
60302         Roo.log("header click");
60303         
60304         if (Roo.isTouch) {
60305             // touch events on header are handled by context
60306             this.handleHdCtx(g,index,e);
60307             return;
60308         }
60309         
60310         
60311         if(this.headersDisabled){
60312             return;
60313         }
60314         var dm = g.dataSource, cm = g.colModel;
60315         if(!cm.isSortable(index)){
60316             return;
60317         }
60318         g.stopEditing();
60319         
60320         if (dm.multiSort) {
60321             // update the sortOrder
60322             var so = [];
60323             for(var i = 0; i < cm.config.length; i++ ) {
60324                 
60325                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60326                     continue; // dont' bother, it's not in sort list or being set.
60327                 }
60328                 
60329                 so.push(cm.config[i].dataIndex);
60330             };
60331             dm.sortOrder = so;
60332         }
60333         
60334         
60335         dm.sort(cm.getDataIndex(index));
60336     },
60337
60338
60339     destroy : function(){
60340         if(this.colMenu){
60341             this.colMenu.removeAll();
60342             Roo.menu.MenuMgr.unregister(this.colMenu);
60343             this.colMenu.getEl().remove();
60344             delete this.colMenu;
60345         }
60346         if(this.hmenu){
60347             this.hmenu.removeAll();
60348             Roo.menu.MenuMgr.unregister(this.hmenu);
60349             this.hmenu.getEl().remove();
60350             delete this.hmenu;
60351         }
60352         if(this.grid.enableColumnMove){
60353             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60354             if(dds){
60355                 for(var dd in dds){
60356                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60357                         var elid = dds[dd].dragElId;
60358                         dds[dd].unreg();
60359                         Roo.get(elid).remove();
60360                     } else if(dds[dd].config.isTarget){
60361                         dds[dd].proxyTop.remove();
60362                         dds[dd].proxyBottom.remove();
60363                         dds[dd].unreg();
60364                     }
60365                     if(Roo.dd.DDM.locationCache[dd]){
60366                         delete Roo.dd.DDM.locationCache[dd];
60367                     }
60368                 }
60369                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60370             }
60371         }
60372         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60373         this.bind(null, null);
60374         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60375     },
60376
60377     handleLockChange : function(){
60378         this.refresh(true);
60379     },
60380
60381     onDenyColumnLock : function(){
60382
60383     },
60384
60385     onDenyColumnHide : function(){
60386
60387     },
60388
60389     handleHdMenuClick : function(item){
60390         var index = this.hdCtxIndex;
60391         var cm = this.cm, ds = this.ds;
60392         switch(item.id){
60393             case "asc":
60394                 ds.sort(cm.getDataIndex(index), "ASC");
60395                 break;
60396             case "desc":
60397                 ds.sort(cm.getDataIndex(index), "DESC");
60398                 break;
60399             case "lock":
60400                 var lc = cm.getLockedCount();
60401                 if(cm.getColumnCount(true) <= lc+1){
60402                     this.onDenyColumnLock();
60403                     return;
60404                 }
60405                 if(lc != index){
60406                     cm.setLocked(index, true, true);
60407                     cm.moveColumn(index, lc);
60408                     this.grid.fireEvent("columnmove", index, lc);
60409                 }else{
60410                     cm.setLocked(index, true);
60411                 }
60412             break;
60413             case "unlock":
60414                 var lc = cm.getLockedCount();
60415                 if((lc-1) != index){
60416                     cm.setLocked(index, false, true);
60417                     cm.moveColumn(index, lc-1);
60418                     this.grid.fireEvent("columnmove", index, lc-1);
60419                 }else{
60420                     cm.setLocked(index, false);
60421                 }
60422             break;
60423             case 'wider': // used to expand cols on touch..
60424             case 'narrow':
60425                 var cw = cm.getColumnWidth(index);
60426                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60427                 cw = Math.max(0, cw);
60428                 cw = Math.min(cw,4000);
60429                 cm.setColumnWidth(index, cw);
60430                 break;
60431                 
60432             default:
60433                 index = cm.getIndexById(item.id.substr(4));
60434                 if(index != -1){
60435                     if(item.checked && cm.getColumnCount(true) <= 1){
60436                         this.onDenyColumnHide();
60437                         return false;
60438                     }
60439                     cm.setHidden(index, item.checked);
60440                 }
60441         }
60442         return true;
60443     },
60444
60445     beforeColMenuShow : function(){
60446         var cm = this.cm,  colCount = cm.getColumnCount();
60447         this.colMenu.removeAll();
60448         
60449         var items = [];
60450         for(var i = 0; i < colCount; i++){
60451             items.push({
60452                 id: "col-"+cm.getColumnId(i),
60453                 text: cm.getColumnHeader(i),
60454                 checked: !cm.isHidden(i),
60455                 hideOnClick:false
60456             });
60457         }
60458         
60459         if (this.grid.sortColMenu) {
60460             items.sort(function(a,b) {
60461                 if (a.text == b.text) {
60462                     return 0;
60463                 }
60464                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60465             });
60466         }
60467         
60468         for(var i = 0; i < colCount; i++){
60469             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60470         }
60471     },
60472
60473     handleHdCtx : function(g, index, e){
60474         e.stopEvent();
60475         var hd = this.getHeaderCell(index);
60476         this.hdCtxIndex = index;
60477         var ms = this.hmenu.items, cm = this.cm;
60478         ms.get("asc").setDisabled(!cm.isSortable(index));
60479         ms.get("desc").setDisabled(!cm.isSortable(index));
60480         if(this.grid.enableColLock !== false){
60481             ms.get("lock").setDisabled(cm.isLocked(index));
60482             ms.get("unlock").setDisabled(!cm.isLocked(index));
60483         }
60484         this.hmenu.show(hd, "tl-bl");
60485     },
60486
60487     handleHdOver : function(e){
60488         var hd = this.findHeaderCell(e.getTarget());
60489         if(hd && !this.headersDisabled){
60490             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60491                this.fly(hd).addClass("x-grid-hd-over");
60492             }
60493         }
60494     },
60495
60496     handleHdOut : function(e){
60497         var hd = this.findHeaderCell(e.getTarget());
60498         if(hd){
60499             this.fly(hd).removeClass("x-grid-hd-over");
60500         }
60501     },
60502
60503     handleSplitDblClick : function(e, t){
60504         var i = this.getCellIndex(t);
60505         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60506             this.autoSizeColumn(i, true);
60507             this.layout();
60508         }
60509     },
60510
60511     render : function(){
60512
60513         var cm = this.cm;
60514         var colCount = cm.getColumnCount();
60515
60516         if(this.grid.monitorWindowResize === true){
60517             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60518         }
60519         var header = this.renderHeaders();
60520         var body = this.templates.body.apply({rows:""});
60521         var html = this.templates.master.apply({
60522             lockedBody: body,
60523             body: body,
60524             lockedHeader: header[0],
60525             header: header[1]
60526         });
60527
60528         //this.updateColumns();
60529
60530         this.grid.getGridEl().dom.innerHTML = html;
60531
60532         this.initElements();
60533         
60534         // a kludge to fix the random scolling effect in webkit
60535         this.el.on("scroll", function() {
60536             this.el.dom.scrollTop=0; // hopefully not recursive..
60537         },this);
60538
60539         this.scroller.on("scroll", this.handleScroll, this);
60540         this.lockedBody.on("mousewheel", this.handleWheel, this);
60541         this.mainBody.on("mousewheel", this.handleWheel, this);
60542
60543         this.mainHd.on("mouseover", this.handleHdOver, this);
60544         this.mainHd.on("mouseout", this.handleHdOut, this);
60545         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60546                 {delegate: "."+this.splitClass});
60547
60548         this.lockedHd.on("mouseover", this.handleHdOver, this);
60549         this.lockedHd.on("mouseout", this.handleHdOut, this);
60550         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60551                 {delegate: "."+this.splitClass});
60552
60553         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60554             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60555         }
60556
60557         this.updateSplitters();
60558
60559         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60560             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60561             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60562         }
60563
60564         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60565             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60566             this.hmenu.add(
60567                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60568                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60569             );
60570             if(this.grid.enableColLock !== false){
60571                 this.hmenu.add('-',
60572                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60573                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60574                 );
60575             }
60576             if (Roo.isTouch) {
60577                  this.hmenu.add('-',
60578                     {id:"wider", text: this.columnsWiderText},
60579                     {id:"narrow", text: this.columnsNarrowText }
60580                 );
60581                 
60582                  
60583             }
60584             
60585             if(this.grid.enableColumnHide !== false){
60586
60587                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60588                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60589                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60590
60591                 this.hmenu.add('-',
60592                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60593                 );
60594             }
60595             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60596
60597             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60598         }
60599
60600         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60601             this.dd = new Roo.grid.GridDragZone(this.grid, {
60602                 ddGroup : this.grid.ddGroup || 'GridDD'
60603             });
60604             
60605         }
60606
60607         /*
60608         for(var i = 0; i < colCount; i++){
60609             if(cm.isHidden(i)){
60610                 this.hideColumn(i);
60611             }
60612             if(cm.config[i].align){
60613                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60614                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60615             }
60616         }*/
60617         
60618         this.updateHeaderSortState();
60619
60620         this.beforeInitialResize();
60621         this.layout(true);
60622
60623         // two part rendering gives faster view to the user
60624         this.renderPhase2.defer(1, this);
60625     },
60626
60627     renderPhase2 : function(){
60628         // render the rows now
60629         this.refresh();
60630         if(this.grid.autoSizeColumns){
60631             this.autoSizeColumns();
60632         }
60633     },
60634
60635     beforeInitialResize : function(){
60636
60637     },
60638
60639     onColumnSplitterMoved : function(i, w){
60640         this.userResized = true;
60641         var cm = this.grid.colModel;
60642         cm.setColumnWidth(i, w, true);
60643         var cid = cm.getColumnId(i);
60644         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60645         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60646         this.updateSplitters();
60647         this.layout();
60648         this.grid.fireEvent("columnresize", i, w);
60649     },
60650
60651     syncRowHeights : function(startIndex, endIndex){
60652         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60653             startIndex = startIndex || 0;
60654             var mrows = this.getBodyTable().rows;
60655             var lrows = this.getLockedTable().rows;
60656             var len = mrows.length-1;
60657             endIndex = Math.min(endIndex || len, len);
60658             for(var i = startIndex; i <= endIndex; i++){
60659                 var m = mrows[i], l = lrows[i];
60660                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60661                 m.style.height = l.style.height = h + "px";
60662             }
60663         }
60664     },
60665
60666     layout : function(initialRender, is2ndPass)
60667     {
60668         var g = this.grid;
60669         var auto = g.autoHeight;
60670         var scrollOffset = 16;
60671         var c = g.getGridEl(), cm = this.cm,
60672                 expandCol = g.autoExpandColumn,
60673                 gv = this;
60674         //c.beginMeasure();
60675
60676         if(!c.dom.offsetWidth){ // display:none?
60677             if(initialRender){
60678                 this.lockedWrap.show();
60679                 this.mainWrap.show();
60680             }
60681             return;
60682         }
60683
60684         var hasLock = this.cm.isLocked(0);
60685
60686         var tbh = this.headerPanel.getHeight();
60687         var bbh = this.footerPanel.getHeight();
60688
60689         if(auto){
60690             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60691             var newHeight = ch + c.getBorderWidth("tb");
60692             if(g.maxHeight){
60693                 newHeight = Math.min(g.maxHeight, newHeight);
60694             }
60695             c.setHeight(newHeight);
60696         }
60697
60698         if(g.autoWidth){
60699             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60700         }
60701
60702         var s = this.scroller;
60703
60704         var csize = c.getSize(true);
60705
60706         this.el.setSize(csize.width, csize.height);
60707
60708         this.headerPanel.setWidth(csize.width);
60709         this.footerPanel.setWidth(csize.width);
60710
60711         var hdHeight = this.mainHd.getHeight();
60712         var vw = csize.width;
60713         var vh = csize.height - (tbh + bbh);
60714
60715         s.setSize(vw, vh);
60716
60717         var bt = this.getBodyTable();
60718         
60719         if(cm.getLockedCount() == cm.config.length){
60720             bt = this.getLockedTable();
60721         }
60722         
60723         var ltWidth = hasLock ?
60724                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60725
60726         var scrollHeight = bt.offsetHeight;
60727         var scrollWidth = ltWidth + bt.offsetWidth;
60728         var vscroll = false, hscroll = false;
60729
60730         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60731
60732         var lw = this.lockedWrap, mw = this.mainWrap;
60733         var lb = this.lockedBody, mb = this.mainBody;
60734
60735         setTimeout(function(){
60736             var t = s.dom.offsetTop;
60737             var w = s.dom.clientWidth,
60738                 h = s.dom.clientHeight;
60739
60740             lw.setTop(t);
60741             lw.setSize(ltWidth, h);
60742
60743             mw.setLeftTop(ltWidth, t);
60744             mw.setSize(w-ltWidth, h);
60745
60746             lb.setHeight(h-hdHeight);
60747             mb.setHeight(h-hdHeight);
60748
60749             if(is2ndPass !== true && !gv.userResized && expandCol){
60750                 // high speed resize without full column calculation
60751                 
60752                 var ci = cm.getIndexById(expandCol);
60753                 if (ci < 0) {
60754                     ci = cm.findColumnIndex(expandCol);
60755                 }
60756                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60757                 var expandId = cm.getColumnId(ci);
60758                 var  tw = cm.getTotalWidth(false);
60759                 var currentWidth = cm.getColumnWidth(ci);
60760                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60761                 if(currentWidth != cw){
60762                     cm.setColumnWidth(ci, cw, true);
60763                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60764                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60765                     gv.updateSplitters();
60766                     gv.layout(false, true);
60767                 }
60768             }
60769
60770             if(initialRender){
60771                 lw.show();
60772                 mw.show();
60773             }
60774             //c.endMeasure();
60775         }, 10);
60776     },
60777
60778     onWindowResize : function(){
60779         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60780             return;
60781         }
60782         this.layout();
60783     },
60784
60785     appendFooter : function(parentEl){
60786         return null;
60787     },
60788
60789     sortAscText : "Sort Ascending",
60790     sortDescText : "Sort Descending",
60791     lockText : "Lock Column",
60792     unlockText : "Unlock Column",
60793     columnsText : "Columns",
60794  
60795     columnsWiderText : "Wider",
60796     columnsNarrowText : "Thinner"
60797 });
60798
60799
60800 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60801     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60802     this.proxy.el.addClass('x-grid3-col-dd');
60803 };
60804
60805 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60806     handleMouseDown : function(e){
60807
60808     },
60809
60810     callHandleMouseDown : function(e){
60811         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60812     }
60813 });
60814 /*
60815  * Based on:
60816  * Ext JS Library 1.1.1
60817  * Copyright(c) 2006-2007, Ext JS, LLC.
60818  *
60819  * Originally Released Under LGPL - original licence link has changed is not relivant.
60820  *
60821  * Fork - LGPL
60822  * <script type="text/javascript">
60823  */
60824  /**
60825  * @extends Roo.dd.DDProxy
60826  * @class Roo.grid.SplitDragZone
60827  * Support for Column Header resizing
60828  * @constructor
60829  * @param {Object} config
60830  */
60831 // private
60832 // This is a support class used internally by the Grid components
60833 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60834     this.grid = grid;
60835     this.view = grid.getView();
60836     this.proxy = this.view.resizeProxy;
60837     Roo.grid.SplitDragZone.superclass.constructor.call(
60838         this,
60839         hd, // ID
60840         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60841         {  // CONFIG
60842             dragElId : Roo.id(this.proxy.dom),
60843             resizeFrame:false
60844         }
60845     );
60846     
60847     this.setHandleElId(Roo.id(hd));
60848     if (hd2 !== false) {
60849         this.setOuterHandleElId(Roo.id(hd2));
60850     }
60851     
60852     this.scroll = false;
60853 };
60854 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60855     fly: Roo.Element.fly,
60856
60857     b4StartDrag : function(x, y){
60858         this.view.headersDisabled = true;
60859         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60860                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60861         );
60862         this.proxy.setHeight(h);
60863         
60864         // for old system colWidth really stored the actual width?
60865         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60866         // which in reality did not work.. - it worked only for fixed sizes
60867         // for resizable we need to use actual sizes.
60868         var w = this.cm.getColumnWidth(this.cellIndex);
60869         if (!this.view.mainWrap) {
60870             // bootstrap.
60871             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60872         }
60873         
60874         
60875         
60876         // this was w-this.grid.minColumnWidth;
60877         // doesnt really make sense? - w = thie curren width or the rendered one?
60878         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60879         this.resetConstraints();
60880         this.setXConstraint(minw, 1000);
60881         this.setYConstraint(0, 0);
60882         this.minX = x - minw;
60883         this.maxX = x + 1000;
60884         this.startPos = x;
60885         if (!this.view.mainWrap) { // this is Bootstrap code..
60886             this.getDragEl().style.display='block';
60887         }
60888         
60889         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60890     },
60891
60892
60893     handleMouseDown : function(e){
60894         ev = Roo.EventObject.setEvent(e);
60895         var t = this.fly(ev.getTarget());
60896         if(t.hasClass("x-grid-split")){
60897             this.cellIndex = this.view.getCellIndex(t.dom);
60898             this.split = t.dom;
60899             this.cm = this.grid.colModel;
60900             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60901                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60902             }
60903         }
60904     },
60905
60906     endDrag : function(e){
60907         this.view.headersDisabled = false;
60908         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60909         var diff = endX - this.startPos;
60910         // 
60911         var w = this.cm.getColumnWidth(this.cellIndex);
60912         if (!this.view.mainWrap) {
60913             w = 0;
60914         }
60915         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60916     },
60917
60918     autoOffset : function(){
60919         this.setDelta(0,0);
60920     }
60921 });/*
60922  * Based on:
60923  * Ext JS Library 1.1.1
60924  * Copyright(c) 2006-2007, Ext JS, LLC.
60925  *
60926  * Originally Released Under LGPL - original licence link has changed is not relivant.
60927  *
60928  * Fork - LGPL
60929  * <script type="text/javascript">
60930  */
60931  
60932 // private
60933 // This is a support class used internally by the Grid components
60934 Roo.grid.GridDragZone = function(grid, config){
60935     this.view = grid.getView();
60936     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60937     if(this.view.lockedBody){
60938         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60939         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60940     }
60941     this.scroll = false;
60942     this.grid = grid;
60943     this.ddel = document.createElement('div');
60944     this.ddel.className = 'x-grid-dd-wrap';
60945 };
60946
60947 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60948     ddGroup : "GridDD",
60949
60950     getDragData : function(e){
60951         var t = Roo.lib.Event.getTarget(e);
60952         var rowIndex = this.view.findRowIndex(t);
60953         var sm = this.grid.selModel;
60954             
60955         //Roo.log(rowIndex);
60956         
60957         if (sm.getSelectedCell) {
60958             // cell selection..
60959             if (!sm.getSelectedCell()) {
60960                 return false;
60961             }
60962             if (rowIndex != sm.getSelectedCell()[0]) {
60963                 return false;
60964             }
60965         
60966         }
60967         if (sm.getSelections && sm.getSelections().length < 1) {
60968             return false;
60969         }
60970         
60971         
60972         // before it used to all dragging of unseleted... - now we dont do that.
60973         if(rowIndex !== false){
60974             
60975             // if editorgrid.. 
60976             
60977             
60978             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60979                
60980             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60981               //  
60982             //}
60983             if (e.hasModifier()){
60984                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60985             }
60986             
60987             Roo.log("getDragData");
60988             
60989             return {
60990                 grid: this.grid,
60991                 ddel: this.ddel,
60992                 rowIndex: rowIndex,
60993                 selections: sm.getSelections ? sm.getSelections() : (
60994                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60995             };
60996         }
60997         return false;
60998     },
60999     
61000     
61001     onInitDrag : function(e){
61002         var data = this.dragData;
61003         this.ddel.innerHTML = this.grid.getDragDropText();
61004         this.proxy.update(this.ddel);
61005         // fire start drag?
61006     },
61007
61008     afterRepair : function(){
61009         this.dragging = false;
61010     },
61011
61012     getRepairXY : function(e, data){
61013         return false;
61014     },
61015
61016     onEndDrag : function(data, e){
61017         // fire end drag?
61018     },
61019
61020     onValidDrop : function(dd, e, id){
61021         // fire drag drop?
61022         this.hideProxy();
61023     },
61024
61025     beforeInvalidDrop : function(e, id){
61026
61027     }
61028 });/*
61029  * Based on:
61030  * Ext JS Library 1.1.1
61031  * Copyright(c) 2006-2007, Ext JS, LLC.
61032  *
61033  * Originally Released Under LGPL - original licence link has changed is not relivant.
61034  *
61035  * Fork - LGPL
61036  * <script type="text/javascript">
61037  */
61038  
61039
61040 /**
61041  * @class Roo.grid.ColumnModel
61042  * @extends Roo.util.Observable
61043  * This is the default implementation of a ColumnModel used by the Grid. It defines
61044  * the columns in the grid.
61045  * <br>Usage:<br>
61046  <pre><code>
61047  var colModel = new Roo.grid.ColumnModel([
61048         {header: "Ticker", width: 60, sortable: true, locked: true},
61049         {header: "Company Name", width: 150, sortable: true},
61050         {header: "Market Cap.", width: 100, sortable: true},
61051         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61052         {header: "Employees", width: 100, sortable: true, resizable: false}
61053  ]);
61054  </code></pre>
61055  * <p>
61056  
61057  * The config options listed for this class are options which may appear in each
61058  * individual column definition.
61059  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61060  * @constructor
61061  * @param {Object} config An Array of column config objects. See this class's
61062  * config objects for details.
61063 */
61064 Roo.grid.ColumnModel = function(config){
61065         /**
61066      * The config passed into the constructor
61067      */
61068     this.config = []; //config;
61069     this.lookup = {};
61070
61071     // if no id, create one
61072     // if the column does not have a dataIndex mapping,
61073     // map it to the order it is in the config
61074     for(var i = 0, len = config.length; i < len; i++){
61075         this.addColumn(config[i]);
61076         
61077     }
61078
61079     /**
61080      * The width of columns which have no width specified (defaults to 100)
61081      * @type Number
61082      */
61083     this.defaultWidth = 100;
61084
61085     /**
61086      * Default sortable of columns which have no sortable specified (defaults to false)
61087      * @type Boolean
61088      */
61089     this.defaultSortable = false;
61090
61091     this.addEvents({
61092         /**
61093              * @event widthchange
61094              * Fires when the width of a column changes.
61095              * @param {ColumnModel} this
61096              * @param {Number} columnIndex The column index
61097              * @param {Number} newWidth The new width
61098              */
61099             "widthchange": true,
61100         /**
61101              * @event headerchange
61102              * Fires when the text of a header changes.
61103              * @param {ColumnModel} this
61104              * @param {Number} columnIndex The column index
61105              * @param {Number} newText The new header text
61106              */
61107             "headerchange": true,
61108         /**
61109              * @event hiddenchange
61110              * Fires when a column is hidden or "unhidden".
61111              * @param {ColumnModel} this
61112              * @param {Number} columnIndex The column index
61113              * @param {Boolean} hidden true if hidden, false otherwise
61114              */
61115             "hiddenchange": true,
61116             /**
61117          * @event columnmoved
61118          * Fires when a column is moved.
61119          * @param {ColumnModel} this
61120          * @param {Number} oldIndex
61121          * @param {Number} newIndex
61122          */
61123         "columnmoved" : true,
61124         /**
61125          * @event columlockchange
61126          * Fires when a column's locked state is changed
61127          * @param {ColumnModel} this
61128          * @param {Number} colIndex
61129          * @param {Boolean} locked true if locked
61130          */
61131         "columnlockchange" : true
61132     });
61133     Roo.grid.ColumnModel.superclass.constructor.call(this);
61134 };
61135 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61136     /**
61137      * @cfg {String} header The header text to display in the Grid view.
61138      */
61139         /**
61140      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61141      */
61142         /**
61143      * @cfg {String} smHeader Header at Bootsrap Small width
61144      */
61145         /**
61146      * @cfg {String} mdHeader Header at Bootsrap Medium width
61147      */
61148         /**
61149      * @cfg {String} lgHeader Header at Bootsrap Large width
61150      */
61151         /**
61152      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61153      */
61154     /**
61155      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61156      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61157      * specified, the column's index is used as an index into the Record's data Array.
61158      */
61159     /**
61160      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61161      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61162      */
61163     /**
61164      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61165      * Defaults to the value of the {@link #defaultSortable} property.
61166      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61167      */
61168     /**
61169      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61170      */
61171     /**
61172      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61173      */
61174     /**
61175      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61176      */
61177     /**
61178      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61179      */
61180     /**
61181      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61182      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61183      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61184      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61185      */
61186        /**
61187      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61188      */
61189     /**
61190      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61191      */
61192     /**
61193      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61194      */
61195     /**
61196      * @cfg {String} cursor (Optional)
61197      */
61198     /**
61199      * @cfg {String} tooltip (Optional)
61200      */
61201     /**
61202      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61203      */
61204     /**
61205      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61206      */
61207     /**
61208      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61209      */
61210     /**
61211      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61212      */
61213         /**
61214      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61215      */
61216     /**
61217      * Returns the id of the column at the specified index.
61218      * @param {Number} index The column index
61219      * @return {String} the id
61220      */
61221     getColumnId : function(index){
61222         return this.config[index].id;
61223     },
61224
61225     /**
61226      * Returns the column for a specified id.
61227      * @param {String} id The column id
61228      * @return {Object} the column
61229      */
61230     getColumnById : function(id){
61231         return this.lookup[id];
61232     },
61233
61234     
61235     /**
61236      * Returns the column Object for a specified dataIndex.
61237      * @param {String} dataIndex The column dataIndex
61238      * @return {Object|Boolean} the column or false if not found
61239      */
61240     getColumnByDataIndex: function(dataIndex){
61241         var index = this.findColumnIndex(dataIndex);
61242         return index > -1 ? this.config[index] : false;
61243     },
61244     
61245     /**
61246      * Returns the index for a specified column id.
61247      * @param {String} id The column id
61248      * @return {Number} the index, or -1 if not found
61249      */
61250     getIndexById : function(id){
61251         for(var i = 0, len = this.config.length; i < len; i++){
61252             if(this.config[i].id == id){
61253                 return i;
61254             }
61255         }
61256         return -1;
61257     },
61258     
61259     /**
61260      * Returns the index for a specified column dataIndex.
61261      * @param {String} dataIndex The column dataIndex
61262      * @return {Number} the index, or -1 if not found
61263      */
61264     
61265     findColumnIndex : function(dataIndex){
61266         for(var i = 0, len = this.config.length; i < len; i++){
61267             if(this.config[i].dataIndex == dataIndex){
61268                 return i;
61269             }
61270         }
61271         return -1;
61272     },
61273     
61274     
61275     moveColumn : function(oldIndex, newIndex){
61276         var c = this.config[oldIndex];
61277         this.config.splice(oldIndex, 1);
61278         this.config.splice(newIndex, 0, c);
61279         this.dataMap = null;
61280         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61281     },
61282
61283     isLocked : function(colIndex){
61284         return this.config[colIndex].locked === true;
61285     },
61286
61287     setLocked : function(colIndex, value, suppressEvent){
61288         if(this.isLocked(colIndex) == value){
61289             return;
61290         }
61291         this.config[colIndex].locked = value;
61292         if(!suppressEvent){
61293             this.fireEvent("columnlockchange", this, colIndex, value);
61294         }
61295     },
61296
61297     getTotalLockedWidth : function(){
61298         var totalWidth = 0;
61299         for(var i = 0; i < this.config.length; i++){
61300             if(this.isLocked(i) && !this.isHidden(i)){
61301                 this.totalWidth += this.getColumnWidth(i);
61302             }
61303         }
61304         return totalWidth;
61305     },
61306
61307     getLockedCount : function(){
61308         for(var i = 0, len = this.config.length; i < len; i++){
61309             if(!this.isLocked(i)){
61310                 return i;
61311             }
61312         }
61313         
61314         return this.config.length;
61315     },
61316
61317     /**
61318      * Returns the number of columns.
61319      * @return {Number}
61320      */
61321     getColumnCount : function(visibleOnly){
61322         if(visibleOnly === true){
61323             var c = 0;
61324             for(var i = 0, len = this.config.length; i < len; i++){
61325                 if(!this.isHidden(i)){
61326                     c++;
61327                 }
61328             }
61329             return c;
61330         }
61331         return this.config.length;
61332     },
61333
61334     /**
61335      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61336      * @param {Function} fn
61337      * @param {Object} scope (optional)
61338      * @return {Array} result
61339      */
61340     getColumnsBy : function(fn, scope){
61341         var r = [];
61342         for(var i = 0, len = this.config.length; i < len; i++){
61343             var c = this.config[i];
61344             if(fn.call(scope||this, c, i) === true){
61345                 r[r.length] = c;
61346             }
61347         }
61348         return r;
61349     },
61350
61351     /**
61352      * Returns true if the specified column is sortable.
61353      * @param {Number} col The column index
61354      * @return {Boolean}
61355      */
61356     isSortable : function(col){
61357         if(typeof this.config[col].sortable == "undefined"){
61358             return this.defaultSortable;
61359         }
61360         return this.config[col].sortable;
61361     },
61362
61363     /**
61364      * Returns the rendering (formatting) function defined for the column.
61365      * @param {Number} col The column index.
61366      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61367      */
61368     getRenderer : function(col){
61369         if(!this.config[col].renderer){
61370             return Roo.grid.ColumnModel.defaultRenderer;
61371         }
61372         return this.config[col].renderer;
61373     },
61374
61375     /**
61376      * Sets the rendering (formatting) function for a column.
61377      * @param {Number} col The column index
61378      * @param {Function} fn The function to use to process the cell's raw data
61379      * to return HTML markup for the grid view. The render function is called with
61380      * the following parameters:<ul>
61381      * <li>Data value.</li>
61382      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61383      * <li>css A CSS style string to apply to the table cell.</li>
61384      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61385      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61386      * <li>Row index</li>
61387      * <li>Column index</li>
61388      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61389      */
61390     setRenderer : function(col, fn){
61391         this.config[col].renderer = fn;
61392     },
61393
61394     /**
61395      * Returns the width for the specified column.
61396      * @param {Number} col The column index
61397      * @param (optional) {String} gridSize bootstrap width size.
61398      * @return {Number}
61399      */
61400     getColumnWidth : function(col, gridSize)
61401         {
61402                 var cfg = this.config[col];
61403                 
61404                 if (typeof(gridSize) == 'undefined') {
61405                         return cfg.width * 1 || this.defaultWidth;
61406                 }
61407                 if (gridSize === false) { // if we set it..
61408                         return cfg.width || false;
61409                 }
61410                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61411                 
61412                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61413                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61414                                 continue;
61415                         }
61416                         return cfg[ sizes[i] ];
61417                 }
61418                 return 1;
61419                 
61420     },
61421
61422     /**
61423      * Sets the width for a column.
61424      * @param {Number} col The column index
61425      * @param {Number} width The new width
61426      */
61427     setColumnWidth : function(col, width, suppressEvent){
61428         this.config[col].width = width;
61429         this.totalWidth = null;
61430         if(!suppressEvent){
61431              this.fireEvent("widthchange", this, col, width);
61432         }
61433     },
61434
61435     /**
61436      * Returns the total width of all columns.
61437      * @param {Boolean} includeHidden True to include hidden column widths
61438      * @return {Number}
61439      */
61440     getTotalWidth : function(includeHidden){
61441         if(!this.totalWidth){
61442             this.totalWidth = 0;
61443             for(var i = 0, len = this.config.length; i < len; i++){
61444                 if(includeHidden || !this.isHidden(i)){
61445                     this.totalWidth += this.getColumnWidth(i);
61446                 }
61447             }
61448         }
61449         return this.totalWidth;
61450     },
61451
61452     /**
61453      * Returns the header for the specified column.
61454      * @param {Number} col The column index
61455      * @return {String}
61456      */
61457     getColumnHeader : function(col){
61458         return this.config[col].header;
61459     },
61460
61461     /**
61462      * Sets the header for a column.
61463      * @param {Number} col The column index
61464      * @param {String} header The new header
61465      */
61466     setColumnHeader : function(col, header){
61467         this.config[col].header = header;
61468         this.fireEvent("headerchange", this, col, header);
61469     },
61470
61471     /**
61472      * Returns the tooltip for the specified column.
61473      * @param {Number} col The column index
61474      * @return {String}
61475      */
61476     getColumnTooltip : function(col){
61477             return this.config[col].tooltip;
61478     },
61479     /**
61480      * Sets the tooltip for a column.
61481      * @param {Number} col The column index
61482      * @param {String} tooltip The new tooltip
61483      */
61484     setColumnTooltip : function(col, tooltip){
61485             this.config[col].tooltip = tooltip;
61486     },
61487
61488     /**
61489      * Returns the dataIndex for the specified column.
61490      * @param {Number} col The column index
61491      * @return {Number}
61492      */
61493     getDataIndex : function(col){
61494         return this.config[col].dataIndex;
61495     },
61496
61497     /**
61498      * Sets the dataIndex for a column.
61499      * @param {Number} col The column index
61500      * @param {Number} dataIndex The new dataIndex
61501      */
61502     setDataIndex : function(col, dataIndex){
61503         this.config[col].dataIndex = dataIndex;
61504     },
61505
61506     
61507     
61508     /**
61509      * Returns true if the cell is editable.
61510      * @param {Number} colIndex The column index
61511      * @param {Number} rowIndex The row index - this is nto actually used..?
61512      * @return {Boolean}
61513      */
61514     isCellEditable : function(colIndex, rowIndex){
61515         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61516     },
61517
61518     /**
61519      * Returns the editor defined for the cell/column.
61520      * return false or null to disable editing.
61521      * @param {Number} colIndex The column index
61522      * @param {Number} rowIndex The row index
61523      * @return {Object}
61524      */
61525     getCellEditor : function(colIndex, rowIndex){
61526         return this.config[colIndex].editor;
61527     },
61528
61529     /**
61530      * Sets if a column is editable.
61531      * @param {Number} col The column index
61532      * @param {Boolean} editable True if the column is editable
61533      */
61534     setEditable : function(col, editable){
61535         this.config[col].editable = editable;
61536     },
61537
61538
61539     /**
61540      * Returns true if the column is hidden.
61541      * @param {Number} colIndex The column index
61542      * @return {Boolean}
61543      */
61544     isHidden : function(colIndex){
61545         return this.config[colIndex].hidden;
61546     },
61547
61548
61549     /**
61550      * Returns true if the column width cannot be changed
61551      */
61552     isFixed : function(colIndex){
61553         return this.config[colIndex].fixed;
61554     },
61555
61556     /**
61557      * Returns true if the column can be resized
61558      * @return {Boolean}
61559      */
61560     isResizable : function(colIndex){
61561         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61562     },
61563     /**
61564      * Sets if a column is hidden.
61565      * @param {Number} colIndex The column index
61566      * @param {Boolean} hidden True if the column is hidden
61567      */
61568     setHidden : function(colIndex, hidden){
61569         this.config[colIndex].hidden = hidden;
61570         this.totalWidth = null;
61571         this.fireEvent("hiddenchange", this, colIndex, hidden);
61572     },
61573
61574     /**
61575      * Sets the editor for a column.
61576      * @param {Number} col The column index
61577      * @param {Object} editor The editor object
61578      */
61579     setEditor : function(col, editor){
61580         this.config[col].editor = editor;
61581     },
61582     /**
61583      * Add a column (experimental...) - defaults to adding to the end..
61584      * @param {Object} config 
61585     */
61586     addColumn : function(c)
61587     {
61588     
61589         var i = this.config.length;
61590         this.config[i] = c;
61591         
61592         if(typeof c.dataIndex == "undefined"){
61593             c.dataIndex = i;
61594         }
61595         if(typeof c.renderer == "string"){
61596             c.renderer = Roo.util.Format[c.renderer];
61597         }
61598         if(typeof c.id == "undefined"){
61599             c.id = Roo.id();
61600         }
61601         if(c.editor && c.editor.xtype){
61602             c.editor  = Roo.factory(c.editor, Roo.grid);
61603         }
61604         if(c.editor && c.editor.isFormField){
61605             c.editor = new Roo.grid.GridEditor(c.editor);
61606         }
61607         this.lookup[c.id] = c;
61608     }
61609     
61610 });
61611
61612 Roo.grid.ColumnModel.defaultRenderer = function(value)
61613 {
61614     if(typeof value == "object") {
61615         return value;
61616     }
61617         if(typeof value == "string" && value.length < 1){
61618             return "&#160;";
61619         }
61620     
61621         return String.format("{0}", value);
61622 };
61623
61624 // Alias for backwards compatibility
61625 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61626 /*
61627  * Based on:
61628  * Ext JS Library 1.1.1
61629  * Copyright(c) 2006-2007, Ext JS, LLC.
61630  *
61631  * Originally Released Under LGPL - original licence link has changed is not relivant.
61632  *
61633  * Fork - LGPL
61634  * <script type="text/javascript">
61635  */
61636
61637 /**
61638  * @class Roo.grid.AbstractSelectionModel
61639  * @extends Roo.util.Observable
61640  * @abstract
61641  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61642  * implemented by descendant classes.  This class should not be directly instantiated.
61643  * @constructor
61644  */
61645 Roo.grid.AbstractSelectionModel = function(){
61646     this.locked = false;
61647     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61648 };
61649
61650 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61651     /** @ignore Called by the grid automatically. Do not call directly. */
61652     init : function(grid){
61653         this.grid = grid;
61654         this.initEvents();
61655     },
61656
61657     /**
61658      * Locks the selections.
61659      */
61660     lock : function(){
61661         this.locked = true;
61662     },
61663
61664     /**
61665      * Unlocks the selections.
61666      */
61667     unlock : function(){
61668         this.locked = false;
61669     },
61670
61671     /**
61672      * Returns true if the selections are locked.
61673      * @return {Boolean}
61674      */
61675     isLocked : function(){
61676         return this.locked;
61677     }
61678 });/*
61679  * Based on:
61680  * Ext JS Library 1.1.1
61681  * Copyright(c) 2006-2007, Ext JS, LLC.
61682  *
61683  * Originally Released Under LGPL - original licence link has changed is not relivant.
61684  *
61685  * Fork - LGPL
61686  * <script type="text/javascript">
61687  */
61688 /**
61689  * @extends Roo.grid.AbstractSelectionModel
61690  * @class Roo.grid.RowSelectionModel
61691  * The default SelectionModel used by {@link Roo.grid.Grid}.
61692  * It supports multiple selections and keyboard selection/navigation. 
61693  * @constructor
61694  * @param {Object} config
61695  */
61696 Roo.grid.RowSelectionModel = function(config){
61697     Roo.apply(this, config);
61698     this.selections = new Roo.util.MixedCollection(false, function(o){
61699         return o.id;
61700     });
61701
61702     this.last = false;
61703     this.lastActive = false;
61704
61705     this.addEvents({
61706         /**
61707         * @event selectionchange
61708         * Fires when the selection changes
61709         * @param {SelectionModel} this
61710         */
61711        "selectionchange" : true,
61712        /**
61713         * @event afterselectionchange
61714         * Fires after the selection changes (eg. by key press or clicking)
61715         * @param {SelectionModel} this
61716         */
61717        "afterselectionchange" : true,
61718        /**
61719         * @event beforerowselect
61720         * Fires when a row is selected being selected, return false to cancel.
61721         * @param {SelectionModel} this
61722         * @param {Number} rowIndex The selected index
61723         * @param {Boolean} keepExisting False if other selections will be cleared
61724         */
61725        "beforerowselect" : true,
61726        /**
61727         * @event rowselect
61728         * Fires when a row is selected.
61729         * @param {SelectionModel} this
61730         * @param {Number} rowIndex The selected index
61731         * @param {Roo.data.Record} r The record
61732         */
61733        "rowselect" : true,
61734        /**
61735         * @event rowdeselect
61736         * Fires when a row is deselected.
61737         * @param {SelectionModel} this
61738         * @param {Number} rowIndex The selected index
61739         */
61740         "rowdeselect" : true
61741     });
61742     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61743     this.locked = false;
61744 };
61745
61746 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61747     /**
61748      * @cfg {Boolean} singleSelect
61749      * True to allow selection of only one row at a time (defaults to false)
61750      */
61751     singleSelect : false,
61752
61753     // private
61754     initEvents : function(){
61755
61756         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61757             this.grid.on("mousedown", this.handleMouseDown, this);
61758         }else{ // allow click to work like normal
61759             this.grid.on("rowclick", this.handleDragableRowClick, this);
61760         }
61761         // bootstrap does not have a view..
61762         var view = this.grid.view ? this.grid.view : this.grid;
61763         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61764             "up" : function(e){
61765                 if(!e.shiftKey){
61766                     this.selectPrevious(e.shiftKey);
61767                 }else if(this.last !== false && this.lastActive !== false){
61768                     var last = this.last;
61769                     this.selectRange(this.last,  this.lastActive-1);
61770                     view.focusRow(this.lastActive);
61771                     if(last !== false){
61772                         this.last = last;
61773                     }
61774                 }else{
61775                     this.selectFirstRow();
61776                 }
61777                 this.fireEvent("afterselectionchange", this);
61778             },
61779             "down" : function(e){
61780                 if(!e.shiftKey){
61781                     this.selectNext(e.shiftKey);
61782                 }else if(this.last !== false && this.lastActive !== false){
61783                     var last = this.last;
61784                     this.selectRange(this.last,  this.lastActive+1);
61785                     view.focusRow(this.lastActive);
61786                     if(last !== false){
61787                         this.last = last;
61788                     }
61789                 }else{
61790                     this.selectFirstRow();
61791                 }
61792                 this.fireEvent("afterselectionchange", this);
61793             },
61794             scope: this
61795         });
61796
61797          
61798         view.on("refresh", this.onRefresh, this);
61799         view.on("rowupdated", this.onRowUpdated, this);
61800         view.on("rowremoved", this.onRemove, this);
61801     },
61802
61803     // private
61804     onRefresh : function(){
61805         var ds = this.grid.ds, i, v = this.grid.view;
61806         var s = this.selections;
61807         s.each(function(r){
61808             if((i = ds.indexOfId(r.id)) != -1){
61809                 v.onRowSelect(i);
61810                 s.add(ds.getAt(i)); // updating the selection relate data
61811             }else{
61812                 s.remove(r);
61813             }
61814         });
61815     },
61816
61817     // private
61818     onRemove : function(v, index, r){
61819         this.selections.remove(r);
61820     },
61821
61822     // private
61823     onRowUpdated : function(v, index, r){
61824         if(this.isSelected(r)){
61825             v.onRowSelect(index);
61826         }
61827     },
61828
61829     /**
61830      * Select records.
61831      * @param {Array} records The records to select
61832      * @param {Boolean} keepExisting (optional) True to keep existing selections
61833      */
61834     selectRecords : function(records, keepExisting){
61835         if(!keepExisting){
61836             this.clearSelections();
61837         }
61838         var ds = this.grid.ds;
61839         for(var i = 0, len = records.length; i < len; i++){
61840             this.selectRow(ds.indexOf(records[i]), true);
61841         }
61842     },
61843
61844     /**
61845      * Gets the number of selected rows.
61846      * @return {Number}
61847      */
61848     getCount : function(){
61849         return this.selections.length;
61850     },
61851
61852     /**
61853      * Selects the first row in the grid.
61854      */
61855     selectFirstRow : function(){
61856         this.selectRow(0);
61857     },
61858
61859     /**
61860      * Select the last row.
61861      * @param {Boolean} keepExisting (optional) True to keep existing selections
61862      */
61863     selectLastRow : function(keepExisting){
61864         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61865     },
61866
61867     /**
61868      * Selects the row immediately following the last selected row.
61869      * @param {Boolean} keepExisting (optional) True to keep existing selections
61870      */
61871     selectNext : function(keepExisting){
61872         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61873             this.selectRow(this.last+1, keepExisting);
61874             var view = this.grid.view ? this.grid.view : this.grid;
61875             view.focusRow(this.last);
61876         }
61877     },
61878
61879     /**
61880      * Selects the row that precedes the last selected row.
61881      * @param {Boolean} keepExisting (optional) True to keep existing selections
61882      */
61883     selectPrevious : function(keepExisting){
61884         if(this.last){
61885             this.selectRow(this.last-1, keepExisting);
61886             var view = this.grid.view ? this.grid.view : this.grid;
61887             view.focusRow(this.last);
61888         }
61889     },
61890
61891     /**
61892      * Returns the selected records
61893      * @return {Array} Array of selected records
61894      */
61895     getSelections : function(){
61896         return [].concat(this.selections.items);
61897     },
61898
61899     /**
61900      * Returns the first selected record.
61901      * @return {Record}
61902      */
61903     getSelected : function(){
61904         return this.selections.itemAt(0);
61905     },
61906
61907
61908     /**
61909      * Clears all selections.
61910      */
61911     clearSelections : function(fast){
61912         if(this.locked) {
61913             return;
61914         }
61915         if(fast !== true){
61916             var ds = this.grid.ds;
61917             var s = this.selections;
61918             s.each(function(r){
61919                 this.deselectRow(ds.indexOfId(r.id));
61920             }, this);
61921             s.clear();
61922         }else{
61923             this.selections.clear();
61924         }
61925         this.last = false;
61926     },
61927
61928
61929     /**
61930      * Selects all rows.
61931      */
61932     selectAll : function(){
61933         if(this.locked) {
61934             return;
61935         }
61936         this.selections.clear();
61937         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61938             this.selectRow(i, true);
61939         }
61940     },
61941
61942     /**
61943      * Returns True if there is a selection.
61944      * @return {Boolean}
61945      */
61946     hasSelection : function(){
61947         return this.selections.length > 0;
61948     },
61949
61950     /**
61951      * Returns True if the specified row is selected.
61952      * @param {Number/Record} record The record or index of the record to check
61953      * @return {Boolean}
61954      */
61955     isSelected : function(index){
61956         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61957         return (r && this.selections.key(r.id) ? true : false);
61958     },
61959
61960     /**
61961      * Returns True if the specified record id is selected.
61962      * @param {String} id The id of record to check
61963      * @return {Boolean}
61964      */
61965     isIdSelected : function(id){
61966         return (this.selections.key(id) ? true : false);
61967     },
61968
61969     // private
61970     handleMouseDown : function(e, t)
61971     {
61972         var view = this.grid.view ? this.grid.view : this.grid;
61973         var rowIndex;
61974         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61975             return;
61976         };
61977         if(e.shiftKey && this.last !== false){
61978             var last = this.last;
61979             this.selectRange(last, rowIndex, e.ctrlKey);
61980             this.last = last; // reset the last
61981             view.focusRow(rowIndex);
61982         }else{
61983             var isSelected = this.isSelected(rowIndex);
61984             if(e.button !== 0 && isSelected){
61985                 view.focusRow(rowIndex);
61986             }else if(e.ctrlKey && isSelected){
61987                 this.deselectRow(rowIndex);
61988             }else if(!isSelected){
61989                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61990                 view.focusRow(rowIndex);
61991             }
61992         }
61993         this.fireEvent("afterselectionchange", this);
61994     },
61995     // private
61996     handleDragableRowClick :  function(grid, rowIndex, e) 
61997     {
61998         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61999             this.selectRow(rowIndex, false);
62000             var view = this.grid.view ? this.grid.view : this.grid;
62001             view.focusRow(rowIndex);
62002              this.fireEvent("afterselectionchange", this);
62003         }
62004     },
62005     
62006     /**
62007      * Selects multiple rows.
62008      * @param {Array} rows Array of the indexes of the row to select
62009      * @param {Boolean} keepExisting (optional) True to keep existing selections
62010      */
62011     selectRows : function(rows, keepExisting){
62012         if(!keepExisting){
62013             this.clearSelections();
62014         }
62015         for(var i = 0, len = rows.length; i < len; i++){
62016             this.selectRow(rows[i], true);
62017         }
62018     },
62019
62020     /**
62021      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62022      * @param {Number} startRow The index of the first row in the range
62023      * @param {Number} endRow The index of the last row in the range
62024      * @param {Boolean} keepExisting (optional) True to retain existing selections
62025      */
62026     selectRange : function(startRow, endRow, keepExisting){
62027         if(this.locked) {
62028             return;
62029         }
62030         if(!keepExisting){
62031             this.clearSelections();
62032         }
62033         if(startRow <= endRow){
62034             for(var i = startRow; i <= endRow; i++){
62035                 this.selectRow(i, true);
62036             }
62037         }else{
62038             for(var i = startRow; i >= endRow; i--){
62039                 this.selectRow(i, true);
62040             }
62041         }
62042     },
62043
62044     /**
62045      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62046      * @param {Number} startRow The index of the first row in the range
62047      * @param {Number} endRow The index of the last row in the range
62048      */
62049     deselectRange : function(startRow, endRow, preventViewNotify){
62050         if(this.locked) {
62051             return;
62052         }
62053         for(var i = startRow; i <= endRow; i++){
62054             this.deselectRow(i, preventViewNotify);
62055         }
62056     },
62057
62058     /**
62059      * Selects a row.
62060      * @param {Number} row The index of the row to select
62061      * @param {Boolean} keepExisting (optional) True to keep existing selections
62062      */
62063     selectRow : function(index, keepExisting, preventViewNotify){
62064         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62065             return;
62066         }
62067         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62068             if(!keepExisting || this.singleSelect){
62069                 this.clearSelections();
62070             }
62071             var r = this.grid.ds.getAt(index);
62072             this.selections.add(r);
62073             this.last = this.lastActive = index;
62074             if(!preventViewNotify){
62075                 var view = this.grid.view ? this.grid.view : this.grid;
62076                 view.onRowSelect(index);
62077             }
62078             this.fireEvent("rowselect", this, index, r);
62079             this.fireEvent("selectionchange", this);
62080         }
62081     },
62082
62083     /**
62084      * Deselects a row.
62085      * @param {Number} row The index of the row to deselect
62086      */
62087     deselectRow : function(index, preventViewNotify){
62088         if(this.locked) {
62089             return;
62090         }
62091         if(this.last == index){
62092             this.last = false;
62093         }
62094         if(this.lastActive == index){
62095             this.lastActive = false;
62096         }
62097         var r = this.grid.ds.getAt(index);
62098         this.selections.remove(r);
62099         if(!preventViewNotify){
62100             var view = this.grid.view ? this.grid.view : this.grid;
62101             view.onRowDeselect(index);
62102         }
62103         this.fireEvent("rowdeselect", this, index);
62104         this.fireEvent("selectionchange", this);
62105     },
62106
62107     // private
62108     restoreLast : function(){
62109         if(this._last){
62110             this.last = this._last;
62111         }
62112     },
62113
62114     // private
62115     acceptsNav : function(row, col, cm){
62116         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62117     },
62118
62119     // private
62120     onEditorKey : function(field, e){
62121         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62122         if(k == e.TAB){
62123             e.stopEvent();
62124             ed.completeEdit();
62125             if(e.shiftKey){
62126                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62127             }else{
62128                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62129             }
62130         }else if(k == e.ENTER && !e.ctrlKey){
62131             e.stopEvent();
62132             ed.completeEdit();
62133             if(e.shiftKey){
62134                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62135             }else{
62136                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62137             }
62138         }else if(k == e.ESC){
62139             ed.cancelEdit();
62140         }
62141         if(newCell){
62142             g.startEditing(newCell[0], newCell[1]);
62143         }
62144     }
62145 });/*
62146  * Based on:
62147  * Ext JS Library 1.1.1
62148  * Copyright(c) 2006-2007, Ext JS, LLC.
62149  *
62150  * Originally Released Under LGPL - original licence link has changed is not relivant.
62151  *
62152  * Fork - LGPL
62153  * <script type="text/javascript">
62154  */
62155 /**
62156  * @class Roo.grid.CellSelectionModel
62157  * @extends Roo.grid.AbstractSelectionModel
62158  * This class provides the basic implementation for cell selection in a grid.
62159  * @constructor
62160  * @param {Object} config The object containing the configuration of this model.
62161  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62162  */
62163 Roo.grid.CellSelectionModel = function(config){
62164     Roo.apply(this, config);
62165
62166     this.selection = null;
62167
62168     this.addEvents({
62169         /**
62170              * @event beforerowselect
62171              * Fires before a cell is selected.
62172              * @param {SelectionModel} this
62173              * @param {Number} rowIndex The selected row index
62174              * @param {Number} colIndex The selected cell index
62175              */
62176             "beforecellselect" : true,
62177         /**
62178              * @event cellselect
62179              * Fires when a cell is selected.
62180              * @param {SelectionModel} this
62181              * @param {Number} rowIndex The selected row index
62182              * @param {Number} colIndex The selected cell index
62183              */
62184             "cellselect" : true,
62185         /**
62186              * @event selectionchange
62187              * Fires when the active selection changes.
62188              * @param {SelectionModel} this
62189              * @param {Object} selection null for no selection or an object (o) with two properties
62190                 <ul>
62191                 <li>o.record: the record object for the row the selection is in</li>
62192                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62193                 </ul>
62194              */
62195             "selectionchange" : true,
62196         /**
62197              * @event tabend
62198              * Fires when the tab (or enter) was pressed on the last editable cell
62199              * You can use this to trigger add new row.
62200              * @param {SelectionModel} this
62201              */
62202             "tabend" : true,
62203          /**
62204              * @event beforeeditnext
62205              * Fires before the next editable sell is made active
62206              * You can use this to skip to another cell or fire the tabend
62207              *    if you set cell to false
62208              * @param {Object} eventdata object : { cell : [ row, col ] } 
62209              */
62210             "beforeeditnext" : true
62211     });
62212     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62213 };
62214
62215 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62216     
62217     enter_is_tab: false,
62218
62219     /** @ignore */
62220     initEvents : function(){
62221         this.grid.on("mousedown", this.handleMouseDown, this);
62222         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62223         var view = this.grid.view;
62224         view.on("refresh", this.onViewChange, this);
62225         view.on("rowupdated", this.onRowUpdated, this);
62226         view.on("beforerowremoved", this.clearSelections, this);
62227         view.on("beforerowsinserted", this.clearSelections, this);
62228         if(this.grid.isEditor){
62229             this.grid.on("beforeedit", this.beforeEdit,  this);
62230         }
62231     },
62232
62233         //private
62234     beforeEdit : function(e){
62235         this.select(e.row, e.column, false, true, e.record);
62236     },
62237
62238         //private
62239     onRowUpdated : function(v, index, r){
62240         if(this.selection && this.selection.record == r){
62241             v.onCellSelect(index, this.selection.cell[1]);
62242         }
62243     },
62244
62245         //private
62246     onViewChange : function(){
62247         this.clearSelections(true);
62248     },
62249
62250         /**
62251          * Returns the currently selected cell,.
62252          * @return {Array} The selected cell (row, column) or null if none selected.
62253          */
62254     getSelectedCell : function(){
62255         return this.selection ? this.selection.cell : null;
62256     },
62257
62258     /**
62259      * Clears all selections.
62260      * @param {Boolean} true to prevent the gridview from being notified about the change.
62261      */
62262     clearSelections : function(preventNotify){
62263         var s = this.selection;
62264         if(s){
62265             if(preventNotify !== true){
62266                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62267             }
62268             this.selection = null;
62269             this.fireEvent("selectionchange", this, null);
62270         }
62271     },
62272
62273     /**
62274      * Returns true if there is a selection.
62275      * @return {Boolean}
62276      */
62277     hasSelection : function(){
62278         return this.selection ? true : false;
62279     },
62280
62281     /** @ignore */
62282     handleMouseDown : function(e, t){
62283         var v = this.grid.getView();
62284         if(this.isLocked()){
62285             return;
62286         };
62287         var row = v.findRowIndex(t);
62288         var cell = v.findCellIndex(t);
62289         if(row !== false && cell !== false){
62290             this.select(row, cell);
62291         }
62292     },
62293
62294     /**
62295      * Selects a cell.
62296      * @param {Number} rowIndex
62297      * @param {Number} collIndex
62298      */
62299     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62300         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62301             this.clearSelections();
62302             r = r || this.grid.dataSource.getAt(rowIndex);
62303             this.selection = {
62304                 record : r,
62305                 cell : [rowIndex, colIndex]
62306             };
62307             if(!preventViewNotify){
62308                 var v = this.grid.getView();
62309                 v.onCellSelect(rowIndex, colIndex);
62310                 if(preventFocus !== true){
62311                     v.focusCell(rowIndex, colIndex);
62312                 }
62313             }
62314             this.fireEvent("cellselect", this, rowIndex, colIndex);
62315             this.fireEvent("selectionchange", this, this.selection);
62316         }
62317     },
62318
62319         //private
62320     isSelectable : function(rowIndex, colIndex, cm){
62321         return !cm.isHidden(colIndex);
62322     },
62323
62324     /** @ignore */
62325     handleKeyDown : function(e){
62326         //Roo.log('Cell Sel Model handleKeyDown');
62327         if(!e.isNavKeyPress()){
62328             return;
62329         }
62330         var g = this.grid, s = this.selection;
62331         if(!s){
62332             e.stopEvent();
62333             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62334             if(cell){
62335                 this.select(cell[0], cell[1]);
62336             }
62337             return;
62338         }
62339         var sm = this;
62340         var walk = function(row, col, step){
62341             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62342         };
62343         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62344         var newCell;
62345
62346       
62347
62348         switch(k){
62349             case e.TAB:
62350                 // handled by onEditorKey
62351                 if (g.isEditor && g.editing) {
62352                     return;
62353                 }
62354                 if(e.shiftKey) {
62355                     newCell = walk(r, c-1, -1);
62356                 } else {
62357                     newCell = walk(r, c+1, 1);
62358                 }
62359                 break;
62360             
62361             case e.DOWN:
62362                newCell = walk(r+1, c, 1);
62363                 break;
62364             
62365             case e.UP:
62366                 newCell = walk(r-1, c, -1);
62367                 break;
62368             
62369             case e.RIGHT:
62370                 newCell = walk(r, c+1, 1);
62371                 break;
62372             
62373             case e.LEFT:
62374                 newCell = walk(r, c-1, -1);
62375                 break;
62376             
62377             case e.ENTER:
62378                 
62379                 if(g.isEditor && !g.editing){
62380                    g.startEditing(r, c);
62381                    e.stopEvent();
62382                    return;
62383                 }
62384                 
62385                 
62386              break;
62387         };
62388         if(newCell){
62389             this.select(newCell[0], newCell[1]);
62390             e.stopEvent();
62391             
62392         }
62393     },
62394
62395     acceptsNav : function(row, col, cm){
62396         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62397     },
62398     /**
62399      * Selects a cell.
62400      * @param {Number} field (not used) - as it's normally used as a listener
62401      * @param {Number} e - event - fake it by using
62402      *
62403      * var e = Roo.EventObjectImpl.prototype;
62404      * e.keyCode = e.TAB
62405      *
62406      * 
62407      */
62408     onEditorKey : function(field, e){
62409         
62410         var k = e.getKey(),
62411             newCell,
62412             g = this.grid,
62413             ed = g.activeEditor,
62414             forward = false;
62415         ///Roo.log('onEditorKey' + k);
62416         
62417         
62418         if (this.enter_is_tab && k == e.ENTER) {
62419             k = e.TAB;
62420         }
62421         
62422         if(k == e.TAB){
62423             if(e.shiftKey){
62424                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62425             }else{
62426                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62427                 forward = true;
62428             }
62429             
62430             e.stopEvent();
62431             
62432         } else if(k == e.ENTER &&  !e.ctrlKey){
62433             ed.completeEdit();
62434             e.stopEvent();
62435             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62436         
62437                 } else if(k == e.ESC){
62438             ed.cancelEdit();
62439         }
62440                 
62441         if (newCell) {
62442             var ecall = { cell : newCell, forward : forward };
62443             this.fireEvent('beforeeditnext', ecall );
62444             newCell = ecall.cell;
62445                         forward = ecall.forward;
62446         }
62447                 
62448         if(newCell){
62449             //Roo.log('next cell after edit');
62450             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62451         } else if (forward) {
62452             // tabbed past last
62453             this.fireEvent.defer(100, this, ['tabend',this]);
62454         }
62455     }
62456 });/*
62457  * Based on:
62458  * Ext JS Library 1.1.1
62459  * Copyright(c) 2006-2007, Ext JS, LLC.
62460  *
62461  * Originally Released Under LGPL - original licence link has changed is not relivant.
62462  *
62463  * Fork - LGPL
62464  * <script type="text/javascript">
62465  */
62466  
62467 /**
62468  * @class Roo.grid.EditorGrid
62469  * @extends Roo.grid.Grid
62470  * Class for creating and editable grid.
62471  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62472  * The container MUST have some type of size defined for the grid to fill. The container will be 
62473  * automatically set to position relative if it isn't already.
62474  * @param {Object} dataSource The data model to bind to
62475  * @param {Object} colModel The column model with info about this grid's columns
62476  */
62477 Roo.grid.EditorGrid = function(container, config){
62478     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62479     this.getGridEl().addClass("xedit-grid");
62480
62481     if(!this.selModel){
62482         this.selModel = new Roo.grid.CellSelectionModel();
62483     }
62484
62485     this.activeEditor = null;
62486
62487         this.addEvents({
62488             /**
62489              * @event beforeedit
62490              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62491              * <ul style="padding:5px;padding-left:16px;">
62492              * <li>grid - This grid</li>
62493              * <li>record - The record being edited</li>
62494              * <li>field - The field name being edited</li>
62495              * <li>value - The value for the field being edited.</li>
62496              * <li>row - The grid row index</li>
62497              * <li>column - The grid column index</li>
62498              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62499              * </ul>
62500              * @param {Object} e An edit event (see above for description)
62501              */
62502             "beforeedit" : true,
62503             /**
62504              * @event afteredit
62505              * Fires after a cell is edited. <br />
62506              * <ul style="padding:5px;padding-left:16px;">
62507              * <li>grid - This grid</li>
62508              * <li>record - The record being edited</li>
62509              * <li>field - The field name being edited</li>
62510              * <li>value - The value being set</li>
62511              * <li>originalValue - The original value for the field, before the edit.</li>
62512              * <li>row - The grid row index</li>
62513              * <li>column - The grid column index</li>
62514              * </ul>
62515              * @param {Object} e An edit event (see above for description)
62516              */
62517             "afteredit" : true,
62518             /**
62519              * @event validateedit
62520              * Fires after a cell is edited, but before the value is set in the record. 
62521          * You can use this to modify the value being set in the field, Return false
62522              * to cancel the change. The edit event object has the following properties <br />
62523              * <ul style="padding:5px;padding-left:16px;">
62524          * <li>editor - This editor</li>
62525              * <li>grid - This grid</li>
62526              * <li>record - The record being edited</li>
62527              * <li>field - The field name being edited</li>
62528              * <li>value - The value being set</li>
62529              * <li>originalValue - The original value for the field, before the edit.</li>
62530              * <li>row - The grid row index</li>
62531              * <li>column - The grid column index</li>
62532              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62533              * </ul>
62534              * @param {Object} e An edit event (see above for description)
62535              */
62536             "validateedit" : true
62537         });
62538     this.on("bodyscroll", this.stopEditing,  this);
62539     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62540 };
62541
62542 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62543     /**
62544      * @cfg {Number} clicksToEdit
62545      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62546      */
62547     clicksToEdit: 2,
62548
62549     // private
62550     isEditor : true,
62551     // private
62552     trackMouseOver: false, // causes very odd FF errors
62553
62554     onCellDblClick : function(g, row, col){
62555         this.startEditing(row, col);
62556     },
62557
62558     onEditComplete : function(ed, value, startValue){
62559         this.editing = false;
62560         this.activeEditor = null;
62561         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62562         var r = ed.record;
62563         var field = this.colModel.getDataIndex(ed.col);
62564         var e = {
62565             grid: this,
62566             record: r,
62567             field: field,
62568             originalValue: startValue,
62569             value: value,
62570             row: ed.row,
62571             column: ed.col,
62572             cancel:false,
62573             editor: ed
62574         };
62575         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62576         cell.show();
62577           
62578         if(String(value) !== String(startValue)){
62579             
62580             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62581                 r.set(field, e.value);
62582                 // if we are dealing with a combo box..
62583                 // then we also set the 'name' colum to be the displayField
62584                 if (ed.field.displayField && ed.field.name) {
62585                     r.set(ed.field.name, ed.field.el.dom.value);
62586                 }
62587                 
62588                 delete e.cancel; //?? why!!!
62589                 this.fireEvent("afteredit", e);
62590             }
62591         } else {
62592             this.fireEvent("afteredit", e); // always fire it!
62593         }
62594         this.view.focusCell(ed.row, ed.col);
62595     },
62596
62597     /**
62598      * Starts editing the specified for the specified row/column
62599      * @param {Number} rowIndex
62600      * @param {Number} colIndex
62601      */
62602     startEditing : function(row, col){
62603         this.stopEditing();
62604         if(this.colModel.isCellEditable(col, row)){
62605             this.view.ensureVisible(row, col, true);
62606           
62607             var r = this.dataSource.getAt(row);
62608             var field = this.colModel.getDataIndex(col);
62609             var cell = Roo.get(this.view.getCell(row,col));
62610             var e = {
62611                 grid: this,
62612                 record: r,
62613                 field: field,
62614                 value: r.data[field],
62615                 row: row,
62616                 column: col,
62617                 cancel:false 
62618             };
62619             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62620                 this.editing = true;
62621                 var ed = this.colModel.getCellEditor(col, row);
62622                 
62623                 if (!ed) {
62624                     return;
62625                 }
62626                 if(!ed.rendered){
62627                     ed.render(ed.parentEl || document.body);
62628                 }
62629                 ed.field.reset();
62630                
62631                 cell.hide();
62632                 
62633                 (function(){ // complex but required for focus issues in safari, ie and opera
62634                     ed.row = row;
62635                     ed.col = col;
62636                     ed.record = r;
62637                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62638                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62639                     this.activeEditor = ed;
62640                     var v = r.data[field];
62641                     ed.startEdit(this.view.getCell(row, col), v);
62642                     // combo's with 'displayField and name set
62643                     if (ed.field.displayField && ed.field.name) {
62644                         ed.field.el.dom.value = r.data[ed.field.name];
62645                     }
62646                     
62647                     
62648                 }).defer(50, this);
62649             }
62650         }
62651     },
62652         
62653     /**
62654      * Stops any active editing
62655      */
62656     stopEditing : function(){
62657         if(this.activeEditor){
62658             this.activeEditor.completeEdit();
62659         }
62660         this.activeEditor = null;
62661     },
62662         
62663          /**
62664      * Called to get grid's drag proxy text, by default returns this.ddText.
62665      * @return {String}
62666      */
62667     getDragDropText : function(){
62668         var count = this.selModel.getSelectedCell() ? 1 : 0;
62669         return String.format(this.ddText, count, count == 1 ? '' : 's');
62670     }
62671         
62672 });/*
62673  * Based on:
62674  * Ext JS Library 1.1.1
62675  * Copyright(c) 2006-2007, Ext JS, LLC.
62676  *
62677  * Originally Released Under LGPL - original licence link has changed is not relivant.
62678  *
62679  * Fork - LGPL
62680  * <script type="text/javascript">
62681  */
62682
62683 // private - not really -- you end up using it !
62684 // This is a support class used internally by the Grid components
62685
62686 /**
62687  * @class Roo.grid.GridEditor
62688  * @extends Roo.Editor
62689  * Class for creating and editable grid elements.
62690  * @param {Object} config any settings (must include field)
62691  */
62692 Roo.grid.GridEditor = function(field, config){
62693     if (!config && field.field) {
62694         config = field;
62695         field = Roo.factory(config.field, Roo.form);
62696     }
62697     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62698     field.monitorTab = false;
62699 };
62700
62701 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62702     
62703     /**
62704      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62705      */
62706     
62707     alignment: "tl-tl",
62708     autoSize: "width",
62709     hideEl : false,
62710     cls: "x-small-editor x-grid-editor",
62711     shim:false,
62712     shadow:"frame"
62713 });/*
62714  * Based on:
62715  * Ext JS Library 1.1.1
62716  * Copyright(c) 2006-2007, Ext JS, LLC.
62717  *
62718  * Originally Released Under LGPL - original licence link has changed is not relivant.
62719  *
62720  * Fork - LGPL
62721  * <script type="text/javascript">
62722  */
62723   
62724
62725   
62726 Roo.grid.PropertyRecord = Roo.data.Record.create([
62727     {name:'name',type:'string'},  'value'
62728 ]);
62729
62730
62731 Roo.grid.PropertyStore = function(grid, source){
62732     this.grid = grid;
62733     this.store = new Roo.data.Store({
62734         recordType : Roo.grid.PropertyRecord
62735     });
62736     this.store.on('update', this.onUpdate,  this);
62737     if(source){
62738         this.setSource(source);
62739     }
62740     Roo.grid.PropertyStore.superclass.constructor.call(this);
62741 };
62742
62743
62744
62745 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62746     setSource : function(o){
62747         this.source = o;
62748         this.store.removeAll();
62749         var data = [];
62750         for(var k in o){
62751             if(this.isEditableValue(o[k])){
62752                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62753             }
62754         }
62755         this.store.loadRecords({records: data}, {}, true);
62756     },
62757
62758     onUpdate : function(ds, record, type){
62759         if(type == Roo.data.Record.EDIT){
62760             var v = record.data['value'];
62761             var oldValue = record.modified['value'];
62762             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62763                 this.source[record.id] = v;
62764                 record.commit();
62765                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62766             }else{
62767                 record.reject();
62768             }
62769         }
62770     },
62771
62772     getProperty : function(row){
62773        return this.store.getAt(row);
62774     },
62775
62776     isEditableValue: function(val){
62777         if(val && val instanceof Date){
62778             return true;
62779         }else if(typeof val == 'object' || typeof val == 'function'){
62780             return false;
62781         }
62782         return true;
62783     },
62784
62785     setValue : function(prop, value){
62786         this.source[prop] = value;
62787         this.store.getById(prop).set('value', value);
62788     },
62789
62790     getSource : function(){
62791         return this.source;
62792     }
62793 });
62794
62795 Roo.grid.PropertyColumnModel = function(grid, store){
62796     this.grid = grid;
62797     var g = Roo.grid;
62798     g.PropertyColumnModel.superclass.constructor.call(this, [
62799         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62800         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62801     ]);
62802     this.store = store;
62803     this.bselect = Roo.DomHelper.append(document.body, {
62804         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62805             {tag: 'option', value: 'true', html: 'true'},
62806             {tag: 'option', value: 'false', html: 'false'}
62807         ]
62808     });
62809     Roo.id(this.bselect);
62810     var f = Roo.form;
62811     this.editors = {
62812         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62813         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62814         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62815         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62816         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62817     };
62818     this.renderCellDelegate = this.renderCell.createDelegate(this);
62819     this.renderPropDelegate = this.renderProp.createDelegate(this);
62820 };
62821
62822 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62823     
62824     
62825     nameText : 'Name',
62826     valueText : 'Value',
62827     
62828     dateFormat : 'm/j/Y',
62829     
62830     
62831     renderDate : function(dateVal){
62832         return dateVal.dateFormat(this.dateFormat);
62833     },
62834
62835     renderBool : function(bVal){
62836         return bVal ? 'true' : 'false';
62837     },
62838
62839     isCellEditable : function(colIndex, rowIndex){
62840         return colIndex == 1;
62841     },
62842
62843     getRenderer : function(col){
62844         return col == 1 ?
62845             this.renderCellDelegate : this.renderPropDelegate;
62846     },
62847
62848     renderProp : function(v){
62849         return this.getPropertyName(v);
62850     },
62851
62852     renderCell : function(val){
62853         var rv = val;
62854         if(val instanceof Date){
62855             rv = this.renderDate(val);
62856         }else if(typeof val == 'boolean'){
62857             rv = this.renderBool(val);
62858         }
62859         return Roo.util.Format.htmlEncode(rv);
62860     },
62861
62862     getPropertyName : function(name){
62863         var pn = this.grid.propertyNames;
62864         return pn && pn[name] ? pn[name] : name;
62865     },
62866
62867     getCellEditor : function(colIndex, rowIndex){
62868         var p = this.store.getProperty(rowIndex);
62869         var n = p.data['name'], val = p.data['value'];
62870         
62871         if(typeof(this.grid.customEditors[n]) == 'string'){
62872             return this.editors[this.grid.customEditors[n]];
62873         }
62874         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62875             return this.grid.customEditors[n];
62876         }
62877         if(val instanceof Date){
62878             return this.editors['date'];
62879         }else if(typeof val == 'number'){
62880             return this.editors['number'];
62881         }else if(typeof val == 'boolean'){
62882             return this.editors['boolean'];
62883         }else{
62884             return this.editors['string'];
62885         }
62886     }
62887 });
62888
62889 /**
62890  * @class Roo.grid.PropertyGrid
62891  * @extends Roo.grid.EditorGrid
62892  * This class represents the  interface of a component based property grid control.
62893  * <br><br>Usage:<pre><code>
62894  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62895       
62896  });
62897  // set any options
62898  grid.render();
62899  * </code></pre>
62900   
62901  * @constructor
62902  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62903  * The container MUST have some type of size defined for the grid to fill. The container will be
62904  * automatically set to position relative if it isn't already.
62905  * @param {Object} config A config object that sets properties on this grid.
62906  */
62907 Roo.grid.PropertyGrid = function(container, config){
62908     config = config || {};
62909     var store = new Roo.grid.PropertyStore(this);
62910     this.store = store;
62911     var cm = new Roo.grid.PropertyColumnModel(this, store);
62912     store.store.sort('name', 'ASC');
62913     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62914         ds: store.store,
62915         cm: cm,
62916         enableColLock:false,
62917         enableColumnMove:false,
62918         stripeRows:false,
62919         trackMouseOver: false,
62920         clicksToEdit:1
62921     }, config));
62922     this.getGridEl().addClass('x-props-grid');
62923     this.lastEditRow = null;
62924     this.on('columnresize', this.onColumnResize, this);
62925     this.addEvents({
62926          /**
62927              * @event beforepropertychange
62928              * Fires before a property changes (return false to stop?)
62929              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62930              * @param {String} id Record Id
62931              * @param {String} newval New Value
62932          * @param {String} oldval Old Value
62933              */
62934         "beforepropertychange": true,
62935         /**
62936              * @event propertychange
62937              * Fires after a property changes
62938              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62939              * @param {String} id Record Id
62940              * @param {String} newval New Value
62941          * @param {String} oldval Old Value
62942              */
62943         "propertychange": true
62944     });
62945     this.customEditors = this.customEditors || {};
62946 };
62947 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62948     
62949      /**
62950      * @cfg {Object} customEditors map of colnames=> custom editors.
62951      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62952      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62953      * false disables editing of the field.
62954          */
62955     
62956       /**
62957      * @cfg {Object} propertyNames map of property Names to their displayed value
62958          */
62959     
62960     render : function(){
62961         Roo.grid.PropertyGrid.superclass.render.call(this);
62962         this.autoSize.defer(100, this);
62963     },
62964
62965     autoSize : function(){
62966         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62967         if(this.view){
62968             this.view.fitColumns();
62969         }
62970     },
62971
62972     onColumnResize : function(){
62973         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62974         this.autoSize();
62975     },
62976     /**
62977      * Sets the data for the Grid
62978      * accepts a Key => Value object of all the elements avaiable.
62979      * @param {Object} data  to appear in grid.
62980      */
62981     setSource : function(source){
62982         this.store.setSource(source);
62983         //this.autoSize();
62984     },
62985     /**
62986      * Gets all the data from the grid.
62987      * @return {Object} data  data stored in grid
62988      */
62989     getSource : function(){
62990         return this.store.getSource();
62991     }
62992 });/*
62993   
62994  * Licence LGPL
62995  
62996  */
62997  
62998 /**
62999  * @class Roo.grid.Calendar
63000  * @extends Roo.grid.Grid
63001  * This class extends the Grid to provide a calendar widget
63002  * <br><br>Usage:<pre><code>
63003  var grid = new Roo.grid.Calendar("my-container-id", {
63004      ds: myDataStore,
63005      cm: myColModel,
63006      selModel: mySelectionModel,
63007      autoSizeColumns: true,
63008      monitorWindowResize: false,
63009      trackMouseOver: true
63010      eventstore : real data store..
63011  });
63012  // set any options
63013  grid.render();
63014   
63015   * @constructor
63016  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63017  * The container MUST have some type of size defined for the grid to fill. The container will be
63018  * automatically set to position relative if it isn't already.
63019  * @param {Object} config A config object that sets properties on this grid.
63020  */
63021 Roo.grid.Calendar = function(container, config){
63022         // initialize the container
63023         this.container = Roo.get(container);
63024         this.container.update("");
63025         this.container.setStyle("overflow", "hidden");
63026     this.container.addClass('x-grid-container');
63027
63028     this.id = this.container.id;
63029
63030     Roo.apply(this, config);
63031     // check and correct shorthanded configs
63032     
63033     var rows = [];
63034     var d =1;
63035     for (var r = 0;r < 6;r++) {
63036         
63037         rows[r]=[];
63038         for (var c =0;c < 7;c++) {
63039             rows[r][c]= '';
63040         }
63041     }
63042     if (this.eventStore) {
63043         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63044         this.eventStore.on('load',this.onLoad, this);
63045         this.eventStore.on('beforeload',this.clearEvents, this);
63046          
63047     }
63048     
63049     this.dataSource = new Roo.data.Store({
63050             proxy: new Roo.data.MemoryProxy(rows),
63051             reader: new Roo.data.ArrayReader({}, [
63052                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63053     });
63054
63055     this.dataSource.load();
63056     this.ds = this.dataSource;
63057     this.ds.xmodule = this.xmodule || false;
63058     
63059     
63060     var cellRender = function(v,x,r)
63061     {
63062         return String.format(
63063             '<div class="fc-day  fc-widget-content"><div>' +
63064                 '<div class="fc-event-container"></div>' +
63065                 '<div class="fc-day-number">{0}</div>'+
63066                 
63067                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63068             '</div></div>', v);
63069     
63070     }
63071     
63072     
63073     this.colModel = new Roo.grid.ColumnModel( [
63074         {
63075             xtype: 'ColumnModel',
63076             xns: Roo.grid,
63077             dataIndex : 'weekday0',
63078             header : 'Sunday',
63079             renderer : cellRender
63080         },
63081         {
63082             xtype: 'ColumnModel',
63083             xns: Roo.grid,
63084             dataIndex : 'weekday1',
63085             header : 'Monday',
63086             renderer : cellRender
63087         },
63088         {
63089             xtype: 'ColumnModel',
63090             xns: Roo.grid,
63091             dataIndex : 'weekday2',
63092             header : 'Tuesday',
63093             renderer : cellRender
63094         },
63095         {
63096             xtype: 'ColumnModel',
63097             xns: Roo.grid,
63098             dataIndex : 'weekday3',
63099             header : 'Wednesday',
63100             renderer : cellRender
63101         },
63102         {
63103             xtype: 'ColumnModel',
63104             xns: Roo.grid,
63105             dataIndex : 'weekday4',
63106             header : 'Thursday',
63107             renderer : cellRender
63108         },
63109         {
63110             xtype: 'ColumnModel',
63111             xns: Roo.grid,
63112             dataIndex : 'weekday5',
63113             header : 'Friday',
63114             renderer : cellRender
63115         },
63116         {
63117             xtype: 'ColumnModel',
63118             xns: Roo.grid,
63119             dataIndex : 'weekday6',
63120             header : 'Saturday',
63121             renderer : cellRender
63122         }
63123     ]);
63124     this.cm = this.colModel;
63125     this.cm.xmodule = this.xmodule || false;
63126  
63127         
63128           
63129     //this.selModel = new Roo.grid.CellSelectionModel();
63130     //this.sm = this.selModel;
63131     //this.selModel.init(this);
63132     
63133     
63134     if(this.width){
63135         this.container.setWidth(this.width);
63136     }
63137
63138     if(this.height){
63139         this.container.setHeight(this.height);
63140     }
63141     /** @private */
63142         this.addEvents({
63143         // raw events
63144         /**
63145          * @event click
63146          * The raw click event for the entire grid.
63147          * @param {Roo.EventObject} e
63148          */
63149         "click" : true,
63150         /**
63151          * @event dblclick
63152          * The raw dblclick event for the entire grid.
63153          * @param {Roo.EventObject} e
63154          */
63155         "dblclick" : true,
63156         /**
63157          * @event contextmenu
63158          * The raw contextmenu event for the entire grid.
63159          * @param {Roo.EventObject} e
63160          */
63161         "contextmenu" : true,
63162         /**
63163          * @event mousedown
63164          * The raw mousedown event for the entire grid.
63165          * @param {Roo.EventObject} e
63166          */
63167         "mousedown" : true,
63168         /**
63169          * @event mouseup
63170          * The raw mouseup event for the entire grid.
63171          * @param {Roo.EventObject} e
63172          */
63173         "mouseup" : true,
63174         /**
63175          * @event mouseover
63176          * The raw mouseover event for the entire grid.
63177          * @param {Roo.EventObject} e
63178          */
63179         "mouseover" : true,
63180         /**
63181          * @event mouseout
63182          * The raw mouseout event for the entire grid.
63183          * @param {Roo.EventObject} e
63184          */
63185         "mouseout" : true,
63186         /**
63187          * @event keypress
63188          * The raw keypress event for the entire grid.
63189          * @param {Roo.EventObject} e
63190          */
63191         "keypress" : true,
63192         /**
63193          * @event keydown
63194          * The raw keydown event for the entire grid.
63195          * @param {Roo.EventObject} e
63196          */
63197         "keydown" : true,
63198
63199         // custom events
63200
63201         /**
63202          * @event cellclick
63203          * Fires when a cell is clicked
63204          * @param {Grid} this
63205          * @param {Number} rowIndex
63206          * @param {Number} columnIndex
63207          * @param {Roo.EventObject} e
63208          */
63209         "cellclick" : true,
63210         /**
63211          * @event celldblclick
63212          * Fires when a cell is double clicked
63213          * @param {Grid} this
63214          * @param {Number} rowIndex
63215          * @param {Number} columnIndex
63216          * @param {Roo.EventObject} e
63217          */
63218         "celldblclick" : true,
63219         /**
63220          * @event rowclick
63221          * Fires when a row is clicked
63222          * @param {Grid} this
63223          * @param {Number} rowIndex
63224          * @param {Roo.EventObject} e
63225          */
63226         "rowclick" : true,
63227         /**
63228          * @event rowdblclick
63229          * Fires when a row is double clicked
63230          * @param {Grid} this
63231          * @param {Number} rowIndex
63232          * @param {Roo.EventObject} e
63233          */
63234         "rowdblclick" : true,
63235         /**
63236          * @event headerclick
63237          * Fires when a header is clicked
63238          * @param {Grid} this
63239          * @param {Number} columnIndex
63240          * @param {Roo.EventObject} e
63241          */
63242         "headerclick" : true,
63243         /**
63244          * @event headerdblclick
63245          * Fires when a header cell is double clicked
63246          * @param {Grid} this
63247          * @param {Number} columnIndex
63248          * @param {Roo.EventObject} e
63249          */
63250         "headerdblclick" : true,
63251         /**
63252          * @event rowcontextmenu
63253          * Fires when a row is right clicked
63254          * @param {Grid} this
63255          * @param {Number} rowIndex
63256          * @param {Roo.EventObject} e
63257          */
63258         "rowcontextmenu" : true,
63259         /**
63260          * @event cellcontextmenu
63261          * Fires when a cell is right clicked
63262          * @param {Grid} this
63263          * @param {Number} rowIndex
63264          * @param {Number} cellIndex
63265          * @param {Roo.EventObject} e
63266          */
63267          "cellcontextmenu" : true,
63268         /**
63269          * @event headercontextmenu
63270          * Fires when a header is right clicked
63271          * @param {Grid} this
63272          * @param {Number} columnIndex
63273          * @param {Roo.EventObject} e
63274          */
63275         "headercontextmenu" : true,
63276         /**
63277          * @event bodyscroll
63278          * Fires when the body element is scrolled
63279          * @param {Number} scrollLeft
63280          * @param {Number} scrollTop
63281          */
63282         "bodyscroll" : true,
63283         /**
63284          * @event columnresize
63285          * Fires when the user resizes a column
63286          * @param {Number} columnIndex
63287          * @param {Number} newSize
63288          */
63289         "columnresize" : true,
63290         /**
63291          * @event columnmove
63292          * Fires when the user moves a column
63293          * @param {Number} oldIndex
63294          * @param {Number} newIndex
63295          */
63296         "columnmove" : true,
63297         /**
63298          * @event startdrag
63299          * Fires when row(s) start being dragged
63300          * @param {Grid} this
63301          * @param {Roo.GridDD} dd The drag drop object
63302          * @param {event} e The raw browser event
63303          */
63304         "startdrag" : true,
63305         /**
63306          * @event enddrag
63307          * Fires when a drag operation is complete
63308          * @param {Grid} this
63309          * @param {Roo.GridDD} dd The drag drop object
63310          * @param {event} e The raw browser event
63311          */
63312         "enddrag" : true,
63313         /**
63314          * @event dragdrop
63315          * Fires when dragged row(s) are dropped on a valid DD target
63316          * @param {Grid} this
63317          * @param {Roo.GridDD} dd The drag drop object
63318          * @param {String} targetId The target drag drop object
63319          * @param {event} e The raw browser event
63320          */
63321         "dragdrop" : true,
63322         /**
63323          * @event dragover
63324          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63325          * @param {Grid} this
63326          * @param {Roo.GridDD} dd The drag drop object
63327          * @param {String} targetId The target drag drop object
63328          * @param {event} e The raw browser event
63329          */
63330         "dragover" : true,
63331         /**
63332          * @event dragenter
63333          *  Fires when the dragged row(s) first cross another DD target while being dragged
63334          * @param {Grid} this
63335          * @param {Roo.GridDD} dd The drag drop object
63336          * @param {String} targetId The target drag drop object
63337          * @param {event} e The raw browser event
63338          */
63339         "dragenter" : true,
63340         /**
63341          * @event dragout
63342          * Fires when the dragged row(s) leave another DD target while being dragged
63343          * @param {Grid} this
63344          * @param {Roo.GridDD} dd The drag drop object
63345          * @param {String} targetId The target drag drop object
63346          * @param {event} e The raw browser event
63347          */
63348         "dragout" : true,
63349         /**
63350          * @event rowclass
63351          * Fires when a row is rendered, so you can change add a style to it.
63352          * @param {GridView} gridview   The grid view
63353          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63354          */
63355         'rowclass' : true,
63356
63357         /**
63358          * @event render
63359          * Fires when the grid is rendered
63360          * @param {Grid} grid
63361          */
63362         'render' : true,
63363             /**
63364              * @event select
63365              * Fires when a date is selected
63366              * @param {DatePicker} this
63367              * @param {Date} date The selected date
63368              */
63369         'select': true,
63370         /**
63371              * @event monthchange
63372              * Fires when the displayed month changes 
63373              * @param {DatePicker} this
63374              * @param {Date} date The selected month
63375              */
63376         'monthchange': true,
63377         /**
63378              * @event evententer
63379              * Fires when mouse over an event
63380              * @param {Calendar} this
63381              * @param {event} Event
63382              */
63383         'evententer': true,
63384         /**
63385              * @event eventleave
63386              * Fires when the mouse leaves an
63387              * @param {Calendar} this
63388              * @param {event}
63389              */
63390         'eventleave': true,
63391         /**
63392              * @event eventclick
63393              * Fires when the mouse click an
63394              * @param {Calendar} this
63395              * @param {event}
63396              */
63397         'eventclick': true,
63398         /**
63399              * @event eventrender
63400              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63401              * @param {Calendar} this
63402              * @param {data} data to be modified
63403              */
63404         'eventrender': true
63405         
63406     });
63407
63408     Roo.grid.Grid.superclass.constructor.call(this);
63409     this.on('render', function() {
63410         this.view.el.addClass('x-grid-cal'); 
63411         
63412         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63413
63414     },this);
63415     
63416     if (!Roo.grid.Calendar.style) {
63417         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63418             
63419             
63420             '.x-grid-cal .x-grid-col' :  {
63421                 height: 'auto !important',
63422                 'vertical-align': 'top'
63423             },
63424             '.x-grid-cal  .fc-event-hori' : {
63425                 height: '14px'
63426             }
63427              
63428             
63429         }, Roo.id());
63430     }
63431
63432     
63433     
63434 };
63435 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63436     /**
63437      * @cfg {Store} eventStore The store that loads events.
63438      */
63439     eventStore : 25,
63440
63441      
63442     activeDate : false,
63443     startDay : 0,
63444     autoWidth : true,
63445     monitorWindowResize : false,
63446
63447     
63448     resizeColumns : function() {
63449         var col = (this.view.el.getWidth() / 7) - 3;
63450         // loop through cols, and setWidth
63451         for(var i =0 ; i < 7 ; i++){
63452             this.cm.setColumnWidth(i, col);
63453         }
63454     },
63455      setDate :function(date) {
63456         
63457         Roo.log('setDate?');
63458         
63459         this.resizeColumns();
63460         var vd = this.activeDate;
63461         this.activeDate = date;
63462 //        if(vd && this.el){
63463 //            var t = date.getTime();
63464 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63465 //                Roo.log('using add remove');
63466 //                
63467 //                this.fireEvent('monthchange', this, date);
63468 //                
63469 //                this.cells.removeClass("fc-state-highlight");
63470 //                this.cells.each(function(c){
63471 //                   if(c.dateValue == t){
63472 //                       c.addClass("fc-state-highlight");
63473 //                       setTimeout(function(){
63474 //                            try{c.dom.firstChild.focus();}catch(e){}
63475 //                       }, 50);
63476 //                       return false;
63477 //                   }
63478 //                   return true;
63479 //                });
63480 //                return;
63481 //            }
63482 //        }
63483         
63484         var days = date.getDaysInMonth();
63485         
63486         var firstOfMonth = date.getFirstDateOfMonth();
63487         var startingPos = firstOfMonth.getDay()-this.startDay;
63488         
63489         if(startingPos < this.startDay){
63490             startingPos += 7;
63491         }
63492         
63493         var pm = date.add(Date.MONTH, -1);
63494         var prevStart = pm.getDaysInMonth()-startingPos;
63495 //        
63496         
63497         
63498         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63499         
63500         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63501         //this.cells.addClassOnOver('fc-state-hover');
63502         
63503         var cells = this.cells.elements;
63504         var textEls = this.textNodes;
63505         
63506         //Roo.each(cells, function(cell){
63507         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63508         //});
63509         
63510         days += startingPos;
63511
63512         // convert everything to numbers so it's fast
63513         var day = 86400000;
63514         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63515         //Roo.log(d);
63516         //Roo.log(pm);
63517         //Roo.log(prevStart);
63518         
63519         var today = new Date().clearTime().getTime();
63520         var sel = date.clearTime().getTime();
63521         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63522         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63523         var ddMatch = this.disabledDatesRE;
63524         var ddText = this.disabledDatesText;
63525         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63526         var ddaysText = this.disabledDaysText;
63527         var format = this.format;
63528         
63529         var setCellClass = function(cal, cell){
63530             
63531             //Roo.log('set Cell Class');
63532             cell.title = "";
63533             var t = d.getTime();
63534             
63535             //Roo.log(d);
63536             
63537             
63538             cell.dateValue = t;
63539             if(t == today){
63540                 cell.className += " fc-today";
63541                 cell.className += " fc-state-highlight";
63542                 cell.title = cal.todayText;
63543             }
63544             if(t == sel){
63545                 // disable highlight in other month..
63546                 cell.className += " fc-state-highlight";
63547                 
63548             }
63549             // disabling
63550             if(t < min) {
63551                 //cell.className = " fc-state-disabled";
63552                 cell.title = cal.minText;
63553                 return;
63554             }
63555             if(t > max) {
63556                 //cell.className = " fc-state-disabled";
63557                 cell.title = cal.maxText;
63558                 return;
63559             }
63560             if(ddays){
63561                 if(ddays.indexOf(d.getDay()) != -1){
63562                     // cell.title = ddaysText;
63563                    // cell.className = " fc-state-disabled";
63564                 }
63565             }
63566             if(ddMatch && format){
63567                 var fvalue = d.dateFormat(format);
63568                 if(ddMatch.test(fvalue)){
63569                     cell.title = ddText.replace("%0", fvalue);
63570                    cell.className = " fc-state-disabled";
63571                 }
63572             }
63573             
63574             if (!cell.initialClassName) {
63575                 cell.initialClassName = cell.dom.className;
63576             }
63577             
63578             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63579         };
63580
63581         var i = 0;
63582         
63583         for(; i < startingPos; i++) {
63584             cells[i].dayName =  (++prevStart);
63585             Roo.log(textEls[i]);
63586             d.setDate(d.getDate()+1);
63587             
63588             //cells[i].className = "fc-past fc-other-month";
63589             setCellClass(this, cells[i]);
63590         }
63591         
63592         var intDay = 0;
63593         
63594         for(; i < days; i++){
63595             intDay = i - startingPos + 1;
63596             cells[i].dayName =  (intDay);
63597             d.setDate(d.getDate()+1);
63598             
63599             cells[i].className = ''; // "x-date-active";
63600             setCellClass(this, cells[i]);
63601         }
63602         var extraDays = 0;
63603         
63604         for(; i < 42; i++) {
63605             //textEls[i].innerHTML = (++extraDays);
63606             
63607             d.setDate(d.getDate()+1);
63608             cells[i].dayName = (++extraDays);
63609             cells[i].className = "fc-future fc-other-month";
63610             setCellClass(this, cells[i]);
63611         }
63612         
63613         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63614         
63615         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63616         
63617         // this will cause all the cells to mis
63618         var rows= [];
63619         var i =0;
63620         for (var r = 0;r < 6;r++) {
63621             for (var c =0;c < 7;c++) {
63622                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63623             }    
63624         }
63625         
63626         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63627         for(i=0;i<cells.length;i++) {
63628             
63629             this.cells.elements[i].dayName = cells[i].dayName ;
63630             this.cells.elements[i].className = cells[i].className;
63631             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63632             this.cells.elements[i].title = cells[i].title ;
63633             this.cells.elements[i].dateValue = cells[i].dateValue ;
63634         }
63635         
63636         
63637         
63638         
63639         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63640         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63641         
63642         ////if(totalRows != 6){
63643             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63644            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63645        // }
63646         
63647         this.fireEvent('monthchange', this, date);
63648         
63649         
63650     },
63651  /**
63652      * Returns the grid's SelectionModel.
63653      * @return {SelectionModel}
63654      */
63655     getSelectionModel : function(){
63656         if(!this.selModel){
63657             this.selModel = new Roo.grid.CellSelectionModel();
63658         }
63659         return this.selModel;
63660     },
63661
63662     load: function() {
63663         this.eventStore.load()
63664         
63665         
63666         
63667     },
63668     
63669     findCell : function(dt) {
63670         dt = dt.clearTime().getTime();
63671         var ret = false;
63672         this.cells.each(function(c){
63673             //Roo.log("check " +c.dateValue + '?=' + dt);
63674             if(c.dateValue == dt){
63675                 ret = c;
63676                 return false;
63677             }
63678             return true;
63679         });
63680         
63681         return ret;
63682     },
63683     
63684     findCells : function(rec) {
63685         var s = rec.data.start_dt.clone().clearTime().getTime();
63686        // Roo.log(s);
63687         var e= rec.data.end_dt.clone().clearTime().getTime();
63688        // Roo.log(e);
63689         var ret = [];
63690         this.cells.each(function(c){
63691              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63692             
63693             if(c.dateValue > e){
63694                 return ;
63695             }
63696             if(c.dateValue < s){
63697                 return ;
63698             }
63699             ret.push(c);
63700         });
63701         
63702         return ret;    
63703     },
63704     
63705     findBestRow: function(cells)
63706     {
63707         var ret = 0;
63708         
63709         for (var i =0 ; i < cells.length;i++) {
63710             ret  = Math.max(cells[i].rows || 0,ret);
63711         }
63712         return ret;
63713         
63714     },
63715     
63716     
63717     addItem : function(rec)
63718     {
63719         // look for vertical location slot in
63720         var cells = this.findCells(rec);
63721         
63722         rec.row = this.findBestRow(cells);
63723         
63724         // work out the location.
63725         
63726         var crow = false;
63727         var rows = [];
63728         for(var i =0; i < cells.length; i++) {
63729             if (!crow) {
63730                 crow = {
63731                     start : cells[i],
63732                     end :  cells[i]
63733                 };
63734                 continue;
63735             }
63736             if (crow.start.getY() == cells[i].getY()) {
63737                 // on same row.
63738                 crow.end = cells[i];
63739                 continue;
63740             }
63741             // different row.
63742             rows.push(crow);
63743             crow = {
63744                 start: cells[i],
63745                 end : cells[i]
63746             };
63747             
63748         }
63749         
63750         rows.push(crow);
63751         rec.els = [];
63752         rec.rows = rows;
63753         rec.cells = cells;
63754         for (var i = 0; i < cells.length;i++) {
63755             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63756             
63757         }
63758         
63759         
63760     },
63761     
63762     clearEvents: function() {
63763         
63764         if (!this.eventStore.getCount()) {
63765             return;
63766         }
63767         // reset number of rows in cells.
63768         Roo.each(this.cells.elements, function(c){
63769             c.rows = 0;
63770         });
63771         
63772         this.eventStore.each(function(e) {
63773             this.clearEvent(e);
63774         },this);
63775         
63776     },
63777     
63778     clearEvent : function(ev)
63779     {
63780         if (ev.els) {
63781             Roo.each(ev.els, function(el) {
63782                 el.un('mouseenter' ,this.onEventEnter, this);
63783                 el.un('mouseleave' ,this.onEventLeave, this);
63784                 el.remove();
63785             },this);
63786             ev.els = [];
63787         }
63788     },
63789     
63790     
63791     renderEvent : function(ev,ctr) {
63792         if (!ctr) {
63793              ctr = this.view.el.select('.fc-event-container',true).first();
63794         }
63795         
63796          
63797         this.clearEvent(ev);
63798             //code
63799        
63800         
63801         
63802         ev.els = [];
63803         var cells = ev.cells;
63804         var rows = ev.rows;
63805         this.fireEvent('eventrender', this, ev);
63806         
63807         for(var i =0; i < rows.length; i++) {
63808             
63809             cls = '';
63810             if (i == 0) {
63811                 cls += ' fc-event-start';
63812             }
63813             if ((i+1) == rows.length) {
63814                 cls += ' fc-event-end';
63815             }
63816             
63817             //Roo.log(ev.data);
63818             // how many rows should it span..
63819             var cg = this.eventTmpl.append(ctr,Roo.apply({
63820                 fccls : cls
63821                 
63822             }, ev.data) , true);
63823             
63824             
63825             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63826             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63827             cg.on('click', this.onEventClick, this, ev);
63828             
63829             ev.els.push(cg);
63830             
63831             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63832             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63833             //Roo.log(cg);
63834              
63835             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63836             cg.setWidth(ebox.right - sbox.x -2);
63837         }
63838     },
63839     
63840     renderEvents: function()
63841     {   
63842         // first make sure there is enough space..
63843         
63844         if (!this.eventTmpl) {
63845             this.eventTmpl = new Roo.Template(
63846                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63847                     '<div class="fc-event-inner">' +
63848                         '<span class="fc-event-time">{time}</span>' +
63849                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63850                     '</div>' +
63851                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63852                 '</div>'
63853             );
63854                 
63855         }
63856                
63857         
63858         
63859         this.cells.each(function(c) {
63860             //Roo.log(c.select('.fc-day-content div',true).first());
63861             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63862         });
63863         
63864         var ctr = this.view.el.select('.fc-event-container',true).first();
63865         
63866         var cls;
63867         this.eventStore.each(function(ev){
63868             
63869             this.renderEvent(ev);
63870              
63871              
63872         }, this);
63873         this.view.layout();
63874         
63875     },
63876     
63877     onEventEnter: function (e, el,event,d) {
63878         this.fireEvent('evententer', this, el, event);
63879     },
63880     
63881     onEventLeave: function (e, el,event,d) {
63882         this.fireEvent('eventleave', this, el, event);
63883     },
63884     
63885     onEventClick: function (e, el,event,d) {
63886         this.fireEvent('eventclick', this, el, event);
63887     },
63888     
63889     onMonthChange: function () {
63890         this.store.load();
63891     },
63892     
63893     onLoad: function () {
63894         
63895         //Roo.log('calendar onload');
63896 //         
63897         if(this.eventStore.getCount() > 0){
63898             
63899            
63900             
63901             this.eventStore.each(function(d){
63902                 
63903                 
63904                 // FIXME..
63905                 var add =   d.data;
63906                 if (typeof(add.end_dt) == 'undefined')  {
63907                     Roo.log("Missing End time in calendar data: ");
63908                     Roo.log(d);
63909                     return;
63910                 }
63911                 if (typeof(add.start_dt) == 'undefined')  {
63912                     Roo.log("Missing Start time in calendar data: ");
63913                     Roo.log(d);
63914                     return;
63915                 }
63916                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63917                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63918                 add.id = add.id || d.id;
63919                 add.title = add.title || '??';
63920                 
63921                 this.addItem(d);
63922                 
63923              
63924             },this);
63925         }
63926         
63927         this.renderEvents();
63928     }
63929     
63930
63931 });
63932 /*
63933  grid : {
63934                 xtype: 'Grid',
63935                 xns: Roo.grid,
63936                 listeners : {
63937                     render : function ()
63938                     {
63939                         _this.grid = this;
63940                         
63941                         if (!this.view.el.hasClass('course-timesheet')) {
63942                             this.view.el.addClass('course-timesheet');
63943                         }
63944                         if (this.tsStyle) {
63945                             this.ds.load({});
63946                             return; 
63947                         }
63948                         Roo.log('width');
63949                         Roo.log(_this.grid.view.el.getWidth());
63950                         
63951                         
63952                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63953                             '.course-timesheet .x-grid-row' : {
63954                                 height: '80px'
63955                             },
63956                             '.x-grid-row td' : {
63957                                 'vertical-align' : 0
63958                             },
63959                             '.course-edit-link' : {
63960                                 'color' : 'blue',
63961                                 'text-overflow' : 'ellipsis',
63962                                 'overflow' : 'hidden',
63963                                 'white-space' : 'nowrap',
63964                                 'cursor' : 'pointer'
63965                             },
63966                             '.sub-link' : {
63967                                 'color' : 'green'
63968                             },
63969                             '.de-act-sup-link' : {
63970                                 'color' : 'purple',
63971                                 'text-decoration' : 'line-through'
63972                             },
63973                             '.de-act-link' : {
63974                                 'color' : 'red',
63975                                 'text-decoration' : 'line-through'
63976                             },
63977                             '.course-timesheet .course-highlight' : {
63978                                 'border-top-style': 'dashed !important',
63979                                 'border-bottom-bottom': 'dashed !important'
63980                             },
63981                             '.course-timesheet .course-item' : {
63982                                 'font-family'   : 'tahoma, arial, helvetica',
63983                                 'font-size'     : '11px',
63984                                 'overflow'      : 'hidden',
63985                                 'padding-left'  : '10px',
63986                                 'padding-right' : '10px',
63987                                 'padding-top' : '10px' 
63988                             }
63989                             
63990                         }, Roo.id());
63991                                 this.ds.load({});
63992                     }
63993                 },
63994                 autoWidth : true,
63995                 monitorWindowResize : false,
63996                 cellrenderer : function(v,x,r)
63997                 {
63998                     return v;
63999                 },
64000                 sm : {
64001                     xtype: 'CellSelectionModel',
64002                     xns: Roo.grid
64003                 },
64004                 dataSource : {
64005                     xtype: 'Store',
64006                     xns: Roo.data,
64007                     listeners : {
64008                         beforeload : function (_self, options)
64009                         {
64010                             options.params = options.params || {};
64011                             options.params._month = _this.monthField.getValue();
64012                             options.params.limit = 9999;
64013                             options.params['sort'] = 'when_dt';    
64014                             options.params['dir'] = 'ASC';    
64015                             this.proxy.loadResponse = this.loadResponse;
64016                             Roo.log("load?");
64017                             //this.addColumns();
64018                         },
64019                         load : function (_self, records, options)
64020                         {
64021                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64022                                 // if you click on the translation.. you can edit it...
64023                                 var el = Roo.get(this);
64024                                 var id = el.dom.getAttribute('data-id');
64025                                 var d = el.dom.getAttribute('data-date');
64026                                 var t = el.dom.getAttribute('data-time');
64027                                 //var id = this.child('span').dom.textContent;
64028                                 
64029                                 //Roo.log(this);
64030                                 Pman.Dialog.CourseCalendar.show({
64031                                     id : id,
64032                                     when_d : d,
64033                                     when_t : t,
64034                                     productitem_active : id ? 1 : 0
64035                                 }, function() {
64036                                     _this.grid.ds.load({});
64037                                 });
64038                            
64039                            });
64040                            
64041                            _this.panel.fireEvent('resize', [ '', '' ]);
64042                         }
64043                     },
64044                     loadResponse : function(o, success, response){
64045                             // this is overridden on before load..
64046                             
64047                             Roo.log("our code?");       
64048                             //Roo.log(success);
64049                             //Roo.log(response)
64050                             delete this.activeRequest;
64051                             if(!success){
64052                                 this.fireEvent("loadexception", this, o, response);
64053                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64054                                 return;
64055                             }
64056                             var result;
64057                             try {
64058                                 result = o.reader.read(response);
64059                             }catch(e){
64060                                 Roo.log("load exception?");
64061                                 this.fireEvent("loadexception", this, o, response, e);
64062                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64063                                 return;
64064                             }
64065                             Roo.log("ready...");        
64066                             // loop through result.records;
64067                             // and set this.tdate[date] = [] << array of records..
64068                             _this.tdata  = {};
64069                             Roo.each(result.records, function(r){
64070                                 //Roo.log(r.data);
64071                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64072                                     _this.tdata[r.data.when_dt.format('j')] = [];
64073                                 }
64074                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64075                             });
64076                             
64077                             //Roo.log(_this.tdata);
64078                             
64079                             result.records = [];
64080                             result.totalRecords = 6;
64081                     
64082                             // let's generate some duumy records for the rows.
64083                             //var st = _this.dateField.getValue();
64084                             
64085                             // work out monday..
64086                             //st = st.add(Date.DAY, -1 * st.format('w'));
64087                             
64088                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64089                             
64090                             var firstOfMonth = date.getFirstDayOfMonth();
64091                             var days = date.getDaysInMonth();
64092                             var d = 1;
64093                             var firstAdded = false;
64094                             for (var i = 0; i < result.totalRecords ; i++) {
64095                                 //var d= st.add(Date.DAY, i);
64096                                 var row = {};
64097                                 var added = 0;
64098                                 for(var w = 0 ; w < 7 ; w++){
64099                                     if(!firstAdded && firstOfMonth != w){
64100                                         continue;
64101                                     }
64102                                     if(d > days){
64103                                         continue;
64104                                     }
64105                                     firstAdded = true;
64106                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64107                                     row['weekday'+w] = String.format(
64108                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64109                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64110                                                     d,
64111                                                     date.format('Y-m-')+dd
64112                                                 );
64113                                     added++;
64114                                     if(typeof(_this.tdata[d]) != 'undefined'){
64115                                         Roo.each(_this.tdata[d], function(r){
64116                                             var is_sub = '';
64117                                             var deactive = '';
64118                                             var id = r.id;
64119                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64120                                             if(r.parent_id*1>0){
64121                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64122                                                 id = r.parent_id;
64123                                             }
64124                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64125                                                 deactive = 'de-act-link';
64126                                             }
64127                                             
64128                                             row['weekday'+w] += String.format(
64129                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64130                                                     id, //0
64131                                                     r.product_id_name, //1
64132                                                     r.when_dt.format('h:ia'), //2
64133                                                     is_sub, //3
64134                                                     deactive, //4
64135                                                     desc // 5
64136                                             );
64137                                         });
64138                                     }
64139                                     d++;
64140                                 }
64141                                 
64142                                 // only do this if something added..
64143                                 if(added > 0){ 
64144                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64145                                 }
64146                                 
64147                                 
64148                                 // push it twice. (second one with an hour..
64149                                 
64150                             }
64151                             //Roo.log(result);
64152                             this.fireEvent("load", this, o, o.request.arg);
64153                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64154                         },
64155                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64156                     proxy : {
64157                         xtype: 'HttpProxy',
64158                         xns: Roo.data,
64159                         method : 'GET',
64160                         url : baseURL + '/Roo/Shop_course.php'
64161                     },
64162                     reader : {
64163                         xtype: 'JsonReader',
64164                         xns: Roo.data,
64165                         id : 'id',
64166                         fields : [
64167                             {
64168                                 'name': 'id',
64169                                 'type': 'int'
64170                             },
64171                             {
64172                                 'name': 'when_dt',
64173                                 'type': 'string'
64174                             },
64175                             {
64176                                 'name': 'end_dt',
64177                                 'type': 'string'
64178                             },
64179                             {
64180                                 'name': 'parent_id',
64181                                 'type': 'int'
64182                             },
64183                             {
64184                                 'name': 'product_id',
64185                                 'type': 'int'
64186                             },
64187                             {
64188                                 'name': 'productitem_id',
64189                                 'type': 'int'
64190                             },
64191                             {
64192                                 'name': 'guid',
64193                                 'type': 'int'
64194                             }
64195                         ]
64196                     }
64197                 },
64198                 toolbar : {
64199                     xtype: 'Toolbar',
64200                     xns: Roo,
64201                     items : [
64202                         {
64203                             xtype: 'Button',
64204                             xns: Roo.Toolbar,
64205                             listeners : {
64206                                 click : function (_self, e)
64207                                 {
64208                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64209                                     sd.setMonth(sd.getMonth()-1);
64210                                     _this.monthField.setValue(sd.format('Y-m-d'));
64211                                     _this.grid.ds.load({});
64212                                 }
64213                             },
64214                             text : "Back"
64215                         },
64216                         {
64217                             xtype: 'Separator',
64218                             xns: Roo.Toolbar
64219                         },
64220                         {
64221                             xtype: 'MonthField',
64222                             xns: Roo.form,
64223                             listeners : {
64224                                 render : function (_self)
64225                                 {
64226                                     _this.monthField = _self;
64227                                    // _this.monthField.set  today
64228                                 },
64229                                 select : function (combo, date)
64230                                 {
64231                                     _this.grid.ds.load({});
64232                                 }
64233                             },
64234                             value : (function() { return new Date(); })()
64235                         },
64236                         {
64237                             xtype: 'Separator',
64238                             xns: Roo.Toolbar
64239                         },
64240                         {
64241                             xtype: 'TextItem',
64242                             xns: Roo.Toolbar,
64243                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64244                         },
64245                         {
64246                             xtype: 'Fill',
64247                             xns: Roo.Toolbar
64248                         },
64249                         {
64250                             xtype: 'Button',
64251                             xns: Roo.Toolbar,
64252                             listeners : {
64253                                 click : function (_self, e)
64254                                 {
64255                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64256                                     sd.setMonth(sd.getMonth()+1);
64257                                     _this.monthField.setValue(sd.format('Y-m-d'));
64258                                     _this.grid.ds.load({});
64259                                 }
64260                             },
64261                             text : "Next"
64262                         }
64263                     ]
64264                 },
64265                  
64266             }
64267         };
64268         
64269         *//*
64270  * Based on:
64271  * Ext JS Library 1.1.1
64272  * Copyright(c) 2006-2007, Ext JS, LLC.
64273  *
64274  * Originally Released Under LGPL - original licence link has changed is not relivant.
64275  *
64276  * Fork - LGPL
64277  * <script type="text/javascript">
64278  */
64279  
64280 /**
64281  * @class Roo.LoadMask
64282  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64283  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64284  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64285  * element's UpdateManager load indicator and will be destroyed after the initial load.
64286  * @constructor
64287  * Create a new LoadMask
64288  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64289  * @param {Object} config The config object
64290  */
64291 Roo.LoadMask = function(el, config){
64292     this.el = Roo.get(el);
64293     Roo.apply(this, config);
64294     if(this.store){
64295         this.store.on('beforeload', this.onBeforeLoad, this);
64296         this.store.on('load', this.onLoad, this);
64297         this.store.on('loadexception', this.onLoadException, this);
64298         this.removeMask = false;
64299     }else{
64300         var um = this.el.getUpdateManager();
64301         um.showLoadIndicator = false; // disable the default indicator
64302         um.on('beforeupdate', this.onBeforeLoad, this);
64303         um.on('update', this.onLoad, this);
64304         um.on('failure', this.onLoad, this);
64305         this.removeMask = true;
64306     }
64307 };
64308
64309 Roo.LoadMask.prototype = {
64310     /**
64311      * @cfg {Boolean} removeMask
64312      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64313      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64314      */
64315     removeMask : false,
64316     /**
64317      * @cfg {String} msg
64318      * The text to display in a centered loading message box (defaults to 'Loading...')
64319      */
64320     msg : 'Loading...',
64321     /**
64322      * @cfg {String} msgCls
64323      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64324      */
64325     msgCls : 'x-mask-loading',
64326
64327     /**
64328      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64329      * @type Boolean
64330      */
64331     disabled: false,
64332
64333     /**
64334      * Disables the mask to prevent it from being displayed
64335      */
64336     disable : function(){
64337        this.disabled = true;
64338     },
64339
64340     /**
64341      * Enables the mask so that it can be displayed
64342      */
64343     enable : function(){
64344         this.disabled = false;
64345     },
64346     
64347     onLoadException : function()
64348     {
64349         Roo.log(arguments);
64350         
64351         if (typeof(arguments[3]) != 'undefined') {
64352             Roo.MessageBox.alert("Error loading",arguments[3]);
64353         } 
64354         /*
64355         try {
64356             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64357                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64358             }   
64359         } catch(e) {
64360             
64361         }
64362         */
64363     
64364         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64365     },
64366     // private
64367     onLoad : function()
64368     {
64369         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64370     },
64371
64372     // private
64373     onBeforeLoad : function(){
64374         if(!this.disabled){
64375             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64376         }
64377     },
64378
64379     // private
64380     destroy : function(){
64381         if(this.store){
64382             this.store.un('beforeload', this.onBeforeLoad, this);
64383             this.store.un('load', this.onLoad, this);
64384             this.store.un('loadexception', this.onLoadException, this);
64385         }else{
64386             var um = this.el.getUpdateManager();
64387             um.un('beforeupdate', this.onBeforeLoad, this);
64388             um.un('update', this.onLoad, this);
64389             um.un('failure', this.onLoad, this);
64390         }
64391     }
64392 };/*
64393  * Based on:
64394  * Ext JS Library 1.1.1
64395  * Copyright(c) 2006-2007, Ext JS, LLC.
64396  *
64397  * Originally Released Under LGPL - original licence link has changed is not relivant.
64398  *
64399  * Fork - LGPL
64400  * <script type="text/javascript">
64401  */
64402
64403
64404 /**
64405  * @class Roo.XTemplate
64406  * @extends Roo.Template
64407  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64408 <pre><code>
64409 var t = new Roo.XTemplate(
64410         '&lt;select name="{name}"&gt;',
64411                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64412         '&lt;/select&gt;'
64413 );
64414  
64415 // then append, applying the master template values
64416  </code></pre>
64417  *
64418  * Supported features:
64419  *
64420  *  Tags:
64421
64422 <pre><code>
64423       {a_variable} - output encoded.
64424       {a_variable.format:("Y-m-d")} - call a method on the variable
64425       {a_variable:raw} - unencoded output
64426       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64427       {a_variable:this.method_on_template(...)} - call a method on the template object.
64428  
64429 </code></pre>
64430  *  The tpl tag:
64431 <pre><code>
64432         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64433         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64434         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64435         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64436   
64437         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64438         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64439 </code></pre>
64440  *      
64441  */
64442 Roo.XTemplate = function()
64443 {
64444     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64445     if (this.html) {
64446         this.compile();
64447     }
64448 };
64449
64450
64451 Roo.extend(Roo.XTemplate, Roo.Template, {
64452
64453     /**
64454      * The various sub templates
64455      */
64456     tpls : false,
64457     /**
64458      *
64459      * basic tag replacing syntax
64460      * WORD:WORD()
64461      *
64462      * // you can fake an object call by doing this
64463      *  x.t:(test,tesT) 
64464      * 
64465      */
64466     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64467
64468     /**
64469      * compile the template
64470      *
64471      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64472      *
64473      */
64474     compile: function()
64475     {
64476         var s = this.html;
64477      
64478         s = ['<tpl>', s, '</tpl>'].join('');
64479     
64480         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64481             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64482             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64483             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64484             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64485             m,
64486             id     = 0,
64487             tpls   = [];
64488     
64489         while(true == !!(m = s.match(re))){
64490             var forMatch   = m[0].match(nameRe),
64491                 ifMatch   = m[0].match(ifRe),
64492                 execMatch   = m[0].match(execRe),
64493                 namedMatch   = m[0].match(namedRe),
64494                 
64495                 exp  = null, 
64496                 fn   = null,
64497                 exec = null,
64498                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64499                 
64500             if (ifMatch) {
64501                 // if - puts fn into test..
64502                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64503                 if(exp){
64504                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64505                 }
64506             }
64507             
64508             if (execMatch) {
64509                 // exec - calls a function... returns empty if true is  returned.
64510                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64511                 if(exp){
64512                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64513                 }
64514             }
64515             
64516             
64517             if (name) {
64518                 // for = 
64519                 switch(name){
64520                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64521                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64522                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64523                 }
64524             }
64525             var uid = namedMatch ? namedMatch[1] : id;
64526             
64527             
64528             tpls.push({
64529                 id:     namedMatch ? namedMatch[1] : id,
64530                 target: name,
64531                 exec:   exec,
64532                 test:   fn,
64533                 body:   m[1] || ''
64534             });
64535             if (namedMatch) {
64536                 s = s.replace(m[0], '');
64537             } else { 
64538                 s = s.replace(m[0], '{xtpl'+ id + '}');
64539             }
64540             ++id;
64541         }
64542         this.tpls = [];
64543         for(var i = tpls.length-1; i >= 0; --i){
64544             this.compileTpl(tpls[i]);
64545             this.tpls[tpls[i].id] = tpls[i];
64546         }
64547         this.master = tpls[tpls.length-1];
64548         return this;
64549     },
64550     /**
64551      * same as applyTemplate, except it's done to one of the subTemplates
64552      * when using named templates, you can do:
64553      *
64554      * var str = pl.applySubTemplate('your-name', values);
64555      *
64556      * 
64557      * @param {Number} id of the template
64558      * @param {Object} values to apply to template
64559      * @param {Object} parent (normaly the instance of this object)
64560      */
64561     applySubTemplate : function(id, values, parent)
64562     {
64563         
64564         
64565         var t = this.tpls[id];
64566         
64567         
64568         try { 
64569             if(t.test && !t.test.call(this, values, parent)){
64570                 return '';
64571             }
64572         } catch(e) {
64573             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64574             Roo.log(e.toString());
64575             Roo.log(t.test);
64576             return ''
64577         }
64578         try { 
64579             
64580             if(t.exec && t.exec.call(this, values, parent)){
64581                 return '';
64582             }
64583         } catch(e) {
64584             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64585             Roo.log(e.toString());
64586             Roo.log(t.exec);
64587             return ''
64588         }
64589         try {
64590             var vs = t.target ? t.target.call(this, values, parent) : values;
64591             parent = t.target ? values : parent;
64592             if(t.target && vs instanceof Array){
64593                 var buf = [];
64594                 for(var i = 0, len = vs.length; i < len; i++){
64595                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64596                 }
64597                 return buf.join('');
64598             }
64599             return t.compiled.call(this, vs, parent);
64600         } catch (e) {
64601             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64602             Roo.log(e.toString());
64603             Roo.log(t.compiled);
64604             return '';
64605         }
64606     },
64607
64608     compileTpl : function(tpl)
64609     {
64610         var fm = Roo.util.Format;
64611         var useF = this.disableFormats !== true;
64612         var sep = Roo.isGecko ? "+" : ",";
64613         var undef = function(str) {
64614             Roo.log("Property not found :"  + str);
64615             return '';
64616         };
64617         
64618         var fn = function(m, name, format, args)
64619         {
64620             //Roo.log(arguments);
64621             args = args ? args.replace(/\\'/g,"'") : args;
64622             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64623             if (typeof(format) == 'undefined') {
64624                 format= 'htmlEncode';
64625             }
64626             if (format == 'raw' ) {
64627                 format = false;
64628             }
64629             
64630             if(name.substr(0, 4) == 'xtpl'){
64631                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64632             }
64633             
64634             // build an array of options to determine if value is undefined..
64635             
64636             // basically get 'xxxx.yyyy' then do
64637             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64638             //    (function () { Roo.log("Property not found"); return ''; })() :
64639             //    ......
64640             
64641             var udef_ar = [];
64642             var lookfor = '';
64643             Roo.each(name.split('.'), function(st) {
64644                 lookfor += (lookfor.length ? '.': '') + st;
64645                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64646             });
64647             
64648             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64649             
64650             
64651             if(format && useF){
64652                 
64653                 args = args ? ',' + args : "";
64654                  
64655                 if(format.substr(0, 5) != "this."){
64656                     format = "fm." + format + '(';
64657                 }else{
64658                     format = 'this.call("'+ format.substr(5) + '", ';
64659                     args = ", values";
64660                 }
64661                 
64662                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64663             }
64664              
64665             if (args.length) {
64666                 // called with xxyx.yuu:(test,test)
64667                 // change to ()
64668                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64669             }
64670             // raw.. - :raw modifier..
64671             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64672             
64673         };
64674         var body;
64675         // branched to use + in gecko and [].join() in others
64676         if(Roo.isGecko){
64677             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64678                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64679                     "';};};";
64680         }else{
64681             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64682             body.push(tpl.body.replace(/(\r\n|\n)/g,
64683                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64684             body.push("'].join('');};};");
64685             body = body.join('');
64686         }
64687         
64688         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64689        
64690         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64691         eval(body);
64692         
64693         return this;
64694     },
64695
64696     applyTemplate : function(values){
64697         return this.master.compiled.call(this, values, {});
64698         //var s = this.subs;
64699     },
64700
64701     apply : function(){
64702         return this.applyTemplate.apply(this, arguments);
64703     }
64704
64705  });
64706
64707 Roo.XTemplate.from = function(el){
64708     el = Roo.getDom(el);
64709     return new Roo.XTemplate(el.value || el.innerHTML);
64710 };