roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /**
4901  * Originally based of this code... - refactored for Roo...
4902  * https://github.com/aaalsaleh/undo-manager
4903  
4904  * undo-manager.js
4905  * @author  Abdulrahman Alsaleh 
4906  * @copyright 2015 Abdulrahman Alsaleh 
4907  * @license  MIT License (c) 
4908  *
4909  * Hackily modifyed by alan@roojs.com
4910  *
4911  *
4912  *  
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     this.reset();
4951     
4952 };
4953         
4954 Roo.lib.UndoManager.prototype = {
4955     
4956     limit : false,
4957     stack : false,
4958     scope :  false,
4959     fireEvent : false,
4960     position : 0,
4961     length : 0,
4962     
4963     
4964      /**
4965      * To push and execute a transaction, the method undoManager.transact
4966      * must be called by passing a transaction object as the first argument, and a merge
4967      * flag as the second argument. A transaction object has the following properties:
4968      *
4969      * Usage:
4970 <pre><code>
4971 undoManager.transact({
4972     label: 'Typing',
4973     execute: function() { ... },
4974     undo: function() { ... },
4975     // redo same as execute
4976     redo: function() { this.execute(); }
4977 }, false);
4978
4979 // merge transaction
4980 undoManager.transact({
4981     label: 'Typing',
4982     execute: function() { ... },  // this will be run...
4983     undo: function() { ... }, // what to do when undo is run.
4984     // redo same as execute
4985     redo: function() { this.execute(); }
4986 }, true); 
4987 </code></pre> 
4988      *
4989      * 
4990      * @param {Object} transaction The transaction to add to the stack.
4991      * @return {String} The HTML fragment
4992      */
4993     
4994     
4995     transact : function (transaction, merge)
4996     {
4997         if (arguments.length < 2) {
4998             throw new TypeError('Not enough arguments to UndoManager.transact.');
4999         }
5000
5001         transaction.execute();
5002
5003         this.stack.splice(0, this.position);
5004         if (merge && this.length) {
5005             this.stack[0].push(transaction);
5006         } else {
5007             this.stack.unshift([transaction]);
5008         }
5009     
5010         this.position = 0;
5011
5012         if (this.limit && this.stack.length > this.limit) {
5013             this.length = this.stack.length = this.limit;
5014         } else {
5015             this.length = this.stack.length;
5016         }
5017
5018         if (this.fireEvent) {
5019             this.scope.dispatchEvent(
5020                 new CustomEvent('DOMTransaction', {
5021                     detail: {
5022                         transactions: this.stack[0].slice()
5023                     },
5024                     bubbles: true,
5025                     cancelable: false
5026                 })
5027             );
5028         }
5029         
5030         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5031       
5032         
5033     },
5034
5035     undo : function ()
5036     {
5037         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5038         
5039         if (this.position < this.length) {
5040             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5041                 this.stack[this.position][i].undo();
5042             }
5043             this.position++;
5044
5045             if (this.fireEvent) {
5046                 this.scope.dispatchEvent(
5047                     new CustomEvent('undo', {
5048                         detail: {
5049                             transactions: this.stack[this.position - 1].slice()
5050                         },
5051                         bubbles: true,
5052                         cancelable: false
5053                     })
5054                 );
5055             }
5056         }
5057     },
5058
5059     redo : function ()
5060     {
5061         if (this.position > 0) {
5062             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5063                 this.stack[this.position - 1][i].redo();
5064             }
5065             this.position--;
5066
5067             if (this.fireEvent) {
5068                 this.scope.dispatchEvent(
5069                     new CustomEvent('redo', {
5070                         detail: {
5071                             transactions: this.stack[this.position].slice()
5072                         },
5073                         bubbles: true,
5074                         cancelable: false
5075                     })
5076                 );
5077             }
5078         }
5079     },
5080
5081     item : function (index)
5082     {
5083         if (index >= 0 && index < this.length) {
5084             return this.stack[index].slice();
5085         }
5086         return null;
5087     },
5088
5089     clearUndo : function () {
5090         this.stack.length = this.length = this.position;
5091     },
5092
5093     clearRedo : function () {
5094         this.stack.splice(0, this.position);
5095         this.position = 0;
5096         this.length = this.stack.length;
5097     },
5098     /**
5099      * Reset the undo - probaly done on load to clear all history.
5100      */
5101     reset : function()
5102     {
5103         this.stack = [];
5104         this.position = 0;
5105         this.length = 0;
5106         this.current_html = this.scope.innerHTML;
5107         if (this.timer !== false) {
5108             clearTimeout(this.timer);
5109         }
5110         this.timer = false;
5111         this.merge = false;
5112         this.addEvent();
5113         
5114     },
5115     current_html : '',
5116     timer : false,
5117     merge : false,
5118     
5119     
5120     // this will handle the undo/redo on the element.?
5121     bindEvents : function()
5122     {
5123         var el  = this.scope;
5124         el.undoManager = this;
5125         
5126         
5127         this.scope.addEventListener('keydown', function(e) {
5128             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5129                 if (e.shiftKey) {
5130                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5131                 } else {
5132                     el.undoManager.undo(); // Ctrl/Command + Z
5133                 }
5134         
5135                 e.preventDefault();
5136             }
5137         });
5138         /// ignore keyup..
5139         this.scope.addEventListener('keyup', function(e) {
5140             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5141                 e.preventDefault();
5142             }
5143         });
5144         
5145         
5146         
5147         var t = this;
5148         
5149         el.addEventListener('input', function(e) {
5150             if(el.innerHTML == t.current_html) {
5151                 return;
5152             }
5153             // only record events every second.
5154             if (t.timer !== false) {
5155                clearTimeout(t.timer);
5156                t.timer = false;
5157             }
5158             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5159             
5160             t.addEvent(t.merge);
5161             t.merge = true; // ignore changes happening every second..
5162         });
5163         },
5164     /**
5165      * Manually add an event.
5166      * Normall called without arguements - and it will just get added to the stack.
5167      * 
5168      */
5169     
5170     addEvent : function(merge)
5171     {
5172         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5173         // not sure if this should clear the timer 
5174         merge = typeof(merge) == 'undefined' ? false : merge; 
5175         
5176         this.scope.undoManager.transact({
5177             scope : this.scope,
5178             oldHTML: this.current_html,
5179             newHTML: this.scope.innerHTML,
5180             // nothing to execute (content already changed when input is fired)
5181             execute: function() { },
5182             undo: function() {
5183                 this.scope.innerHTML = this.current_html = this.oldHTML;
5184             },
5185             redo: function() {
5186                 this.scope.innerHTML = this.current_html = this.newHTML;
5187             }
5188         }, false); //merge);
5189         
5190         this.merge = merge;
5191         
5192         this.current_html = this.scope.innerHTML;
5193     }
5194     
5195     
5196      
5197     
5198     
5199     
5200 };
5201 /*
5202  * Based on:
5203  * Ext JS Library 1.1.1
5204  * Copyright(c) 2006-2007, Ext JS, LLC.
5205  *
5206  * Originally Released Under LGPL - original licence link has changed is not relivant.
5207  *
5208  * Fork - LGPL
5209  * <script type="text/javascript">
5210  */
5211
5212
5213 // nasty IE9 hack - what a pile of crap that is..
5214
5215  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5216     Range.prototype.createContextualFragment = function (html) {
5217         var doc = window.document;
5218         var container = doc.createElement("div");
5219         container.innerHTML = html;
5220         var frag = doc.createDocumentFragment(), n;
5221         while ((n = container.firstChild)) {
5222             frag.appendChild(n);
5223         }
5224         return frag;
5225     };
5226 }
5227
5228 /**
5229  * @class Roo.DomHelper
5230  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5231  * 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>.
5232  * @static
5233  */
5234 Roo.DomHelper = function(){
5235     var tempTableEl = null;
5236     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5237     var tableRe = /^table|tbody|tr|td$/i;
5238     var xmlns = {};
5239     // build as innerHTML where available
5240     /** @ignore */
5241     var createHtml = function(o){
5242         if(typeof o == 'string'){
5243             return o;
5244         }
5245         var b = "";
5246         if(!o.tag){
5247             o.tag = "div";
5248         }
5249         b += "<" + o.tag;
5250         for(var attr in o){
5251             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5252             if(attr == "style"){
5253                 var s = o["style"];
5254                 if(typeof s == "function"){
5255                     s = s.call();
5256                 }
5257                 if(typeof s == "string"){
5258                     b += ' style="' + s + '"';
5259                 }else if(typeof s == "object"){
5260                     b += ' style="';
5261                     for(var key in s){
5262                         if(typeof s[key] != "function"){
5263                             b += key + ":" + s[key] + ";";
5264                         }
5265                     }
5266                     b += '"';
5267                 }
5268             }else{
5269                 if(attr == "cls"){
5270                     b += ' class="' + o["cls"] + '"';
5271                 }else if(attr == "htmlFor"){
5272                     b += ' for="' + o["htmlFor"] + '"';
5273                 }else{
5274                     b += " " + attr + '="' + o[attr] + '"';
5275                 }
5276             }
5277         }
5278         if(emptyTags.test(o.tag)){
5279             b += "/>";
5280         }else{
5281             b += ">";
5282             var cn = o.children || o.cn;
5283             if(cn){
5284                 //http://bugs.kde.org/show_bug.cgi?id=71506
5285                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5286                     for(var i = 0, len = cn.length; i < len; i++) {
5287                         b += createHtml(cn[i], b);
5288                     }
5289                 }else{
5290                     b += createHtml(cn, b);
5291                 }
5292             }
5293             if(o.html){
5294                 b += o.html;
5295             }
5296             b += "</" + o.tag + ">";
5297         }
5298         return b;
5299     };
5300
5301     // build as dom
5302     /** @ignore */
5303     var createDom = function(o, parentNode){
5304          
5305         // defininition craeted..
5306         var ns = false;
5307         if (o.ns && o.ns != 'html') {
5308                
5309             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5310                 xmlns[o.ns] = o.xmlns;
5311                 ns = o.xmlns;
5312             }
5313             if (typeof(xmlns[o.ns]) == 'undefined') {
5314                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5315             }
5316             ns = xmlns[o.ns];
5317         }
5318         
5319         
5320         if (typeof(o) == 'string') {
5321             return parentNode.appendChild(document.createTextNode(o));
5322         }
5323         o.tag = o.tag || div;
5324         if (o.ns && Roo.isIE) {
5325             ns = false;
5326             o.tag = o.ns + ':' + o.tag;
5327             
5328         }
5329         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5330         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5331         for(var attr in o){
5332             
5333             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5334                     attr == "style" || typeof o[attr] == "function") { continue; }
5335                     
5336             if(attr=="cls" && Roo.isIE){
5337                 el.className = o["cls"];
5338             }else{
5339                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5340                 else { 
5341                     el[attr] = o[attr];
5342                 }
5343             }
5344         }
5345         Roo.DomHelper.applyStyles(el, o.style);
5346         var cn = o.children || o.cn;
5347         if(cn){
5348             //http://bugs.kde.org/show_bug.cgi?id=71506
5349              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5350                 for(var i = 0, len = cn.length; i < len; i++) {
5351                     createDom(cn[i], el);
5352                 }
5353             }else{
5354                 createDom(cn, el);
5355             }
5356         }
5357         if(o.html){
5358             el.innerHTML = o.html;
5359         }
5360         if(parentNode){
5361            parentNode.appendChild(el);
5362         }
5363         return el;
5364     };
5365
5366     var ieTable = function(depth, s, h, e){
5367         tempTableEl.innerHTML = [s, h, e].join('');
5368         var i = -1, el = tempTableEl;
5369         while(++i < depth && el.firstChild){
5370             el = el.firstChild;
5371         }
5372         return el;
5373     };
5374
5375     // kill repeat to save bytes
5376     var ts = '<table>',
5377         te = '</table>',
5378         tbs = ts+'<tbody>',
5379         tbe = '</tbody>'+te,
5380         trs = tbs + '<tr>',
5381         tre = '</tr>'+tbe;
5382
5383     /**
5384      * @ignore
5385      * Nasty code for IE's broken table implementation
5386      */
5387     var insertIntoTable = function(tag, where, el, html){
5388         if(!tempTableEl){
5389             tempTableEl = document.createElement('div');
5390         }
5391         var node;
5392         var before = null;
5393         if(tag == 'td'){
5394             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5395                 return;
5396             }
5397             if(where == 'beforebegin'){
5398                 before = el;
5399                 el = el.parentNode;
5400             } else{
5401                 before = el.nextSibling;
5402                 el = el.parentNode;
5403             }
5404             node = ieTable(4, trs, html, tre);
5405         }
5406         else if(tag == 'tr'){
5407             if(where == 'beforebegin'){
5408                 before = el;
5409                 el = el.parentNode;
5410                 node = ieTable(3, tbs, html, tbe);
5411             } else if(where == 'afterend'){
5412                 before = el.nextSibling;
5413                 el = el.parentNode;
5414                 node = ieTable(3, tbs, html, tbe);
5415             } else{ // INTO a TR
5416                 if(where == 'afterbegin'){
5417                     before = el.firstChild;
5418                 }
5419                 node = ieTable(4, trs, html, tre);
5420             }
5421         } else if(tag == 'tbody'){
5422             if(where == 'beforebegin'){
5423                 before = el;
5424                 el = el.parentNode;
5425                 node = ieTable(2, ts, html, te);
5426             } else if(where == 'afterend'){
5427                 before = el.nextSibling;
5428                 el = el.parentNode;
5429                 node = ieTable(2, ts, html, te);
5430             } else{
5431                 if(where == 'afterbegin'){
5432                     before = el.firstChild;
5433                 }
5434                 node = ieTable(3, tbs, html, tbe);
5435             }
5436         } else{ // TABLE
5437             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5438                 return;
5439             }
5440             if(where == 'afterbegin'){
5441                 before = el.firstChild;
5442             }
5443             node = ieTable(2, ts, html, te);
5444         }
5445         el.insertBefore(node, before);
5446         return node;
5447     };
5448     
5449     // this is a bit like the react update code...
5450     // 
5451     
5452     var updateNode = function(from, to)
5453     {
5454         // should we handle non-standard elements?
5455         Roo.log(["UpdateNode" , from, to]);
5456         if (from.nodeType != to.nodeType) {
5457             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5458             from.parentNode.replaceChild(to, from);
5459         }
5460         
5461         if (from.nodeType == 3) {
5462             // assume it's text?!
5463             if (from.data == to.data) {
5464                 return;
5465             }
5466             from.data = to.data;
5467             return;
5468         }
5469         
5470         // assume 'to' doesnt have '1/3 nodetypes!
5471         if (from.nodeType !=1 || from.tagName != to.tagName) {
5472             Roo.log(["ReplaceChild" , from, to ]);
5473             from.parentNode.replaceChild(to, from);
5474             return;
5475         }
5476         // compare attributes
5477         var ar = Array.from(from.attributes);
5478         for(var i = 0; i< ar.length;i++) {
5479             if (to.hasAttribute(ar[i].name)) {
5480                 continue;
5481             }
5482             if (ar[i].name == 'id') { // always keep ids?
5483                continue;
5484             }
5485             //if (ar[i].name == 'style') {
5486             //   throw "style removed?";
5487             //}
5488             Roo.log("removeAttribute" + ar[i].name);
5489             from.removeAttribute(ar[i].name);
5490         }
5491         ar = to.attributes;
5492         for(var i = 0; i< ar.length;i++) {
5493             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5494                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5495                 continue;
5496             }
5497             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5498             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5499         }
5500         // children
5501         var far = Array.from(from.childNodes);
5502         var tar = Array.from(to.childNodes);
5503         // if the lengths are different.. then it's probably a editable content change, rather than
5504         // a change of the block definition..
5505         
5506         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5507          /*if (from.innerHTML == to.innerHTML) {
5508             return;
5509         }
5510         if (far.length != tar.length) {
5511             from.innerHTML = to.innerHTML;
5512             return;
5513         }
5514         */
5515         
5516         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5517             if (i >= far.length) {
5518                 from.appendChild(tar[i]);
5519                 Roo.log(["add", tar[i]]);
5520                 
5521             } else if ( i  >= tar.length) {
5522                 from.removeChild(far[i]);
5523                 Roo.log(["remove", far[i]]);
5524             } else {
5525                 
5526                 updateNode(far[i], tar[i]);
5527             }    
5528         }
5529         
5530         
5531         
5532         
5533     };
5534     
5535     
5536
5537     return {
5538         /** True to force the use of DOM instead of html fragments @type Boolean */
5539         useDom : false,
5540     
5541         /**
5542          * Returns the markup for the passed Element(s) config
5543          * @param {Object} o The Dom object spec (and children)
5544          * @return {String}
5545          */
5546         markup : function(o){
5547             return createHtml(o);
5548         },
5549     
5550         /**
5551          * Applies a style specification to an element
5552          * @param {String/HTMLElement} el The element to apply styles to
5553          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5554          * a function which returns such a specification.
5555          */
5556         applyStyles : function(el, styles){
5557             if(styles){
5558                el = Roo.fly(el);
5559                if(typeof styles == "string"){
5560                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5561                    var matches;
5562                    while ((matches = re.exec(styles)) != null){
5563                        el.setStyle(matches[1], matches[2]);
5564                    }
5565                }else if (typeof styles == "object"){
5566                    for (var style in styles){
5567                       el.setStyle(style, styles[style]);
5568                    }
5569                }else if (typeof styles == "function"){
5570                     Roo.DomHelper.applyStyles(el, styles.call());
5571                }
5572             }
5573         },
5574     
5575         /**
5576          * Inserts an HTML fragment into the Dom
5577          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5578          * @param {HTMLElement} el The context element
5579          * @param {String} html The HTML fragmenet
5580          * @return {HTMLElement} The new node
5581          */
5582         insertHtml : function(where, el, html){
5583             where = where.toLowerCase();
5584             if(el.insertAdjacentHTML){
5585                 if(tableRe.test(el.tagName)){
5586                     var rs;
5587                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5588                         return rs;
5589                     }
5590                 }
5591                 switch(where){
5592                     case "beforebegin":
5593                         el.insertAdjacentHTML('BeforeBegin', html);
5594                         return el.previousSibling;
5595                     case "afterbegin":
5596                         el.insertAdjacentHTML('AfterBegin', html);
5597                         return el.firstChild;
5598                     case "beforeend":
5599                         el.insertAdjacentHTML('BeforeEnd', html);
5600                         return el.lastChild;
5601                     case "afterend":
5602                         el.insertAdjacentHTML('AfterEnd', html);
5603                         return el.nextSibling;
5604                 }
5605                 throw 'Illegal insertion point -> "' + where + '"';
5606             }
5607             var range = el.ownerDocument.createRange();
5608             var frag;
5609             switch(where){
5610                  case "beforebegin":
5611                     range.setStartBefore(el);
5612                     frag = range.createContextualFragment(html);
5613                     el.parentNode.insertBefore(frag, el);
5614                     return el.previousSibling;
5615                  case "afterbegin":
5616                     if(el.firstChild){
5617                         range.setStartBefore(el.firstChild);
5618                         frag = range.createContextualFragment(html);
5619                         el.insertBefore(frag, el.firstChild);
5620                         return el.firstChild;
5621                     }else{
5622                         el.innerHTML = html;
5623                         return el.firstChild;
5624                     }
5625                 case "beforeend":
5626                     if(el.lastChild){
5627                         range.setStartAfter(el.lastChild);
5628                         frag = range.createContextualFragment(html);
5629                         el.appendChild(frag);
5630                         return el.lastChild;
5631                     }else{
5632                         el.innerHTML = html;
5633                         return el.lastChild;
5634                     }
5635                 case "afterend":
5636                     range.setStartAfter(el);
5637                     frag = range.createContextualFragment(html);
5638                     el.parentNode.insertBefore(frag, el.nextSibling);
5639                     return el.nextSibling;
5640                 }
5641                 throw 'Illegal insertion point -> "' + where + '"';
5642         },
5643     
5644         /**
5645          * Creates new Dom element(s) and inserts them before el
5646          * @param {String/HTMLElement/Element} el The context element
5647          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5648          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5649          * @return {HTMLElement/Roo.Element} The new node
5650          */
5651         insertBefore : function(el, o, returnElement){
5652             return this.doInsert(el, o, returnElement, "beforeBegin");
5653         },
5654     
5655         /**
5656          * Creates new Dom element(s) and inserts them after el
5657          * @param {String/HTMLElement/Element} el The context element
5658          * @param {Object} o The Dom object spec (and children)
5659          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5660          * @return {HTMLElement/Roo.Element} The new node
5661          */
5662         insertAfter : function(el, o, returnElement){
5663             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5664         },
5665     
5666         /**
5667          * Creates new Dom element(s) and inserts them as the first child of el
5668          * @param {String/HTMLElement/Element} el The context element
5669          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5670          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5671          * @return {HTMLElement/Roo.Element} The new node
5672          */
5673         insertFirst : function(el, o, returnElement){
5674             return this.doInsert(el, o, returnElement, "afterBegin");
5675         },
5676     
5677         // private
5678         doInsert : function(el, o, returnElement, pos, sibling){
5679             el = Roo.getDom(el);
5680             var newNode;
5681             if(this.useDom || o.ns){
5682                 newNode = createDom(o, null);
5683                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5684             }else{
5685                 var html = createHtml(o);
5686                 newNode = this.insertHtml(pos, el, html);
5687             }
5688             return returnElement ? Roo.get(newNode, true) : newNode;
5689         },
5690     
5691         /**
5692          * Creates new Dom element(s) and appends them to el
5693          * @param {String/HTMLElement/Element} el The context element
5694          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5695          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5696          * @return {HTMLElement/Roo.Element} The new node
5697          */
5698         append : function(el, o, returnElement){
5699             el = Roo.getDom(el);
5700             var newNode;
5701             if(this.useDom || o.ns){
5702                 newNode = createDom(o, null);
5703                 el.appendChild(newNode);
5704             }else{
5705                 var html = createHtml(o);
5706                 newNode = this.insertHtml("beforeEnd", el, html);
5707             }
5708             return returnElement ? Roo.get(newNode, true) : newNode;
5709         },
5710     
5711         /**
5712          * Creates new Dom element(s) and overwrites the contents of el with them
5713          * @param {String/HTMLElement/Element} el The context element
5714          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5715          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5716          * @return {HTMLElement/Roo.Element} The new node
5717          */
5718         overwrite : function(el, o, returnElement)
5719         {
5720             el = Roo.getDom(el);
5721             if (o.ns) {
5722               
5723                 while (el.childNodes.length) {
5724                     el.removeChild(el.firstChild);
5725                 }
5726                 createDom(o, el);
5727             } else {
5728                 el.innerHTML = createHtml(o);   
5729             }
5730             
5731             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5732         },
5733     
5734         /**
5735          * Creates a new Roo.DomHelper.Template from the Dom object spec
5736          * @param {Object} o The Dom object spec (and children)
5737          * @return {Roo.DomHelper.Template} The new template
5738          */
5739         createTemplate : function(o){
5740             var html = createHtml(o);
5741             return new Roo.Template(html);
5742         },
5743          /**
5744          * Updates the first element with the spec from the o (replacing if necessary)
5745          * This iterates through the children, and updates attributes / children etc..
5746          * @param {String/HTMLElement/Element} el The context element
5747          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5748          */
5749         
5750         update : function(el, o)
5751         {
5752             updateNode(Roo.getDom(el), createDom(o));
5753             
5754         }
5755         
5756         
5757     };
5758 }();
5759 /*
5760  * Based on:
5761  * Ext JS Library 1.1.1
5762  * Copyright(c) 2006-2007, Ext JS, LLC.
5763  *
5764  * Originally Released Under LGPL - original licence link has changed is not relivant.
5765  *
5766  * Fork - LGPL
5767  * <script type="text/javascript">
5768  */
5769  
5770 /**
5771 * @class Roo.Template
5772 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5773 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5774 * Usage:
5775 <pre><code>
5776 var t = new Roo.Template({
5777     html :  '&lt;div name="{id}"&gt;' + 
5778         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5779         '&lt;/div&gt;',
5780     myformat: function (value, allValues) {
5781         return 'XX' + value;
5782     }
5783 });
5784 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5785 </code></pre>
5786 * For more information see this blog post with examples:
5787 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5788      - Create Elements using DOM, HTML fragments and Templates</a>. 
5789 * @constructor
5790 * @param {Object} cfg - Configuration object.
5791 */
5792 Roo.Template = function(cfg){
5793     // BC!
5794     if(cfg instanceof Array){
5795         cfg = cfg.join("");
5796     }else if(arguments.length > 1){
5797         cfg = Array.prototype.join.call(arguments, "");
5798     }
5799     
5800     
5801     if (typeof(cfg) == 'object') {
5802         Roo.apply(this,cfg)
5803     } else {
5804         // bc
5805         this.html = cfg;
5806     }
5807     if (this.url) {
5808         this.load();
5809     }
5810     
5811 };
5812 Roo.Template.prototype = {
5813     
5814     /**
5815      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5816      */
5817     onLoad : false,
5818     
5819     
5820     /**
5821      * @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..
5822      *                    it should be fixed so that template is observable...
5823      */
5824     url : false,
5825     /**
5826      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5827      */
5828     html : '',
5829     
5830     
5831     compiled : false,
5832     loaded : false,
5833     /**
5834      * Returns an HTML fragment of this template with the specified values applied.
5835      * @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'})
5836      * @return {String} The HTML fragment
5837      */
5838     
5839    
5840     
5841     applyTemplate : function(values){
5842         //Roo.log(["applyTemplate", values]);
5843         try {
5844            
5845             if(this.compiled){
5846                 return this.compiled(values);
5847             }
5848             var useF = this.disableFormats !== true;
5849             var fm = Roo.util.Format, tpl = this;
5850             var fn = function(m, name, format, args){
5851                 if(format && useF){
5852                     if(format.substr(0, 5) == "this."){
5853                         return tpl.call(format.substr(5), values[name], values);
5854                     }else{
5855                         if(args){
5856                             // quoted values are required for strings in compiled templates, 
5857                             // but for non compiled we need to strip them
5858                             // quoted reversed for jsmin
5859                             var re = /^\s*['"](.*)["']\s*$/;
5860                             args = args.split(',');
5861                             for(var i = 0, len = args.length; i < len; i++){
5862                                 args[i] = args[i].replace(re, "$1");
5863                             }
5864                             args = [values[name]].concat(args);
5865                         }else{
5866                             args = [values[name]];
5867                         }
5868                         return fm[format].apply(fm, args);
5869                     }
5870                 }else{
5871                     return values[name] !== undefined ? values[name] : "";
5872                 }
5873             };
5874             return this.html.replace(this.re, fn);
5875         } catch (e) {
5876             Roo.log(e);
5877             throw e;
5878         }
5879          
5880     },
5881     
5882     loading : false,
5883       
5884     load : function ()
5885     {
5886          
5887         if (this.loading) {
5888             return;
5889         }
5890         var _t = this;
5891         
5892         this.loading = true;
5893         this.compiled = false;
5894         
5895         var cx = new Roo.data.Connection();
5896         cx.request({
5897             url : this.url,
5898             method : 'GET',
5899             success : function (response) {
5900                 _t.loading = false;
5901                 _t.url = false;
5902                 
5903                 _t.set(response.responseText,true);
5904                 _t.loaded = true;
5905                 if (_t.onLoad) {
5906                     _t.onLoad();
5907                 }
5908              },
5909             failure : function(response) {
5910                 Roo.log("Template failed to load from " + _t.url);
5911                 _t.loading = false;
5912             }
5913         });
5914     },
5915
5916     /**
5917      * Sets the HTML used as the template and optionally compiles it.
5918      * @param {String} html
5919      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5920      * @return {Roo.Template} this
5921      */
5922     set : function(html, compile){
5923         this.html = html;
5924         this.compiled = false;
5925         if(compile){
5926             this.compile();
5927         }
5928         return this;
5929     },
5930     
5931     /**
5932      * True to disable format functions (defaults to false)
5933      * @type Boolean
5934      */
5935     disableFormats : false,
5936     
5937     /**
5938     * The regular expression used to match template variables 
5939     * @type RegExp
5940     * @property 
5941     */
5942     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5943     
5944     /**
5945      * Compiles the template into an internal function, eliminating the RegEx overhead.
5946      * @return {Roo.Template} this
5947      */
5948     compile : function(){
5949         var fm = Roo.util.Format;
5950         var useF = this.disableFormats !== true;
5951         var sep = Roo.isGecko ? "+" : ",";
5952         var fn = function(m, name, format, args){
5953             if(format && useF){
5954                 args = args ? ',' + args : "";
5955                 if(format.substr(0, 5) != "this."){
5956                     format = "fm." + format + '(';
5957                 }else{
5958                     format = 'this.call("'+ format.substr(5) + '", ';
5959                     args = ", values";
5960                 }
5961             }else{
5962                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5963             }
5964             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5965         };
5966         var body;
5967         // branched to use + in gecko and [].join() in others
5968         if(Roo.isGecko){
5969             body = "this.compiled = function(values){ return '" +
5970                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5971                     "';};";
5972         }else{
5973             body = ["this.compiled = function(values){ return ['"];
5974             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5975             body.push("'].join('');};");
5976             body = body.join('');
5977         }
5978         /**
5979          * eval:var:values
5980          * eval:var:fm
5981          */
5982         eval(body);
5983         return this;
5984     },
5985     
5986     // private function used to call members
5987     call : function(fnName, value, allValues){
5988         return this[fnName](value, allValues);
5989     },
5990     
5991     /**
5992      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5993      * @param {String/HTMLElement/Roo.Element} el The context element
5994      * @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'})
5995      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5996      * @return {HTMLElement/Roo.Element} The new node or Element
5997      */
5998     insertFirst: function(el, values, returnElement){
5999         return this.doInsert('afterBegin', el, values, returnElement);
6000     },
6001
6002     /**
6003      * Applies the supplied values to the template and inserts the new node(s) before el.
6004      * @param {String/HTMLElement/Roo.Element} el The context element
6005      * @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'})
6006      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6007      * @return {HTMLElement/Roo.Element} The new node or Element
6008      */
6009     insertBefore: function(el, values, returnElement){
6010         return this.doInsert('beforeBegin', el, values, returnElement);
6011     },
6012
6013     /**
6014      * Applies the supplied values to the template and inserts the new node(s) after el.
6015      * @param {String/HTMLElement/Roo.Element} el The context element
6016      * @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'})
6017      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6018      * @return {HTMLElement/Roo.Element} The new node or Element
6019      */
6020     insertAfter : function(el, values, returnElement){
6021         return this.doInsert('afterEnd', el, values, returnElement);
6022     },
6023     
6024     /**
6025      * Applies the supplied values to the template and appends the new node(s) to el.
6026      * @param {String/HTMLElement/Roo.Element} el The context element
6027      * @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'})
6028      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6029      * @return {HTMLElement/Roo.Element} The new node or Element
6030      */
6031     append : function(el, values, returnElement){
6032         return this.doInsert('beforeEnd', el, values, returnElement);
6033     },
6034
6035     doInsert : function(where, el, values, returnEl){
6036         el = Roo.getDom(el);
6037         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6038         return returnEl ? Roo.get(newNode, true) : newNode;
6039     },
6040
6041     /**
6042      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6043      * @param {String/HTMLElement/Roo.Element} el The context element
6044      * @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'})
6045      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6046      * @return {HTMLElement/Roo.Element} The new node or Element
6047      */
6048     overwrite : function(el, values, returnElement){
6049         el = Roo.getDom(el);
6050         el.innerHTML = this.applyTemplate(values);
6051         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6052     }
6053 };
6054 /**
6055  * Alias for {@link #applyTemplate}
6056  * @method
6057  */
6058 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6059
6060 // backwards compat
6061 Roo.DomHelper.Template = Roo.Template;
6062
6063 /**
6064  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6065  * @param {String/HTMLElement} el A DOM element or its id
6066  * @returns {Roo.Template} The created template
6067  * @static
6068  */
6069 Roo.Template.from = function(el){
6070     el = Roo.getDom(el);
6071     return new Roo.Template(el.value || el.innerHTML);
6072 };/*
6073  * Based on:
6074  * Ext JS Library 1.1.1
6075  * Copyright(c) 2006-2007, Ext JS, LLC.
6076  *
6077  * Originally Released Under LGPL - original licence link has changed is not relivant.
6078  *
6079  * Fork - LGPL
6080  * <script type="text/javascript">
6081  */
6082  
6083
6084 /*
6085  * This is code is also distributed under MIT license for use
6086  * with jQuery and prototype JavaScript libraries.
6087  */
6088 /**
6089  * @class Roo.DomQuery
6090 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).
6091 <p>
6092 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>
6093
6094 <p>
6095 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.
6096 </p>
6097 <h4>Element Selectors:</h4>
6098 <ul class="list">
6099     <li> <b>*</b> any element</li>
6100     <li> <b>E</b> an element with the tag E</li>
6101     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6102     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6103     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6104     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6105 </ul>
6106 <h4>Attribute Selectors:</h4>
6107 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6108 <ul class="list">
6109     <li> <b>E[foo]</b> has an attribute "foo"</li>
6110     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6111     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6112     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6113     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6114     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6115     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6116 </ul>
6117 <h4>Pseudo Classes:</h4>
6118 <ul class="list">
6119     <li> <b>E:first-child</b> E is the first child of its parent</li>
6120     <li> <b>E:last-child</b> E is the last child of its parent</li>
6121     <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>
6122     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6123     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6124     <li> <b>E:only-child</b> E is the only child of its parent</li>
6125     <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>
6126     <li> <b>E:first</b> the first E in the resultset</li>
6127     <li> <b>E:last</b> the last E in the resultset</li>
6128     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6129     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6130     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6131     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6132     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6133     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6134     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6135     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6136     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6137 </ul>
6138 <h4>CSS Value Selectors:</h4>
6139 <ul class="list">
6140     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6141     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6142     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6143     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6144     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6145     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6146 </ul>
6147  * @static
6148  */
6149 Roo.DomQuery = function(){
6150     var cache = {}, simpleCache = {}, valueCache = {};
6151     var nonSpace = /\S/;
6152     var trimRe = /^\s+|\s+$/g;
6153     var tplRe = /\{(\d+)\}/g;
6154     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6155     var tagTokenRe = /^(#)?([\w-\*]+)/;
6156     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6157
6158     function child(p, index){
6159         var i = 0;
6160         var n = p.firstChild;
6161         while(n){
6162             if(n.nodeType == 1){
6163                if(++i == index){
6164                    return n;
6165                }
6166             }
6167             n = n.nextSibling;
6168         }
6169         return null;
6170     };
6171
6172     function next(n){
6173         while((n = n.nextSibling) && n.nodeType != 1);
6174         return n;
6175     };
6176
6177     function prev(n){
6178         while((n = n.previousSibling) && n.nodeType != 1);
6179         return n;
6180     };
6181
6182     function children(d){
6183         var n = d.firstChild, ni = -1;
6184             while(n){
6185                 var nx = n.nextSibling;
6186                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6187                     d.removeChild(n);
6188                 }else{
6189                     n.nodeIndex = ++ni;
6190                 }
6191                 n = nx;
6192             }
6193             return this;
6194         };
6195
6196     function byClassName(c, a, v){
6197         if(!v){
6198             return c;
6199         }
6200         var r = [], ri = -1, cn;
6201         for(var i = 0, ci; ci = c[i]; i++){
6202             
6203             
6204             if((' '+
6205                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6206                  +' ').indexOf(v) != -1){
6207                 r[++ri] = ci;
6208             }
6209         }
6210         return r;
6211     };
6212
6213     function attrValue(n, attr){
6214         if(!n.tagName && typeof n.length != "undefined"){
6215             n = n[0];
6216         }
6217         if(!n){
6218             return null;
6219         }
6220         if(attr == "for"){
6221             return n.htmlFor;
6222         }
6223         if(attr == "class" || attr == "className"){
6224             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6225         }
6226         return n.getAttribute(attr) || n[attr];
6227
6228     };
6229
6230     function getNodes(ns, mode, tagName){
6231         var result = [], ri = -1, cs;
6232         if(!ns){
6233             return result;
6234         }
6235         tagName = tagName || "*";
6236         if(typeof ns.getElementsByTagName != "undefined"){
6237             ns = [ns];
6238         }
6239         if(!mode){
6240             for(var i = 0, ni; ni = ns[i]; i++){
6241                 cs = ni.getElementsByTagName(tagName);
6242                 for(var j = 0, ci; ci = cs[j]; j++){
6243                     result[++ri] = ci;
6244                 }
6245             }
6246         }else if(mode == "/" || mode == ">"){
6247             var utag = tagName.toUpperCase();
6248             for(var i = 0, ni, cn; ni = ns[i]; i++){
6249                 cn = ni.children || ni.childNodes;
6250                 for(var j = 0, cj; cj = cn[j]; j++){
6251                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6252                         result[++ri] = cj;
6253                     }
6254                 }
6255             }
6256         }else if(mode == "+"){
6257             var utag = tagName.toUpperCase();
6258             for(var i = 0, n; n = ns[i]; i++){
6259                 while((n = n.nextSibling) && n.nodeType != 1);
6260                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6261                     result[++ri] = n;
6262                 }
6263             }
6264         }else if(mode == "~"){
6265             for(var i = 0, n; n = ns[i]; i++){
6266                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6267                 if(n){
6268                     result[++ri] = n;
6269                 }
6270             }
6271         }
6272         return result;
6273     };
6274
6275     function concat(a, b){
6276         if(b.slice){
6277             return a.concat(b);
6278         }
6279         for(var i = 0, l = b.length; i < l; i++){
6280             a[a.length] = b[i];
6281         }
6282         return a;
6283     }
6284
6285     function byTag(cs, tagName){
6286         if(cs.tagName || cs == document){
6287             cs = [cs];
6288         }
6289         if(!tagName){
6290             return cs;
6291         }
6292         var r = [], ri = -1;
6293         tagName = tagName.toLowerCase();
6294         for(var i = 0, ci; ci = cs[i]; i++){
6295             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6296                 r[++ri] = ci;
6297             }
6298         }
6299         return r;
6300     };
6301
6302     function byId(cs, attr, id){
6303         if(cs.tagName || cs == document){
6304             cs = [cs];
6305         }
6306         if(!id){
6307             return cs;
6308         }
6309         var r = [], ri = -1;
6310         for(var i = 0,ci; ci = cs[i]; i++){
6311             if(ci && ci.id == id){
6312                 r[++ri] = ci;
6313                 return r;
6314             }
6315         }
6316         return r;
6317     };
6318
6319     function byAttribute(cs, attr, value, op, custom){
6320         var r = [], ri = -1, st = custom=="{";
6321         var f = Roo.DomQuery.operators[op];
6322         for(var i = 0, ci; ci = cs[i]; i++){
6323             var a;
6324             if(st){
6325                 a = Roo.DomQuery.getStyle(ci, attr);
6326             }
6327             else if(attr == "class" || attr == "className"){
6328                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6329             }else if(attr == "for"){
6330                 a = ci.htmlFor;
6331             }else if(attr == "href"){
6332                 a = ci.getAttribute("href", 2);
6333             }else{
6334                 a = ci.getAttribute(attr);
6335             }
6336             if((f && f(a, value)) || (!f && a)){
6337                 r[++ri] = ci;
6338             }
6339         }
6340         return r;
6341     };
6342
6343     function byPseudo(cs, name, value){
6344         return Roo.DomQuery.pseudos[name](cs, value);
6345     };
6346
6347     // This is for IE MSXML which does not support expandos.
6348     // IE runs the same speed using setAttribute, however FF slows way down
6349     // and Safari completely fails so they need to continue to use expandos.
6350     var isIE = window.ActiveXObject ? true : false;
6351
6352     // this eval is stop the compressor from
6353     // renaming the variable to something shorter
6354     
6355     /** eval:var:batch */
6356     var batch = 30803; 
6357
6358     var key = 30803;
6359
6360     function nodupIEXml(cs){
6361         var d = ++key;
6362         cs[0].setAttribute("_nodup", d);
6363         var r = [cs[0]];
6364         for(var i = 1, len = cs.length; i < len; i++){
6365             var c = cs[i];
6366             if(!c.getAttribute("_nodup") != d){
6367                 c.setAttribute("_nodup", d);
6368                 r[r.length] = c;
6369             }
6370         }
6371         for(var i = 0, len = cs.length; i < len; i++){
6372             cs[i].removeAttribute("_nodup");
6373         }
6374         return r;
6375     }
6376
6377     function nodup(cs){
6378         if(!cs){
6379             return [];
6380         }
6381         var len = cs.length, c, i, r = cs, cj, ri = -1;
6382         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6383             return cs;
6384         }
6385         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6386             return nodupIEXml(cs);
6387         }
6388         var d = ++key;
6389         cs[0]._nodup = d;
6390         for(i = 1; c = cs[i]; i++){
6391             if(c._nodup != d){
6392                 c._nodup = d;
6393             }else{
6394                 r = [];
6395                 for(var j = 0; j < i; j++){
6396                     r[++ri] = cs[j];
6397                 }
6398                 for(j = i+1; cj = cs[j]; j++){
6399                     if(cj._nodup != d){
6400                         cj._nodup = d;
6401                         r[++ri] = cj;
6402                     }
6403                 }
6404                 return r;
6405             }
6406         }
6407         return r;
6408     }
6409
6410     function quickDiffIEXml(c1, c2){
6411         var d = ++key;
6412         for(var i = 0, len = c1.length; i < len; i++){
6413             c1[i].setAttribute("_qdiff", d);
6414         }
6415         var r = [];
6416         for(var i = 0, len = c2.length; i < len; i++){
6417             if(c2[i].getAttribute("_qdiff") != d){
6418                 r[r.length] = c2[i];
6419             }
6420         }
6421         for(var i = 0, len = c1.length; i < len; i++){
6422            c1[i].removeAttribute("_qdiff");
6423         }
6424         return r;
6425     }
6426
6427     function quickDiff(c1, c2){
6428         var len1 = c1.length;
6429         if(!len1){
6430             return c2;
6431         }
6432         if(isIE && c1[0].selectSingleNode){
6433             return quickDiffIEXml(c1, c2);
6434         }
6435         var d = ++key;
6436         for(var i = 0; i < len1; i++){
6437             c1[i]._qdiff = d;
6438         }
6439         var r = [];
6440         for(var i = 0, len = c2.length; i < len; i++){
6441             if(c2[i]._qdiff != d){
6442                 r[r.length] = c2[i];
6443             }
6444         }
6445         return r;
6446     }
6447
6448     function quickId(ns, mode, root, id){
6449         if(ns == root){
6450            var d = root.ownerDocument || root;
6451            return d.getElementById(id);
6452         }
6453         ns = getNodes(ns, mode, "*");
6454         return byId(ns, null, id);
6455     }
6456
6457     return {
6458         getStyle : function(el, name){
6459             return Roo.fly(el).getStyle(name);
6460         },
6461         /**
6462          * Compiles a selector/xpath query into a reusable function. The returned function
6463          * takes one parameter "root" (optional), which is the context node from where the query should start.
6464          * @param {String} selector The selector/xpath query
6465          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6466          * @return {Function}
6467          */
6468         compile : function(path, type){
6469             type = type || "select";
6470             
6471             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6472             var q = path, mode, lq;
6473             var tk = Roo.DomQuery.matchers;
6474             var tklen = tk.length;
6475             var mm;
6476
6477             // accept leading mode switch
6478             var lmode = q.match(modeRe);
6479             if(lmode && lmode[1]){
6480                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6481                 q = q.replace(lmode[1], "");
6482             }
6483             // strip leading slashes
6484             while(path.substr(0, 1)=="/"){
6485                 path = path.substr(1);
6486             }
6487
6488             while(q && lq != q){
6489                 lq = q;
6490                 var tm = q.match(tagTokenRe);
6491                 if(type == "select"){
6492                     if(tm){
6493                         if(tm[1] == "#"){
6494                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6495                         }else{
6496                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6497                         }
6498                         q = q.replace(tm[0], "");
6499                     }else if(q.substr(0, 1) != '@'){
6500                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6501                     }
6502                 }else{
6503                     if(tm){
6504                         if(tm[1] == "#"){
6505                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6506                         }else{
6507                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6508                         }
6509                         q = q.replace(tm[0], "");
6510                     }
6511                 }
6512                 while(!(mm = q.match(modeRe))){
6513                     var matched = false;
6514                     for(var j = 0; j < tklen; j++){
6515                         var t = tk[j];
6516                         var m = q.match(t.re);
6517                         if(m){
6518                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6519                                                     return m[i];
6520                                                 });
6521                             q = q.replace(m[0], "");
6522                             matched = true;
6523                             break;
6524                         }
6525                     }
6526                     // prevent infinite loop on bad selector
6527                     if(!matched){
6528                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6529                     }
6530                 }
6531                 if(mm[1]){
6532                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6533                     q = q.replace(mm[1], "");
6534                 }
6535             }
6536             fn[fn.length] = "return nodup(n);\n}";
6537             
6538              /** 
6539               * list of variables that need from compression as they are used by eval.
6540              *  eval:var:batch 
6541              *  eval:var:nodup
6542              *  eval:var:byTag
6543              *  eval:var:ById
6544              *  eval:var:getNodes
6545              *  eval:var:quickId
6546              *  eval:var:mode
6547              *  eval:var:root
6548              *  eval:var:n
6549              *  eval:var:byClassName
6550              *  eval:var:byPseudo
6551              *  eval:var:byAttribute
6552              *  eval:var:attrValue
6553              * 
6554              **/ 
6555             eval(fn.join(""));
6556             return f;
6557         },
6558
6559         /**
6560          * Selects a group of elements.
6561          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6562          * @param {Node} root (optional) The start of the query (defaults to document).
6563          * @return {Array}
6564          */
6565         select : function(path, root, type){
6566             if(!root || root == document){
6567                 root = document;
6568             }
6569             if(typeof root == "string"){
6570                 root = document.getElementById(root);
6571             }
6572             var paths = path.split(",");
6573             var results = [];
6574             for(var i = 0, len = paths.length; i < len; i++){
6575                 var p = paths[i].replace(trimRe, "");
6576                 if(!cache[p]){
6577                     cache[p] = Roo.DomQuery.compile(p);
6578                     if(!cache[p]){
6579                         throw p + " is not a valid selector";
6580                     }
6581                 }
6582                 var result = cache[p](root);
6583                 if(result && result != document){
6584                     results = results.concat(result);
6585                 }
6586             }
6587             if(paths.length > 1){
6588                 return nodup(results);
6589             }
6590             return results;
6591         },
6592
6593         /**
6594          * Selects a single element.
6595          * @param {String} selector The selector/xpath query
6596          * @param {Node} root (optional) The start of the query (defaults to document).
6597          * @return {Element}
6598          */
6599         selectNode : function(path, root){
6600             return Roo.DomQuery.select(path, root)[0];
6601         },
6602
6603         /**
6604          * Selects the value of a node, optionally replacing null with the defaultValue.
6605          * @param {String} selector The selector/xpath query
6606          * @param {Node} root (optional) The start of the query (defaults to document).
6607          * @param {String} defaultValue
6608          */
6609         selectValue : function(path, root, defaultValue){
6610             path = path.replace(trimRe, "");
6611             if(!valueCache[path]){
6612                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6613             }
6614             var n = valueCache[path](root);
6615             n = n[0] ? n[0] : n;
6616             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6617             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6618         },
6619
6620         /**
6621          * Selects the value of a node, parsing integers and floats.
6622          * @param {String} selector The selector/xpath query
6623          * @param {Node} root (optional) The start of the query (defaults to document).
6624          * @param {Number} defaultValue
6625          * @return {Number}
6626          */
6627         selectNumber : function(path, root, defaultValue){
6628             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6629             return parseFloat(v);
6630         },
6631
6632         /**
6633          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6634          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6635          * @param {String} selector The simple selector to test
6636          * @return {Boolean}
6637          */
6638         is : function(el, ss){
6639             if(typeof el == "string"){
6640                 el = document.getElementById(el);
6641             }
6642             var isArray = (el instanceof Array);
6643             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6644             return isArray ? (result.length == el.length) : (result.length > 0);
6645         },
6646
6647         /**
6648          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6649          * @param {Array} el An array of elements to filter
6650          * @param {String} selector The simple selector to test
6651          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6652          * the selector instead of the ones that match
6653          * @return {Array}
6654          */
6655         filter : function(els, ss, nonMatches){
6656             ss = ss.replace(trimRe, "");
6657             if(!simpleCache[ss]){
6658                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6659             }
6660             var result = simpleCache[ss](els);
6661             return nonMatches ? quickDiff(result, els) : result;
6662         },
6663
6664         /**
6665          * Collection of matching regular expressions and code snippets.
6666          */
6667         matchers : [{
6668                 re: /^\.([\w-]+)/,
6669                 select: 'n = byClassName(n, null, " {1} ");'
6670             }, {
6671                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6672                 select: 'n = byPseudo(n, "{1}", "{2}");'
6673             },{
6674                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6675                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6676             }, {
6677                 re: /^#([\w-]+)/,
6678                 select: 'n = byId(n, null, "{1}");'
6679             },{
6680                 re: /^@([\w-]+)/,
6681                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6682             }
6683         ],
6684
6685         /**
6686          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6687          * 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;.
6688          */
6689         operators : {
6690             "=" : function(a, v){
6691                 return a == v;
6692             },
6693             "!=" : function(a, v){
6694                 return a != v;
6695             },
6696             "^=" : function(a, v){
6697                 return a && a.substr(0, v.length) == v;
6698             },
6699             "$=" : function(a, v){
6700                 return a && a.substr(a.length-v.length) == v;
6701             },
6702             "*=" : function(a, v){
6703                 return a && a.indexOf(v) !== -1;
6704             },
6705             "%=" : function(a, v){
6706                 return (a % v) == 0;
6707             },
6708             "|=" : function(a, v){
6709                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6710             },
6711             "~=" : function(a, v){
6712                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6713             }
6714         },
6715
6716         /**
6717          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6718          * and the argument (if any) supplied in the selector.
6719          */
6720         pseudos : {
6721             "first-child" : function(c){
6722                 var r = [], ri = -1, n;
6723                 for(var i = 0, ci; ci = n = c[i]; i++){
6724                     while((n = n.previousSibling) && n.nodeType != 1);
6725                     if(!n){
6726                         r[++ri] = ci;
6727                     }
6728                 }
6729                 return r;
6730             },
6731
6732             "last-child" : function(c){
6733                 var r = [], ri = -1, n;
6734                 for(var i = 0, ci; ci = n = c[i]; i++){
6735                     while((n = n.nextSibling) && n.nodeType != 1);
6736                     if(!n){
6737                         r[++ri] = ci;
6738                     }
6739                 }
6740                 return r;
6741             },
6742
6743             "nth-child" : function(c, a) {
6744                 var r = [], ri = -1;
6745                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6746                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6747                 for(var i = 0, n; n = c[i]; i++){
6748                     var pn = n.parentNode;
6749                     if (batch != pn._batch) {
6750                         var j = 0;
6751                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6752                             if(cn.nodeType == 1){
6753                                cn.nodeIndex = ++j;
6754                             }
6755                         }
6756                         pn._batch = batch;
6757                     }
6758                     if (f == 1) {
6759                         if (l == 0 || n.nodeIndex == l){
6760                             r[++ri] = n;
6761                         }
6762                     } else if ((n.nodeIndex + l) % f == 0){
6763                         r[++ri] = n;
6764                     }
6765                 }
6766
6767                 return r;
6768             },
6769
6770             "only-child" : function(c){
6771                 var r = [], ri = -1;;
6772                 for(var i = 0, ci; ci = c[i]; i++){
6773                     if(!prev(ci) && !next(ci)){
6774                         r[++ri] = ci;
6775                     }
6776                 }
6777                 return r;
6778             },
6779
6780             "empty" : function(c){
6781                 var r = [], ri = -1;
6782                 for(var i = 0, ci; ci = c[i]; i++){
6783                     var cns = ci.childNodes, j = 0, cn, empty = true;
6784                     while(cn = cns[j]){
6785                         ++j;
6786                         if(cn.nodeType == 1 || cn.nodeType == 3){
6787                             empty = false;
6788                             break;
6789                         }
6790                     }
6791                     if(empty){
6792                         r[++ri] = ci;
6793                     }
6794                 }
6795                 return r;
6796             },
6797
6798             "contains" : function(c, v){
6799                 var r = [], ri = -1;
6800                 for(var i = 0, ci; ci = c[i]; i++){
6801                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6802                         r[++ri] = ci;
6803                     }
6804                 }
6805                 return r;
6806             },
6807
6808             "nodeValue" : function(c, v){
6809                 var r = [], ri = -1;
6810                 for(var i = 0, ci; ci = c[i]; i++){
6811                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6812                         r[++ri] = ci;
6813                     }
6814                 }
6815                 return r;
6816             },
6817
6818             "checked" : function(c){
6819                 var r = [], ri = -1;
6820                 for(var i = 0, ci; ci = c[i]; i++){
6821                     if(ci.checked == true){
6822                         r[++ri] = ci;
6823                     }
6824                 }
6825                 return r;
6826             },
6827
6828             "not" : function(c, ss){
6829                 return Roo.DomQuery.filter(c, ss, true);
6830             },
6831
6832             "odd" : function(c){
6833                 return this["nth-child"](c, "odd");
6834             },
6835
6836             "even" : function(c){
6837                 return this["nth-child"](c, "even");
6838             },
6839
6840             "nth" : function(c, a){
6841                 return c[a-1] || [];
6842             },
6843
6844             "first" : function(c){
6845                 return c[0] || [];
6846             },
6847
6848             "last" : function(c){
6849                 return c[c.length-1] || [];
6850             },
6851
6852             "has" : function(c, ss){
6853                 var s = Roo.DomQuery.select;
6854                 var r = [], ri = -1;
6855                 for(var i = 0, ci; ci = c[i]; i++){
6856                     if(s(ss, ci).length > 0){
6857                         r[++ri] = ci;
6858                     }
6859                 }
6860                 return r;
6861             },
6862
6863             "next" : function(c, ss){
6864                 var is = Roo.DomQuery.is;
6865                 var r = [], ri = -1;
6866                 for(var i = 0, ci; ci = c[i]; i++){
6867                     var n = next(ci);
6868                     if(n && is(n, ss)){
6869                         r[++ri] = ci;
6870                     }
6871                 }
6872                 return r;
6873             },
6874
6875             "prev" : function(c, ss){
6876                 var is = Roo.DomQuery.is;
6877                 var r = [], ri = -1;
6878                 for(var i = 0, ci; ci = c[i]; i++){
6879                     var n = prev(ci);
6880                     if(n && is(n, ss)){
6881                         r[++ri] = ci;
6882                     }
6883                 }
6884                 return r;
6885             }
6886         }
6887     };
6888 }();
6889
6890 /**
6891  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6892  * @param {String} path The selector/xpath query
6893  * @param {Node} root (optional) The start of the query (defaults to document).
6894  * @return {Array}
6895  * @member Roo
6896  * @method query
6897  */
6898 Roo.query = Roo.DomQuery.select;
6899 /*
6900  * Based on:
6901  * Ext JS Library 1.1.1
6902  * Copyright(c) 2006-2007, Ext JS, LLC.
6903  *
6904  * Originally Released Under LGPL - original licence link has changed is not relivant.
6905  *
6906  * Fork - LGPL
6907  * <script type="text/javascript">
6908  */
6909
6910 /**
6911  * @class Roo.util.Observable
6912  * Base class that provides a common interface for publishing events. Subclasses are expected to
6913  * to have a property "events" with all the events defined.<br>
6914  * For example:
6915  * <pre><code>
6916  Employee = function(name){
6917     this.name = name;
6918     this.addEvents({
6919         "fired" : true,
6920         "quit" : true
6921     });
6922  }
6923  Roo.extend(Employee, Roo.util.Observable);
6924 </code></pre>
6925  * @param {Object} config properties to use (incuding events / listeners)
6926  */
6927
6928 Roo.util.Observable = function(cfg){
6929     
6930     cfg = cfg|| {};
6931     this.addEvents(cfg.events || {});
6932     if (cfg.events) {
6933         delete cfg.events; // make sure
6934     }
6935      
6936     Roo.apply(this, cfg);
6937     
6938     if(this.listeners){
6939         this.on(this.listeners);
6940         delete this.listeners;
6941     }
6942 };
6943 Roo.util.Observable.prototype = {
6944     /** 
6945  * @cfg {Object} listeners  list of events and functions to call for this object, 
6946  * For example :
6947  * <pre><code>
6948     listeners :  { 
6949        'click' : function(e) {
6950            ..... 
6951         } ,
6952         .... 
6953     } 
6954   </code></pre>
6955  */
6956     
6957     
6958     /**
6959      * Fires the specified event with the passed parameters (minus the event name).
6960      * @param {String} eventName
6961      * @param {Object...} args Variable number of parameters are passed to handlers
6962      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6963      */
6964     fireEvent : function(){
6965         var ce = this.events[arguments[0].toLowerCase()];
6966         if(typeof ce == "object"){
6967             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6968         }else{
6969             return true;
6970         }
6971     },
6972
6973     // private
6974     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6975
6976     /**
6977      * Appends an event handler to this component
6978      * @param {String}   eventName The type of event to listen for
6979      * @param {Function} handler The method the event invokes
6980      * @param {Object}   scope (optional) The scope in which to execute the handler
6981      * function. The handler function's "this" context.
6982      * @param {Object}   options (optional) An object containing handler configuration
6983      * properties. This may contain any of the following properties:<ul>
6984      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6985      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6986      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6987      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6988      * by the specified number of milliseconds. If the event fires again within that time, the original
6989      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6990      * </ul><br>
6991      * <p>
6992      * <b>Combining Options</b><br>
6993      * Using the options argument, it is possible to combine different types of listeners:<br>
6994      * <br>
6995      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6996                 <pre><code>
6997                 el.on('click', this.onClick, this, {
6998                         single: true,
6999                 delay: 100,
7000                 forumId: 4
7001                 });
7002                 </code></pre>
7003      * <p>
7004      * <b>Attaching multiple handlers in 1 call</b><br>
7005      * The method also allows for a single argument to be passed which is a config object containing properties
7006      * which specify multiple handlers.
7007      * <pre><code>
7008                 el.on({
7009                         'click': {
7010                         fn: this.onClick,
7011                         scope: this,
7012                         delay: 100
7013                 }, 
7014                 'mouseover': {
7015                         fn: this.onMouseOver,
7016                         scope: this
7017                 },
7018                 'mouseout': {
7019                         fn: this.onMouseOut,
7020                         scope: this
7021                 }
7022                 });
7023                 </code></pre>
7024      * <p>
7025      * Or a shorthand syntax which passes the same scope object to all handlers:
7026         <pre><code>
7027                 el.on({
7028                         'click': this.onClick,
7029                 'mouseover': this.onMouseOver,
7030                 'mouseout': this.onMouseOut,
7031                 scope: this
7032                 });
7033                 </code></pre>
7034      */
7035     addListener : function(eventName, fn, scope, o){
7036         if(typeof eventName == "object"){
7037             o = eventName;
7038             for(var e in o){
7039                 if(this.filterOptRe.test(e)){
7040                     continue;
7041                 }
7042                 if(typeof o[e] == "function"){
7043                     // shared options
7044                     this.addListener(e, o[e], o.scope,  o);
7045                 }else{
7046                     // individual options
7047                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7048                 }
7049             }
7050             return;
7051         }
7052         o = (!o || typeof o == "boolean") ? {} : o;
7053         eventName = eventName.toLowerCase();
7054         var ce = this.events[eventName] || true;
7055         if(typeof ce == "boolean"){
7056             ce = new Roo.util.Event(this, eventName);
7057             this.events[eventName] = ce;
7058         }
7059         ce.addListener(fn, scope, o);
7060     },
7061
7062     /**
7063      * Removes a listener
7064      * @param {String}   eventName     The type of event to listen for
7065      * @param {Function} handler        The handler to remove
7066      * @param {Object}   scope  (optional) The scope (this object) for the handler
7067      */
7068     removeListener : function(eventName, fn, scope){
7069         var ce = this.events[eventName.toLowerCase()];
7070         if(typeof ce == "object"){
7071             ce.removeListener(fn, scope);
7072         }
7073     },
7074
7075     /**
7076      * Removes all listeners for this object
7077      */
7078     purgeListeners : function(){
7079         for(var evt in this.events){
7080             if(typeof this.events[evt] == "object"){
7081                  this.events[evt].clearListeners();
7082             }
7083         }
7084     },
7085
7086     relayEvents : function(o, events){
7087         var createHandler = function(ename){
7088             return function(){
7089                  
7090                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7091             };
7092         };
7093         for(var i = 0, len = events.length; i < len; i++){
7094             var ename = events[i];
7095             if(!this.events[ename]){
7096                 this.events[ename] = true;
7097             };
7098             o.on(ename, createHandler(ename), this);
7099         }
7100     },
7101
7102     /**
7103      * Used to define events on this Observable
7104      * @param {Object} object The object with the events defined
7105      */
7106     addEvents : function(o){
7107         if(!this.events){
7108             this.events = {};
7109         }
7110         Roo.applyIf(this.events, o);
7111     },
7112
7113     /**
7114      * Checks to see if this object has any listeners for a specified event
7115      * @param {String} eventName The name of the event to check for
7116      * @return {Boolean} True if the event is being listened for, else false
7117      */
7118     hasListener : function(eventName){
7119         var e = this.events[eventName];
7120         return typeof e == "object" && e.listeners.length > 0;
7121     }
7122 };
7123 /**
7124  * Appends an event handler to this element (shorthand for addListener)
7125  * @param {String}   eventName     The type of event to listen for
7126  * @param {Function} handler        The method the event invokes
7127  * @param {Object}   scope (optional) The scope in which to execute the handler
7128  * function. The handler function's "this" context.
7129  * @param {Object}   options  (optional)
7130  * @method
7131  */
7132 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7133 /**
7134  * Removes a listener (shorthand for removeListener)
7135  * @param {String}   eventName     The type of event to listen for
7136  * @param {Function} handler        The handler to remove
7137  * @param {Object}   scope  (optional) The scope (this object) for the handler
7138  * @method
7139  */
7140 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7141
7142 /**
7143  * Starts capture on the specified Observable. All events will be passed
7144  * to the supplied function with the event name + standard signature of the event
7145  * <b>before</b> the event is fired. If the supplied function returns false,
7146  * the event will not fire.
7147  * @param {Observable} o The Observable to capture
7148  * @param {Function} fn The function to call
7149  * @param {Object} scope (optional) The scope (this object) for the fn
7150  * @static
7151  */
7152 Roo.util.Observable.capture = function(o, fn, scope){
7153     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7154 };
7155
7156 /**
7157  * Removes <b>all</b> added captures from the Observable.
7158  * @param {Observable} o The Observable to release
7159  * @static
7160  */
7161 Roo.util.Observable.releaseCapture = function(o){
7162     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7163 };
7164
7165 (function(){
7166
7167     var createBuffered = function(h, o, scope){
7168         var task = new Roo.util.DelayedTask();
7169         return function(){
7170             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7171         };
7172     };
7173
7174     var createSingle = function(h, e, fn, scope){
7175         return function(){
7176             e.removeListener(fn, scope);
7177             return h.apply(scope, arguments);
7178         };
7179     };
7180
7181     var createDelayed = function(h, o, scope){
7182         return function(){
7183             var args = Array.prototype.slice.call(arguments, 0);
7184             setTimeout(function(){
7185                 h.apply(scope, args);
7186             }, o.delay || 10);
7187         };
7188     };
7189
7190     Roo.util.Event = function(obj, name){
7191         this.name = name;
7192         this.obj = obj;
7193         this.listeners = [];
7194     };
7195
7196     Roo.util.Event.prototype = {
7197         addListener : function(fn, scope, options){
7198             var o = options || {};
7199             scope = scope || this.obj;
7200             if(!this.isListening(fn, scope)){
7201                 var l = {fn: fn, scope: scope, options: o};
7202                 var h = fn;
7203                 if(o.delay){
7204                     h = createDelayed(h, o, scope);
7205                 }
7206                 if(o.single){
7207                     h = createSingle(h, this, fn, scope);
7208                 }
7209                 if(o.buffer){
7210                     h = createBuffered(h, o, scope);
7211                 }
7212                 l.fireFn = h;
7213                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7214                     this.listeners.push(l);
7215                 }else{
7216                     this.listeners = this.listeners.slice(0);
7217                     this.listeners.push(l);
7218                 }
7219             }
7220         },
7221
7222         findListener : function(fn, scope){
7223             scope = scope || this.obj;
7224             var ls = this.listeners;
7225             for(var i = 0, len = ls.length; i < len; i++){
7226                 var l = ls[i];
7227                 if(l.fn == fn && l.scope == scope){
7228                     return i;
7229                 }
7230             }
7231             return -1;
7232         },
7233
7234         isListening : function(fn, scope){
7235             return this.findListener(fn, scope) != -1;
7236         },
7237
7238         removeListener : function(fn, scope){
7239             var index;
7240             if((index = this.findListener(fn, scope)) != -1){
7241                 if(!this.firing){
7242                     this.listeners.splice(index, 1);
7243                 }else{
7244                     this.listeners = this.listeners.slice(0);
7245                     this.listeners.splice(index, 1);
7246                 }
7247                 return true;
7248             }
7249             return false;
7250         },
7251
7252         clearListeners : function(){
7253             this.listeners = [];
7254         },
7255
7256         fire : function(){
7257             var ls = this.listeners, scope, len = ls.length;
7258             if(len > 0){
7259                 this.firing = true;
7260                 var args = Array.prototype.slice.call(arguments, 0);                
7261                 for(var i = 0; i < len; i++){
7262                     var l = ls[i];
7263                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7264                         this.firing = false;
7265                         return false;
7266                     }
7267                 }
7268                 this.firing = false;
7269             }
7270             return true;
7271         }
7272     };
7273 })();/*
7274  * RooJS Library 
7275  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7276  *
7277  * Licence LGPL 
7278  *
7279  */
7280  
7281 /**
7282  * @class Roo.Document
7283  * @extends Roo.util.Observable
7284  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7285  * 
7286  * @param {Object} config the methods and properties of the 'base' class for the application.
7287  * 
7288  *  Generic Page handler - implement this to start your app..
7289  * 
7290  * eg.
7291  *  MyProject = new Roo.Document({
7292         events : {
7293             'load' : true // your events..
7294         },
7295         listeners : {
7296             'ready' : function() {
7297                 // fired on Roo.onReady()
7298             }
7299         }
7300  * 
7301  */
7302 Roo.Document = function(cfg) {
7303      
7304     this.addEvents({ 
7305         'ready' : true
7306     });
7307     Roo.util.Observable.call(this,cfg);
7308     
7309     var _this = this;
7310     
7311     Roo.onReady(function() {
7312         _this.fireEvent('ready');
7313     },null,false);
7314     
7315     
7316 }
7317
7318 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7319  * Based on:
7320  * Ext JS Library 1.1.1
7321  * Copyright(c) 2006-2007, Ext JS, LLC.
7322  *
7323  * Originally Released Under LGPL - original licence link has changed is not relivant.
7324  *
7325  * Fork - LGPL
7326  * <script type="text/javascript">
7327  */
7328
7329 /**
7330  * @class Roo.EventManager
7331  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7332  * several useful events directly.
7333  * See {@link Roo.EventObject} for more details on normalized event objects.
7334  * @static
7335  */
7336 Roo.EventManager = function(){
7337     var docReadyEvent, docReadyProcId, docReadyState = false;
7338     var resizeEvent, resizeTask, textEvent, textSize;
7339     var E = Roo.lib.Event;
7340     var D = Roo.lib.Dom;
7341
7342     
7343     
7344
7345     var fireDocReady = function(){
7346         if(!docReadyState){
7347             docReadyState = true;
7348             Roo.isReady = true;
7349             if(docReadyProcId){
7350                 clearInterval(docReadyProcId);
7351             }
7352             if(Roo.isGecko || Roo.isOpera) {
7353                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7354             }
7355             if(Roo.isIE){
7356                 var defer = document.getElementById("ie-deferred-loader");
7357                 if(defer){
7358                     defer.onreadystatechange = null;
7359                     defer.parentNode.removeChild(defer);
7360                 }
7361             }
7362             if(docReadyEvent){
7363                 docReadyEvent.fire();
7364                 docReadyEvent.clearListeners();
7365             }
7366         }
7367     };
7368     
7369     var initDocReady = function(){
7370         docReadyEvent = new Roo.util.Event();
7371         if(Roo.isGecko || Roo.isOpera) {
7372             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7373         }else if(Roo.isIE){
7374             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7375             var defer = document.getElementById("ie-deferred-loader");
7376             defer.onreadystatechange = function(){
7377                 if(this.readyState == "complete"){
7378                     fireDocReady();
7379                 }
7380             };
7381         }else if(Roo.isSafari){ 
7382             docReadyProcId = setInterval(function(){
7383                 var rs = document.readyState;
7384                 if(rs == "complete") {
7385                     fireDocReady();     
7386                  }
7387             }, 10);
7388         }
7389         // no matter what, make sure it fires on load
7390         E.on(window, "load", fireDocReady);
7391     };
7392
7393     var createBuffered = function(h, o){
7394         var task = new Roo.util.DelayedTask(h);
7395         return function(e){
7396             // create new event object impl so new events don't wipe out properties
7397             e = new Roo.EventObjectImpl(e);
7398             task.delay(o.buffer, h, null, [e]);
7399         };
7400     };
7401
7402     var createSingle = function(h, el, ename, fn){
7403         return function(e){
7404             Roo.EventManager.removeListener(el, ename, fn);
7405             h(e);
7406         };
7407     };
7408
7409     var createDelayed = function(h, o){
7410         return function(e){
7411             // create new event object impl so new events don't wipe out properties
7412             e = new Roo.EventObjectImpl(e);
7413             setTimeout(function(){
7414                 h(e);
7415             }, o.delay || 10);
7416         };
7417     };
7418     var transitionEndVal = false;
7419     
7420     var transitionEnd = function()
7421     {
7422         if (transitionEndVal) {
7423             return transitionEndVal;
7424         }
7425         var el = document.createElement('div');
7426
7427         var transEndEventNames = {
7428             WebkitTransition : 'webkitTransitionEnd',
7429             MozTransition    : 'transitionend',
7430             OTransition      : 'oTransitionEnd otransitionend',
7431             transition       : 'transitionend'
7432         };
7433     
7434         for (var name in transEndEventNames) {
7435             if (el.style[name] !== undefined) {
7436                 transitionEndVal = transEndEventNames[name];
7437                 return  transitionEndVal ;
7438             }
7439         }
7440     }
7441     
7442   
7443
7444     var listen = function(element, ename, opt, fn, scope)
7445     {
7446         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7447         fn = fn || o.fn; scope = scope || o.scope;
7448         var el = Roo.getDom(element);
7449         
7450         
7451         if(!el){
7452             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7453         }
7454         
7455         if (ename == 'transitionend') {
7456             ename = transitionEnd();
7457         }
7458         var h = function(e){
7459             e = Roo.EventObject.setEvent(e);
7460             var t;
7461             if(o.delegate){
7462                 t = e.getTarget(o.delegate, el);
7463                 if(!t){
7464                     return;
7465                 }
7466             }else{
7467                 t = e.target;
7468             }
7469             if(o.stopEvent === true){
7470                 e.stopEvent();
7471             }
7472             if(o.preventDefault === true){
7473                e.preventDefault();
7474             }
7475             if(o.stopPropagation === true){
7476                 e.stopPropagation();
7477             }
7478
7479             if(o.normalized === false){
7480                 e = e.browserEvent;
7481             }
7482
7483             fn.call(scope || el, e, t, o);
7484         };
7485         if(o.delay){
7486             h = createDelayed(h, o);
7487         }
7488         if(o.single){
7489             h = createSingle(h, el, ename, fn);
7490         }
7491         if(o.buffer){
7492             h = createBuffered(h, o);
7493         }
7494         
7495         fn._handlers = fn._handlers || [];
7496         
7497         
7498         fn._handlers.push([Roo.id(el), ename, h]);
7499         
7500         
7501          
7502         E.on(el, ename, h); // this adds the actuall listener to the object..
7503         
7504         
7505         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7506             el.addEventListener("DOMMouseScroll", h, false);
7507             E.on(window, 'unload', function(){
7508                 el.removeEventListener("DOMMouseScroll", h, false);
7509             });
7510         }
7511         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7512             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7513         }
7514         return h;
7515     };
7516
7517     var stopListening = function(el, ename, fn){
7518         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7519         if(hds){
7520             for(var i = 0, len = hds.length; i < len; i++){
7521                 var h = hds[i];
7522                 if(h[0] == id && h[1] == ename){
7523                     hd = h[2];
7524                     hds.splice(i, 1);
7525                     break;
7526                 }
7527             }
7528         }
7529         E.un(el, ename, hd);
7530         el = Roo.getDom(el);
7531         if(ename == "mousewheel" && el.addEventListener){
7532             el.removeEventListener("DOMMouseScroll", hd, false);
7533         }
7534         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7535             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7536         }
7537     };
7538
7539     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7540     
7541     var pub = {
7542         
7543         
7544         /** 
7545          * Fix for doc tools
7546          * @scope Roo.EventManager
7547          */
7548         
7549         
7550         /** 
7551          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7552          * object with a Roo.EventObject
7553          * @param {Function} fn        The method the event invokes
7554          * @param {Object}   scope    An object that becomes the scope of the handler
7555          * @param {boolean}  override If true, the obj passed in becomes
7556          *                             the execution scope of the listener
7557          * @return {Function} The wrapped function
7558          * @deprecated
7559          */
7560         wrap : function(fn, scope, override){
7561             return function(e){
7562                 Roo.EventObject.setEvent(e);
7563                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7564             };
7565         },
7566         
7567         /**
7568      * Appends an event handler to an element (shorthand for addListener)
7569      * @param {String/HTMLElement}   element        The html element or id to assign the
7570      * @param {String}   eventName The type of event to listen for
7571      * @param {Function} handler The method the event invokes
7572      * @param {Object}   scope (optional) The scope in which to execute the handler
7573      * function. The handler function's "this" context.
7574      * @param {Object}   options (optional) An object containing handler configuration
7575      * properties. This may contain any of the following properties:<ul>
7576      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7577      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7578      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7579      * <li>preventDefault {Boolean} True to prevent the default action</li>
7580      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7581      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7582      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7583      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7584      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7585      * by the specified number of milliseconds. If the event fires again within that time, the original
7586      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7587      * </ul><br>
7588      * <p>
7589      * <b>Combining Options</b><br>
7590      * Using the options argument, it is possible to combine different types of listeners:<br>
7591      * <br>
7592      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7593      * Code:<pre><code>
7594 el.on('click', this.onClick, this, {
7595     single: true,
7596     delay: 100,
7597     stopEvent : true,
7598     forumId: 4
7599 });</code></pre>
7600      * <p>
7601      * <b>Attaching multiple handlers in 1 call</b><br>
7602       * The method also allows for a single argument to be passed which is a config object containing properties
7603      * which specify multiple handlers.
7604      * <p>
7605      * Code:<pre><code>
7606 el.on({
7607     'click' : {
7608         fn: this.onClick
7609         scope: this,
7610         delay: 100
7611     },
7612     'mouseover' : {
7613         fn: this.onMouseOver
7614         scope: this
7615     },
7616     'mouseout' : {
7617         fn: this.onMouseOut
7618         scope: this
7619     }
7620 });</code></pre>
7621      * <p>
7622      * Or a shorthand syntax:<br>
7623      * Code:<pre><code>
7624 el.on({
7625     'click' : this.onClick,
7626     'mouseover' : this.onMouseOver,
7627     'mouseout' : this.onMouseOut
7628     scope: this
7629 });</code></pre>
7630      */
7631         addListener : function(element, eventName, fn, scope, options){
7632             if(typeof eventName == "object"){
7633                 var o = eventName;
7634                 for(var e in o){
7635                     if(propRe.test(e)){
7636                         continue;
7637                     }
7638                     if(typeof o[e] == "function"){
7639                         // shared options
7640                         listen(element, e, o, o[e], o.scope);
7641                     }else{
7642                         // individual options
7643                         listen(element, e, o[e]);
7644                     }
7645                 }
7646                 return;
7647             }
7648             return listen(element, eventName, options, fn, scope);
7649         },
7650         
7651         /**
7652          * Removes an event handler
7653          *
7654          * @param {String/HTMLElement}   element        The id or html element to remove the 
7655          *                             event from
7656          * @param {String}   eventName     The type of event
7657          * @param {Function} fn
7658          * @return {Boolean} True if a listener was actually removed
7659          */
7660         removeListener : function(element, eventName, fn){
7661             return stopListening(element, eventName, fn);
7662         },
7663         
7664         /**
7665          * Fires when the document is ready (before onload and before images are loaded). Can be 
7666          * accessed shorthanded Roo.onReady().
7667          * @param {Function} fn        The method the event invokes
7668          * @param {Object}   scope    An  object that becomes the scope of the handler
7669          * @param {boolean}  options
7670          */
7671         onDocumentReady : function(fn, scope, options){
7672             if(docReadyState){ // if it already fired
7673                 docReadyEvent.addListener(fn, scope, options);
7674                 docReadyEvent.fire();
7675                 docReadyEvent.clearListeners();
7676                 return;
7677             }
7678             if(!docReadyEvent){
7679                 initDocReady();
7680             }
7681             docReadyEvent.addListener(fn, scope, options);
7682         },
7683         
7684         /**
7685          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7686          * @param {Function} fn        The method the event invokes
7687          * @param {Object}   scope    An object that becomes the scope of the handler
7688          * @param {boolean}  options
7689          */
7690         onWindowResize : function(fn, scope, options)
7691         {
7692             if(!resizeEvent){
7693                 resizeEvent = new Roo.util.Event();
7694                 resizeTask = new Roo.util.DelayedTask(function(){
7695                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7696                 });
7697                 E.on(window, "resize", function()
7698                 {
7699                     if (Roo.isIE) {
7700                         resizeTask.delay(50);
7701                     } else {
7702                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7703                     }
7704                 });
7705             }
7706             resizeEvent.addListener(fn, scope, options);
7707         },
7708
7709         /**
7710          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7711          * @param {Function} fn        The method the event invokes
7712          * @param {Object}   scope    An object that becomes the scope of the handler
7713          * @param {boolean}  options
7714          */
7715         onTextResize : function(fn, scope, options){
7716             if(!textEvent){
7717                 textEvent = new Roo.util.Event();
7718                 var textEl = new Roo.Element(document.createElement('div'));
7719                 textEl.dom.className = 'x-text-resize';
7720                 textEl.dom.innerHTML = 'X';
7721                 textEl.appendTo(document.body);
7722                 textSize = textEl.dom.offsetHeight;
7723                 setInterval(function(){
7724                     if(textEl.dom.offsetHeight != textSize){
7725                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7726                     }
7727                 }, this.textResizeInterval);
7728             }
7729             textEvent.addListener(fn, scope, options);
7730         },
7731
7732         /**
7733          * Removes the passed window resize listener.
7734          * @param {Function} fn        The method the event invokes
7735          * @param {Object}   scope    The scope of handler
7736          */
7737         removeResizeListener : function(fn, scope){
7738             if(resizeEvent){
7739                 resizeEvent.removeListener(fn, scope);
7740             }
7741         },
7742
7743         // private
7744         fireResize : function(){
7745             if(resizeEvent){
7746                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7747             }   
7748         },
7749         /**
7750          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7751          */
7752         ieDeferSrc : false,
7753         /**
7754          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7755          */
7756         textResizeInterval : 50
7757     };
7758     
7759     /**
7760      * Fix for doc tools
7761      * @scopeAlias pub=Roo.EventManager
7762      */
7763     
7764      /**
7765      * Appends an event handler to an element (shorthand for addListener)
7766      * @param {String/HTMLElement}   element        The html element or id to assign the
7767      * @param {String}   eventName The type of event to listen for
7768      * @param {Function} handler The method the event invokes
7769      * @param {Object}   scope (optional) The scope in which to execute the handler
7770      * function. The handler function's "this" context.
7771      * @param {Object}   options (optional) An object containing handler configuration
7772      * properties. This may contain any of the following properties:<ul>
7773      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7774      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7775      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7776      * <li>preventDefault {Boolean} True to prevent the default action</li>
7777      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7778      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7779      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7780      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7781      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7782      * by the specified number of milliseconds. If the event fires again within that time, the original
7783      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7784      * </ul><br>
7785      * <p>
7786      * <b>Combining Options</b><br>
7787      * Using the options argument, it is possible to combine different types of listeners:<br>
7788      * <br>
7789      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7790      * Code:<pre><code>
7791 el.on('click', this.onClick, this, {
7792     single: true,
7793     delay: 100,
7794     stopEvent : true,
7795     forumId: 4
7796 });</code></pre>
7797      * <p>
7798      * <b>Attaching multiple handlers in 1 call</b><br>
7799       * The method also allows for a single argument to be passed which is a config object containing properties
7800      * which specify multiple handlers.
7801      * <p>
7802      * Code:<pre><code>
7803 el.on({
7804     'click' : {
7805         fn: this.onClick
7806         scope: this,
7807         delay: 100
7808     },
7809     'mouseover' : {
7810         fn: this.onMouseOver
7811         scope: this
7812     },
7813     'mouseout' : {
7814         fn: this.onMouseOut
7815         scope: this
7816     }
7817 });</code></pre>
7818      * <p>
7819      * Or a shorthand syntax:<br>
7820      * Code:<pre><code>
7821 el.on({
7822     'click' : this.onClick,
7823     'mouseover' : this.onMouseOver,
7824     'mouseout' : this.onMouseOut
7825     scope: this
7826 });</code></pre>
7827      */
7828     pub.on = pub.addListener;
7829     pub.un = pub.removeListener;
7830
7831     pub.stoppedMouseDownEvent = new Roo.util.Event();
7832     return pub;
7833 }();
7834 /**
7835   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7836   * @param {Function} fn        The method the event invokes
7837   * @param {Object}   scope    An  object that becomes the scope of the handler
7838   * @param {boolean}  override If true, the obj passed in becomes
7839   *                             the execution scope of the listener
7840   * @member Roo
7841   * @method onReady
7842  */
7843 Roo.onReady = Roo.EventManager.onDocumentReady;
7844
7845 Roo.onReady(function(){
7846     var bd = Roo.get(document.body);
7847     if(!bd){ return; }
7848
7849     var cls = [
7850             Roo.isIE ? "roo-ie"
7851             : Roo.isIE11 ? "roo-ie11"
7852             : Roo.isEdge ? "roo-edge"
7853             : Roo.isGecko ? "roo-gecko"
7854             : Roo.isOpera ? "roo-opera"
7855             : Roo.isSafari ? "roo-safari" : ""];
7856
7857     if(Roo.isMac){
7858         cls.push("roo-mac");
7859     }
7860     if(Roo.isLinux){
7861         cls.push("roo-linux");
7862     }
7863     if(Roo.isIOS){
7864         cls.push("roo-ios");
7865     }
7866     if(Roo.isTouch){
7867         cls.push("roo-touch");
7868     }
7869     if(Roo.isBorderBox){
7870         cls.push('roo-border-box');
7871     }
7872     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7873         var p = bd.dom.parentNode;
7874         if(p){
7875             p.className += ' roo-strict';
7876         }
7877     }
7878     bd.addClass(cls.join(' '));
7879 });
7880
7881 /**
7882  * @class Roo.EventObject
7883  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7884  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7885  * Example:
7886  * <pre><code>
7887  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7888     e.preventDefault();
7889     var target = e.getTarget();
7890     ...
7891  }
7892  var myDiv = Roo.get("myDiv");
7893  myDiv.on("click", handleClick);
7894  //or
7895  Roo.EventManager.on("myDiv", 'click', handleClick);
7896  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7897  </code></pre>
7898  * @static
7899  */
7900 Roo.EventObject = function(){
7901     
7902     var E = Roo.lib.Event;
7903     
7904     // safari keypress events for special keys return bad keycodes
7905     var safariKeys = {
7906         63234 : 37, // left
7907         63235 : 39, // right
7908         63232 : 38, // up
7909         63233 : 40, // down
7910         63276 : 33, // page up
7911         63277 : 34, // page down
7912         63272 : 46, // delete
7913         63273 : 36, // home
7914         63275 : 35  // end
7915     };
7916
7917     // normalize button clicks
7918     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7919                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7920
7921     Roo.EventObjectImpl = function(e){
7922         if(e){
7923             this.setEvent(e.browserEvent || e);
7924         }
7925     };
7926     Roo.EventObjectImpl.prototype = {
7927         /**
7928          * Used to fix doc tools.
7929          * @scope Roo.EventObject.prototype
7930          */
7931             
7932
7933         
7934         
7935         /** The normal browser event */
7936         browserEvent : null,
7937         /** The button pressed in a mouse event */
7938         button : -1,
7939         /** True if the shift key was down during the event */
7940         shiftKey : false,
7941         /** True if the control key was down during the event */
7942         ctrlKey : false,
7943         /** True if the alt key was down during the event */
7944         altKey : false,
7945
7946         /** Key constant 
7947         * @type Number */
7948         BACKSPACE : 8,
7949         /** Key constant 
7950         * @type Number */
7951         TAB : 9,
7952         /** Key constant 
7953         * @type Number */
7954         RETURN : 13,
7955         /** Key constant 
7956         * @type Number */
7957         ENTER : 13,
7958         /** Key constant 
7959         * @type Number */
7960         SHIFT : 16,
7961         /** Key constant 
7962         * @type Number */
7963         CONTROL : 17,
7964         /** Key constant 
7965         * @type Number */
7966         ESC : 27,
7967         /** Key constant 
7968         * @type Number */
7969         SPACE : 32,
7970         /** Key constant 
7971         * @type Number */
7972         PAGEUP : 33,
7973         /** Key constant 
7974         * @type Number */
7975         PAGEDOWN : 34,
7976         /** Key constant 
7977         * @type Number */
7978         END : 35,
7979         /** Key constant 
7980         * @type Number */
7981         HOME : 36,
7982         /** Key constant 
7983         * @type Number */
7984         LEFT : 37,
7985         /** Key constant 
7986         * @type Number */
7987         UP : 38,
7988         /** Key constant 
7989         * @type Number */
7990         RIGHT : 39,
7991         /** Key constant 
7992         * @type Number */
7993         DOWN : 40,
7994         /** Key constant 
7995         * @type Number */
7996         DELETE : 46,
7997         /** Key constant 
7998         * @type Number */
7999         F5 : 116,
8000
8001            /** @private */
8002         setEvent : function(e){
8003             if(e == this || (e && e.browserEvent)){ // already wrapped
8004                 return e;
8005             }
8006             this.browserEvent = e;
8007             if(e){
8008                 // normalize buttons
8009                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8010                 if(e.type == 'click' && this.button == -1){
8011                     this.button = 0;
8012                 }
8013                 this.type = e.type;
8014                 this.shiftKey = e.shiftKey;
8015                 // mac metaKey behaves like ctrlKey
8016                 this.ctrlKey = e.ctrlKey || e.metaKey;
8017                 this.altKey = e.altKey;
8018                 // in getKey these will be normalized for the mac
8019                 this.keyCode = e.keyCode;
8020                 // keyup warnings on firefox.
8021                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8022                 // cache the target for the delayed and or buffered events
8023                 this.target = E.getTarget(e);
8024                 // same for XY
8025                 this.xy = E.getXY(e);
8026             }else{
8027                 this.button = -1;
8028                 this.shiftKey = false;
8029                 this.ctrlKey = false;
8030                 this.altKey = false;
8031                 this.keyCode = 0;
8032                 this.charCode =0;
8033                 this.target = null;
8034                 this.xy = [0, 0];
8035             }
8036             return this;
8037         },
8038
8039         /**
8040          * Stop the event (preventDefault and stopPropagation)
8041          */
8042         stopEvent : function(){
8043             if(this.browserEvent){
8044                 if(this.browserEvent.type == 'mousedown'){
8045                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8046                 }
8047                 E.stopEvent(this.browserEvent);
8048             }
8049         },
8050
8051         /**
8052          * Prevents the browsers default handling of the event.
8053          */
8054         preventDefault : function(){
8055             if(this.browserEvent){
8056                 E.preventDefault(this.browserEvent);
8057             }
8058         },
8059
8060         /** @private */
8061         isNavKeyPress : function(){
8062             var k = this.keyCode;
8063             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8064             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8065         },
8066
8067         isSpecialKey : function(){
8068             var k = this.keyCode;
8069             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8070             (k == 16) || (k == 17) ||
8071             (k >= 18 && k <= 20) ||
8072             (k >= 33 && k <= 35) ||
8073             (k >= 36 && k <= 39) ||
8074             (k >= 44 && k <= 45);
8075         },
8076         /**
8077          * Cancels bubbling of the event.
8078          */
8079         stopPropagation : function(){
8080             if(this.browserEvent){
8081                 if(this.type == 'mousedown'){
8082                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8083                 }
8084                 E.stopPropagation(this.browserEvent);
8085             }
8086         },
8087
8088         /**
8089          * Gets the key code for the event.
8090          * @return {Number}
8091          */
8092         getCharCode : function(){
8093             return this.charCode || this.keyCode;
8094         },
8095
8096         /**
8097          * Returns a normalized keyCode for the event.
8098          * @return {Number} The key code
8099          */
8100         getKey : function(){
8101             var k = this.keyCode || this.charCode;
8102             return Roo.isSafari ? (safariKeys[k] || k) : k;
8103         },
8104
8105         /**
8106          * Gets the x coordinate of the event.
8107          * @return {Number}
8108          */
8109         getPageX : function(){
8110             return this.xy[0];
8111         },
8112
8113         /**
8114          * Gets the y coordinate of the event.
8115          * @return {Number}
8116          */
8117         getPageY : function(){
8118             return this.xy[1];
8119         },
8120
8121         /**
8122          * Gets the time of the event.
8123          * @return {Number}
8124          */
8125         getTime : function(){
8126             if(this.browserEvent){
8127                 return E.getTime(this.browserEvent);
8128             }
8129             return null;
8130         },
8131
8132         /**
8133          * Gets the page coordinates of the event.
8134          * @return {Array} The xy values like [x, y]
8135          */
8136         getXY : function(){
8137             return this.xy;
8138         },
8139
8140         /**
8141          * Gets the target for the event.
8142          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8143          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8144                 search as a number or element (defaults to 10 || document.body)
8145          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8146          * @return {HTMLelement}
8147          */
8148         getTarget : function(selector, maxDepth, returnEl){
8149             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8150         },
8151         /**
8152          * Gets the related target.
8153          * @return {HTMLElement}
8154          */
8155         getRelatedTarget : function(){
8156             if(this.browserEvent){
8157                 return E.getRelatedTarget(this.browserEvent);
8158             }
8159             return null;
8160         },
8161
8162         /**
8163          * Normalizes mouse wheel delta across browsers
8164          * @return {Number} The delta
8165          */
8166         getWheelDelta : function(){
8167             var e = this.browserEvent;
8168             var delta = 0;
8169             if(e.wheelDelta){ /* IE/Opera. */
8170                 delta = e.wheelDelta/120;
8171             }else if(e.detail){ /* Mozilla case. */
8172                 delta = -e.detail/3;
8173             }
8174             return delta;
8175         },
8176
8177         /**
8178          * Returns true if the control, meta, shift or alt key was pressed during this event.
8179          * @return {Boolean}
8180          */
8181         hasModifier : function(){
8182             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8183         },
8184
8185         /**
8186          * Returns true if the target of this event equals el or is a child of el
8187          * @param {String/HTMLElement/Element} el
8188          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8189          * @return {Boolean}
8190          */
8191         within : function(el, related){
8192             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8193             return t && Roo.fly(el).contains(t);
8194         },
8195
8196         getPoint : function(){
8197             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8198         }
8199     };
8200
8201     return new Roo.EventObjectImpl();
8202 }();
8203             
8204     /*
8205  * Based on:
8206  * Ext JS Library 1.1.1
8207  * Copyright(c) 2006-2007, Ext JS, LLC.
8208  *
8209  * Originally Released Under LGPL - original licence link has changed is not relivant.
8210  *
8211  * Fork - LGPL
8212  * <script type="text/javascript">
8213  */
8214
8215  
8216 // was in Composite Element!??!?!
8217  
8218 (function(){
8219     var D = Roo.lib.Dom;
8220     var E = Roo.lib.Event;
8221     var A = Roo.lib.Anim;
8222
8223     // local style camelizing for speed
8224     var propCache = {};
8225     var camelRe = /(-[a-z])/gi;
8226     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8227     var view = document.defaultView;
8228
8229 /**
8230  * @class Roo.Element
8231  * Represents an Element in the DOM.<br><br>
8232  * Usage:<br>
8233 <pre><code>
8234 var el = Roo.get("my-div");
8235
8236 // or with getEl
8237 var el = getEl("my-div");
8238
8239 // or with a DOM element
8240 var el = Roo.get(myDivElement);
8241 </code></pre>
8242  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8243  * each call instead of constructing a new one.<br><br>
8244  * <b>Animations</b><br />
8245  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8246  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8247 <pre>
8248 Option    Default   Description
8249 --------- --------  ---------------------------------------------
8250 duration  .35       The duration of the animation in seconds
8251 easing    easeOut   The YUI easing method
8252 callback  none      A function to execute when the anim completes
8253 scope     this      The scope (this) of the callback function
8254 </pre>
8255 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8256 * manipulate the animation. Here's an example:
8257 <pre><code>
8258 var el = Roo.get("my-div");
8259
8260 // no animation
8261 el.setWidth(100);
8262
8263 // default animation
8264 el.setWidth(100, true);
8265
8266 // animation with some options set
8267 el.setWidth(100, {
8268     duration: 1,
8269     callback: this.foo,
8270     scope: this
8271 });
8272
8273 // using the "anim" property to get the Anim object
8274 var opt = {
8275     duration: 1,
8276     callback: this.foo,
8277     scope: this
8278 };
8279 el.setWidth(100, opt);
8280 ...
8281 if(opt.anim.isAnimated()){
8282     opt.anim.stop();
8283 }
8284 </code></pre>
8285 * <b> Composite (Collections of) Elements</b><br />
8286  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8287  * @constructor Create a new Element directly.
8288  * @param {String/HTMLElement} element
8289  * @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).
8290  */
8291     Roo.Element = function(element, forceNew)
8292     {
8293         var dom = typeof element == "string" ?
8294                 document.getElementById(element) : element;
8295         
8296         this.listeners = {};
8297         
8298         if(!dom){ // invalid id/element
8299             return null;
8300         }
8301         var id = dom.id;
8302         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8303             return Roo.Element.cache[id];
8304         }
8305
8306         /**
8307          * The DOM element
8308          * @type HTMLElement
8309          */
8310         this.dom = dom;
8311
8312         /**
8313          * The DOM element ID
8314          * @type String
8315          */
8316         this.id = id || Roo.id(dom);
8317         
8318         return this; // assumed for cctor?
8319     };
8320
8321     var El = Roo.Element;
8322
8323     El.prototype = {
8324         /**
8325          * The element's default display mode  (defaults to "") 
8326          * @type String
8327          */
8328         originalDisplay : "",
8329
8330         
8331         // note this is overridden in BS version..
8332         visibilityMode : 1, 
8333         /**
8334          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8335          * @type String
8336          */
8337         defaultUnit : "px",
8338         
8339         /**
8340          * Sets the element's visibility mode. When setVisible() is called it
8341          * will use this to determine whether to set the visibility or the display property.
8342          * @param visMode Element.VISIBILITY or Element.DISPLAY
8343          * @return {Roo.Element} this
8344          */
8345         setVisibilityMode : function(visMode){
8346             this.visibilityMode = visMode;
8347             return this;
8348         },
8349         /**
8350          * Convenience method for setVisibilityMode(Element.DISPLAY)
8351          * @param {String} display (optional) What to set display to when visible
8352          * @return {Roo.Element} this
8353          */
8354         enableDisplayMode : function(display){
8355             this.setVisibilityMode(El.DISPLAY);
8356             if(typeof display != "undefined") { this.originalDisplay = display; }
8357             return this;
8358         },
8359
8360         /**
8361          * 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)
8362          * @param {String} selector The simple selector to test
8363          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8364                 search as a number or element (defaults to 10 || document.body)
8365          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8366          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8367          */
8368         findParent : function(simpleSelector, maxDepth, returnEl){
8369             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8370             maxDepth = maxDepth || 50;
8371             if(typeof maxDepth != "number"){
8372                 stopEl = Roo.getDom(maxDepth);
8373                 maxDepth = 10;
8374             }
8375             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8376                 if(dq.is(p, simpleSelector)){
8377                     return returnEl ? Roo.get(p) : p;
8378                 }
8379                 depth++;
8380                 p = p.parentNode;
8381             }
8382             return null;
8383         },
8384
8385
8386         /**
8387          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8388          * @param {String} selector The simple selector to test
8389          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8390                 search as a number or element (defaults to 10 || document.body)
8391          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8392          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8393          */
8394         findParentNode : function(simpleSelector, maxDepth, returnEl){
8395             var p = Roo.fly(this.dom.parentNode, '_internal');
8396             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8397         },
8398         
8399         /**
8400          * Looks at  the scrollable parent element
8401          */
8402         findScrollableParent : function()
8403         {
8404             var overflowRegex = /(auto|scroll)/;
8405             
8406             if(this.getStyle('position') === 'fixed'){
8407                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8408             }
8409             
8410             var excludeStaticParent = this.getStyle('position') === "absolute";
8411             
8412             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8413                 
8414                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8415                     continue;
8416                 }
8417                 
8418                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8419                     return parent;
8420                 }
8421                 
8422                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8423                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8424                 }
8425             }
8426             
8427             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8428         },
8429
8430         /**
8431          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8432          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8433          * @param {String} selector The simple selector to test
8434          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8435                 search as a number or element (defaults to 10 || document.body)
8436          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8437          */
8438         up : function(simpleSelector, maxDepth){
8439             return this.findParentNode(simpleSelector, maxDepth, true);
8440         },
8441
8442
8443
8444         /**
8445          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8446          * @param {String} selector The simple selector to test
8447          * @return {Boolean} True if this element matches the selector, else false
8448          */
8449         is : function(simpleSelector){
8450             return Roo.DomQuery.is(this.dom, simpleSelector);
8451         },
8452
8453         /**
8454          * Perform animation on this element.
8455          * @param {Object} args The YUI animation control args
8456          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8457          * @param {Function} onComplete (optional) Function to call when animation completes
8458          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8459          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8460          * @return {Roo.Element} this
8461          */
8462         animate : function(args, duration, onComplete, easing, animType){
8463             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8464             return this;
8465         },
8466
8467         /*
8468          * @private Internal animation call
8469          */
8470         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8471             animType = animType || 'run';
8472             opt = opt || {};
8473             var anim = Roo.lib.Anim[animType](
8474                 this.dom, args,
8475                 (opt.duration || defaultDur) || .35,
8476                 (opt.easing || defaultEase) || 'easeOut',
8477                 function(){
8478                     Roo.callback(cb, this);
8479                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8480                 },
8481                 this
8482             );
8483             opt.anim = anim;
8484             return anim;
8485         },
8486
8487         // private legacy anim prep
8488         preanim : function(a, i){
8489             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8490         },
8491
8492         /**
8493          * Removes worthless text nodes
8494          * @param {Boolean} forceReclean (optional) By default the element
8495          * keeps track if it has been cleaned already so
8496          * you can call this over and over. However, if you update the element and
8497          * need to force a reclean, you can pass true.
8498          */
8499         clean : function(forceReclean){
8500             if(this.isCleaned && forceReclean !== true){
8501                 return this;
8502             }
8503             var ns = /\S/;
8504             var d = this.dom, n = d.firstChild, ni = -1;
8505             while(n){
8506                 var nx = n.nextSibling;
8507                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8508                     d.removeChild(n);
8509                 }else{
8510                     n.nodeIndex = ++ni;
8511                 }
8512                 n = nx;
8513             }
8514             this.isCleaned = true;
8515             return this;
8516         },
8517
8518         // private
8519         calcOffsetsTo : function(el){
8520             el = Roo.get(el);
8521             var d = el.dom;
8522             var restorePos = false;
8523             if(el.getStyle('position') == 'static'){
8524                 el.position('relative');
8525                 restorePos = true;
8526             }
8527             var x = 0, y =0;
8528             var op = this.dom;
8529             while(op && op != d && op.tagName != 'HTML'){
8530                 x+= op.offsetLeft;
8531                 y+= op.offsetTop;
8532                 op = op.offsetParent;
8533             }
8534             if(restorePos){
8535                 el.position('static');
8536             }
8537             return [x, y];
8538         },
8539
8540         /**
8541          * Scrolls this element into view within the passed container.
8542          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8543          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8544          * @return {Roo.Element} this
8545          */
8546         scrollIntoView : function(container, hscroll){
8547             var c = Roo.getDom(container) || document.body;
8548             var el = this.dom;
8549
8550             var o = this.calcOffsetsTo(c),
8551                 l = o[0],
8552                 t = o[1],
8553                 b = t+el.offsetHeight,
8554                 r = l+el.offsetWidth;
8555
8556             var ch = c.clientHeight;
8557             var ct = parseInt(c.scrollTop, 10);
8558             var cl = parseInt(c.scrollLeft, 10);
8559             var cb = ct + ch;
8560             var cr = cl + c.clientWidth;
8561
8562             if(t < ct){
8563                 c.scrollTop = t;
8564             }else if(b > cb){
8565                 c.scrollTop = b-ch;
8566             }
8567
8568             if(hscroll !== false){
8569                 if(l < cl){
8570                     c.scrollLeft = l;
8571                 }else if(r > cr){
8572                     c.scrollLeft = r-c.clientWidth;
8573                 }
8574             }
8575             return this;
8576         },
8577
8578         // private
8579         scrollChildIntoView : function(child, hscroll){
8580             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8581         },
8582
8583         /**
8584          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8585          * the new height may not be available immediately.
8586          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8587          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8588          * @param {Function} onComplete (optional) Function to call when animation completes
8589          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8590          * @return {Roo.Element} this
8591          */
8592         autoHeight : function(animate, duration, onComplete, easing){
8593             var oldHeight = this.getHeight();
8594             this.clip();
8595             this.setHeight(1); // force clipping
8596             setTimeout(function(){
8597                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8598                 if(!animate){
8599                     this.setHeight(height);
8600                     this.unclip();
8601                     if(typeof onComplete == "function"){
8602                         onComplete();
8603                     }
8604                 }else{
8605                     this.setHeight(oldHeight); // restore original height
8606                     this.setHeight(height, animate, duration, function(){
8607                         this.unclip();
8608                         if(typeof onComplete == "function") { onComplete(); }
8609                     }.createDelegate(this), easing);
8610                 }
8611             }.createDelegate(this), 0);
8612             return this;
8613         },
8614
8615         /**
8616          * Returns true if this element is an ancestor of the passed element
8617          * @param {HTMLElement/String} el The element to check
8618          * @return {Boolean} True if this element is an ancestor of el, else false
8619          */
8620         contains : function(el){
8621             if(!el){return false;}
8622             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8623         },
8624
8625         /**
8626          * Checks whether the element is currently visible using both visibility and display properties.
8627          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8628          * @return {Boolean} True if the element is currently visible, else false
8629          */
8630         isVisible : function(deep) {
8631             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8632             if(deep !== true || !vis){
8633                 return vis;
8634             }
8635             var p = this.dom.parentNode;
8636             while(p && p.tagName.toLowerCase() != "body"){
8637                 if(!Roo.fly(p, '_isVisible').isVisible()){
8638                     return false;
8639                 }
8640                 p = p.parentNode;
8641             }
8642             return true;
8643         },
8644
8645         /**
8646          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8647          * @param {String} selector The CSS selector
8648          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8649          * @return {CompositeElement/CompositeElementLite} The composite element
8650          */
8651         select : function(selector, unique){
8652             return El.select(selector, unique, this.dom);
8653         },
8654
8655         /**
8656          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8657          * @param {String} selector The CSS selector
8658          * @return {Array} An array of the matched nodes
8659          */
8660         query : function(selector, unique){
8661             return Roo.DomQuery.select(selector, this.dom);
8662         },
8663
8664         /**
8665          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8666          * @param {String} selector The CSS selector
8667          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8668          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8669          */
8670         child : function(selector, returnDom){
8671             var n = Roo.DomQuery.selectNode(selector, this.dom);
8672             return returnDom ? n : Roo.get(n);
8673         },
8674
8675         /**
8676          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8677          * @param {String} selector The CSS selector
8678          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8679          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8680          */
8681         down : function(selector, returnDom){
8682             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8683             return returnDom ? n : Roo.get(n);
8684         },
8685
8686         /**
8687          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8688          * @param {String} group The group the DD object is member of
8689          * @param {Object} config The DD config object
8690          * @param {Object} overrides An object containing methods to override/implement on the DD object
8691          * @return {Roo.dd.DD} The DD object
8692          */
8693         initDD : function(group, config, overrides){
8694             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8695             return Roo.apply(dd, overrides);
8696         },
8697
8698         /**
8699          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8700          * @param {String} group The group the DDProxy object is member of
8701          * @param {Object} config The DDProxy config object
8702          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8703          * @return {Roo.dd.DDProxy} The DDProxy object
8704          */
8705         initDDProxy : function(group, config, overrides){
8706             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8707             return Roo.apply(dd, overrides);
8708         },
8709
8710         /**
8711          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8712          * @param {String} group The group the DDTarget object is member of
8713          * @param {Object} config The DDTarget config object
8714          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8715          * @return {Roo.dd.DDTarget} The DDTarget object
8716          */
8717         initDDTarget : function(group, config, overrides){
8718             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8719             return Roo.apply(dd, overrides);
8720         },
8721
8722         /**
8723          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8724          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8725          * @param {Boolean} visible Whether the element is visible
8726          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8727          * @return {Roo.Element} this
8728          */
8729          setVisible : function(visible, animate){
8730             if(!animate || !A){
8731                 if(this.visibilityMode == El.DISPLAY){
8732                     this.setDisplayed(visible);
8733                 }else{
8734                     this.fixDisplay();
8735                     this.dom.style.visibility = visible ? "visible" : "hidden";
8736                 }
8737             }else{
8738                 // closure for composites
8739                 var dom = this.dom;
8740                 var visMode = this.visibilityMode;
8741                 if(visible){
8742                     this.setOpacity(.01);
8743                     this.setVisible(true);
8744                 }
8745                 this.anim({opacity: { to: (visible?1:0) }},
8746                       this.preanim(arguments, 1),
8747                       null, .35, 'easeIn', function(){
8748                          if(!visible){
8749                              if(visMode == El.DISPLAY){
8750                                  dom.style.display = "none";
8751                              }else{
8752                                  dom.style.visibility = "hidden";
8753                              }
8754                              Roo.get(dom).setOpacity(1);
8755                          }
8756                      });
8757             }
8758             return this;
8759         },
8760
8761         /**
8762          * Returns true if display is not "none"
8763          * @return {Boolean}
8764          */
8765         isDisplayed : function() {
8766             return this.getStyle("display") != "none";
8767         },
8768
8769         /**
8770          * Toggles the element's visibility or display, depending on visibility mode.
8771          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8772          * @return {Roo.Element} this
8773          */
8774         toggle : function(animate){
8775             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8776             return this;
8777         },
8778
8779         /**
8780          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8781          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8782          * @return {Roo.Element} this
8783          */
8784         setDisplayed : function(value) {
8785             if(typeof value == "boolean"){
8786                value = value ? this.originalDisplay : "none";
8787             }
8788             this.setStyle("display", value);
8789             return this;
8790         },
8791
8792         /**
8793          * Tries to focus the element. Any exceptions are caught and ignored.
8794          * @return {Roo.Element} this
8795          */
8796         focus : function() {
8797             try{
8798                 this.dom.focus();
8799             }catch(e){}
8800             return this;
8801         },
8802
8803         /**
8804          * Tries to blur the element. Any exceptions are caught and ignored.
8805          * @return {Roo.Element} this
8806          */
8807         blur : function() {
8808             try{
8809                 this.dom.blur();
8810             }catch(e){}
8811             return this;
8812         },
8813
8814         /**
8815          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8816          * @param {String/Array} className The CSS class to add, or an array of classes
8817          * @return {Roo.Element} this
8818          */
8819         addClass : function(className){
8820             if(className instanceof Array){
8821                 for(var i = 0, len = className.length; i < len; i++) {
8822                     this.addClass(className[i]);
8823                 }
8824             }else{
8825                 if(className && !this.hasClass(className)){
8826                     if (this.dom instanceof SVGElement) {
8827                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8828                     } else {
8829                         this.dom.className = this.dom.className + " " + className;
8830                     }
8831                 }
8832             }
8833             return this;
8834         },
8835
8836         /**
8837          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8838          * @param {String/Array} className The CSS class to add, or an array of classes
8839          * @return {Roo.Element} this
8840          */
8841         radioClass : function(className){
8842             var siblings = this.dom.parentNode.childNodes;
8843             for(var i = 0; i < siblings.length; i++) {
8844                 var s = siblings[i];
8845                 if(s.nodeType == 1){
8846                     Roo.get(s).removeClass(className);
8847                 }
8848             }
8849             this.addClass(className);
8850             return this;
8851         },
8852
8853         /**
8854          * Removes one or more CSS classes from the element.
8855          * @param {String/Array} className The CSS class to remove, or an array of classes
8856          * @return {Roo.Element} this
8857          */
8858         removeClass : function(className){
8859             
8860             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8861             if(!className || !cn){
8862                 return this;
8863             }
8864             if(className instanceof Array){
8865                 for(var i = 0, len = className.length; i < len; i++) {
8866                     this.removeClass(className[i]);
8867                 }
8868             }else{
8869                 if(this.hasClass(className)){
8870                     var re = this.classReCache[className];
8871                     if (!re) {
8872                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8873                        this.classReCache[className] = re;
8874                     }
8875                     if (this.dom instanceof SVGElement) {
8876                         this.dom.className.baseVal = cn.replace(re, " ");
8877                     } else {
8878                         this.dom.className = cn.replace(re, " ");
8879                     }
8880                 }
8881             }
8882             return this;
8883         },
8884
8885         // private
8886         classReCache: {},
8887
8888         /**
8889          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8890          * @param {String} className The CSS class to toggle
8891          * @return {Roo.Element} this
8892          */
8893         toggleClass : function(className){
8894             if(this.hasClass(className)){
8895                 this.removeClass(className);
8896             }else{
8897                 this.addClass(className);
8898             }
8899             return this;
8900         },
8901
8902         /**
8903          * Checks if the specified CSS class exists on this element's DOM node.
8904          * @param {String} className The CSS class to check for
8905          * @return {Boolean} True if the class exists, else false
8906          */
8907         hasClass : function(className){
8908             if (this.dom instanceof SVGElement) {
8909                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8910             } 
8911             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8912         },
8913
8914         /**
8915          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8916          * @param {String} oldClassName The CSS class to replace
8917          * @param {String} newClassName The replacement CSS class
8918          * @return {Roo.Element} this
8919          */
8920         replaceClass : function(oldClassName, newClassName){
8921             this.removeClass(oldClassName);
8922             this.addClass(newClassName);
8923             return this;
8924         },
8925
8926         /**
8927          * Returns an object with properties matching the styles requested.
8928          * For example, el.getStyles('color', 'font-size', 'width') might return
8929          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8930          * @param {String} style1 A style name
8931          * @param {String} style2 A style name
8932          * @param {String} etc.
8933          * @return {Object} The style object
8934          */
8935         getStyles : function(){
8936             var a = arguments, len = a.length, r = {};
8937             for(var i = 0; i < len; i++){
8938                 r[a[i]] = this.getStyle(a[i]);
8939             }
8940             return r;
8941         },
8942
8943         /**
8944          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8945          * @param {String} property The style property whose value is returned.
8946          * @return {String} The current value of the style property for this element.
8947          */
8948         getStyle : function(){
8949             return view && view.getComputedStyle ?
8950                 function(prop){
8951                     var el = this.dom, v, cs, camel;
8952                     if(prop == 'float'){
8953                         prop = "cssFloat";
8954                     }
8955                     if(el.style && (v = el.style[prop])){
8956                         return v;
8957                     }
8958                     if(cs = view.getComputedStyle(el, "")){
8959                         if(!(camel = propCache[prop])){
8960                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8961                         }
8962                         return cs[camel];
8963                     }
8964                     return null;
8965                 } :
8966                 function(prop){
8967                     var el = this.dom, v, cs, camel;
8968                     if(prop == 'opacity'){
8969                         if(typeof el.style.filter == 'string'){
8970                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8971                             if(m){
8972                                 var fv = parseFloat(m[1]);
8973                                 if(!isNaN(fv)){
8974                                     return fv ? fv / 100 : 0;
8975                                 }
8976                             }
8977                         }
8978                         return 1;
8979                     }else if(prop == 'float'){
8980                         prop = "styleFloat";
8981                     }
8982                     if(!(camel = propCache[prop])){
8983                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8984                     }
8985                     if(v = el.style[camel]){
8986                         return v;
8987                     }
8988                     if(cs = el.currentStyle){
8989                         return cs[camel];
8990                     }
8991                     return null;
8992                 };
8993         }(),
8994
8995         /**
8996          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8997          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8998          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8999          * @return {Roo.Element} this
9000          */
9001         setStyle : function(prop, value){
9002             if(typeof prop == "string"){
9003                 
9004                 if (prop == 'float') {
9005                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9006                     return this;
9007                 }
9008                 
9009                 var camel;
9010                 if(!(camel = propCache[prop])){
9011                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9012                 }
9013                 
9014                 if(camel == 'opacity') {
9015                     this.setOpacity(value);
9016                 }else{
9017                     this.dom.style[camel] = value;
9018                 }
9019             }else{
9020                 for(var style in prop){
9021                     if(typeof prop[style] != "function"){
9022                        this.setStyle(style, prop[style]);
9023                     }
9024                 }
9025             }
9026             return this;
9027         },
9028
9029         /**
9030          * More flexible version of {@link #setStyle} for setting style properties.
9031          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9032          * a function which returns such a specification.
9033          * @return {Roo.Element} this
9034          */
9035         applyStyles : function(style){
9036             Roo.DomHelper.applyStyles(this.dom, style);
9037             return this;
9038         },
9039
9040         /**
9041           * 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).
9042           * @return {Number} The X position of the element
9043           */
9044         getX : function(){
9045             return D.getX(this.dom);
9046         },
9047
9048         /**
9049           * 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).
9050           * @return {Number} The Y position of the element
9051           */
9052         getY : function(){
9053             return D.getY(this.dom);
9054         },
9055
9056         /**
9057           * 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).
9058           * @return {Array} The XY position of the element
9059           */
9060         getXY : function(){
9061             return D.getXY(this.dom);
9062         },
9063
9064         /**
9065          * 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).
9066          * @param {Number} The X position of the element
9067          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9068          * @return {Roo.Element} this
9069          */
9070         setX : function(x, animate){
9071             if(!animate || !A){
9072                 D.setX(this.dom, x);
9073             }else{
9074                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9075             }
9076             return this;
9077         },
9078
9079         /**
9080          * 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).
9081          * @param {Number} The Y position of the element
9082          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9083          * @return {Roo.Element} this
9084          */
9085         setY : function(y, animate){
9086             if(!animate || !A){
9087                 D.setY(this.dom, y);
9088             }else{
9089                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9090             }
9091             return this;
9092         },
9093
9094         /**
9095          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9096          * @param {String} left The left CSS property value
9097          * @return {Roo.Element} this
9098          */
9099         setLeft : function(left){
9100             this.setStyle("left", this.addUnits(left));
9101             return this;
9102         },
9103
9104         /**
9105          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9106          * @param {String} top The top CSS property value
9107          * @return {Roo.Element} this
9108          */
9109         setTop : function(top){
9110             this.setStyle("top", this.addUnits(top));
9111             return this;
9112         },
9113
9114         /**
9115          * Sets the element's CSS right style.
9116          * @param {String} right The right CSS property value
9117          * @return {Roo.Element} this
9118          */
9119         setRight : function(right){
9120             this.setStyle("right", this.addUnits(right));
9121             return this;
9122         },
9123
9124         /**
9125          * Sets the element's CSS bottom style.
9126          * @param {String} bottom The bottom CSS property value
9127          * @return {Roo.Element} this
9128          */
9129         setBottom : function(bottom){
9130             this.setStyle("bottom", this.addUnits(bottom));
9131             return this;
9132         },
9133
9134         /**
9135          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9136          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9137          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9138          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9139          * @return {Roo.Element} this
9140          */
9141         setXY : function(pos, animate){
9142             if(!animate || !A){
9143                 D.setXY(this.dom, pos);
9144             }else{
9145                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9146             }
9147             return this;
9148         },
9149
9150         /**
9151          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9152          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9153          * @param {Number} x X value for new position (coordinates are page-based)
9154          * @param {Number} y Y value for new position (coordinates are page-based)
9155          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9156          * @return {Roo.Element} this
9157          */
9158         setLocation : function(x, y, animate){
9159             this.setXY([x, y], this.preanim(arguments, 2));
9160             return this;
9161         },
9162
9163         /**
9164          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9165          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9166          * @param {Number} x X value for new position (coordinates are page-based)
9167          * @param {Number} y Y value for new position (coordinates are page-based)
9168          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9169          * @return {Roo.Element} this
9170          */
9171         moveTo : function(x, y, animate){
9172             this.setXY([x, y], this.preanim(arguments, 2));
9173             return this;
9174         },
9175
9176         /**
9177          * Returns the region of the given element.
9178          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9179          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9180          */
9181         getRegion : function(){
9182             return D.getRegion(this.dom);
9183         },
9184
9185         /**
9186          * Returns the offset height of the element
9187          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9188          * @return {Number} The element's height
9189          */
9190         getHeight : function(contentHeight){
9191             var h = this.dom.offsetHeight || 0;
9192             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9193         },
9194
9195         /**
9196          * Returns the offset width of the element
9197          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9198          * @return {Number} The element's width
9199          */
9200         getWidth : function(contentWidth){
9201             var w = this.dom.offsetWidth || 0;
9202             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9203         },
9204
9205         /**
9206          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9207          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9208          * if a height has not been set using CSS.
9209          * @return {Number}
9210          */
9211         getComputedHeight : function(){
9212             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9213             if(!h){
9214                 h = parseInt(this.getStyle('height'), 10) || 0;
9215                 if(!this.isBorderBox()){
9216                     h += this.getFrameWidth('tb');
9217                 }
9218             }
9219             return h;
9220         },
9221
9222         /**
9223          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9224          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9225          * if a width has not been set using CSS.
9226          * @return {Number}
9227          */
9228         getComputedWidth : function(){
9229             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9230             if(!w){
9231                 w = parseInt(this.getStyle('width'), 10) || 0;
9232                 if(!this.isBorderBox()){
9233                     w += this.getFrameWidth('lr');
9234                 }
9235             }
9236             return w;
9237         },
9238
9239         /**
9240          * Returns the size of the element.
9241          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9242          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9243          */
9244         getSize : function(contentSize){
9245             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9246         },
9247
9248         /**
9249          * Returns the width and height of the viewport.
9250          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9251          */
9252         getViewSize : function(){
9253             var d = this.dom, doc = document, aw = 0, ah = 0;
9254             if(d == doc || d == doc.body){
9255                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9256             }else{
9257                 return {
9258                     width : d.clientWidth,
9259                     height: d.clientHeight
9260                 };
9261             }
9262         },
9263
9264         /**
9265          * Returns the value of the "value" attribute
9266          * @param {Boolean} asNumber true to parse the value as a number
9267          * @return {String/Number}
9268          */
9269         getValue : function(asNumber){
9270             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9271         },
9272
9273         // private
9274         adjustWidth : function(width){
9275             if(typeof width == "number"){
9276                 if(this.autoBoxAdjust && !this.isBorderBox()){
9277                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9278                 }
9279                 if(width < 0){
9280                     width = 0;
9281                 }
9282             }
9283             return width;
9284         },
9285
9286         // private
9287         adjustHeight : function(height){
9288             if(typeof height == "number"){
9289                if(this.autoBoxAdjust && !this.isBorderBox()){
9290                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9291                }
9292                if(height < 0){
9293                    height = 0;
9294                }
9295             }
9296             return height;
9297         },
9298
9299         /**
9300          * Set the width of the element
9301          * @param {Number} width The new width
9302          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9303          * @return {Roo.Element} this
9304          */
9305         setWidth : function(width, animate){
9306             width = this.adjustWidth(width);
9307             if(!animate || !A){
9308                 this.dom.style.width = this.addUnits(width);
9309             }else{
9310                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9311             }
9312             return this;
9313         },
9314
9315         /**
9316          * Set the height of the element
9317          * @param {Number} height The new height
9318          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9319          * @return {Roo.Element} this
9320          */
9321          setHeight : function(height, animate){
9322             height = this.adjustHeight(height);
9323             if(!animate || !A){
9324                 this.dom.style.height = this.addUnits(height);
9325             }else{
9326                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9327             }
9328             return this;
9329         },
9330
9331         /**
9332          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
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          setSize : function(width, height, animate){
9339             if(typeof width == "object"){ // in case of object from getSize()
9340                 height = width.height; width = width.width;
9341             }
9342             width = this.adjustWidth(width); height = this.adjustHeight(height);
9343             if(!animate || !A){
9344                 this.dom.style.width = this.addUnits(width);
9345                 this.dom.style.height = this.addUnits(height);
9346             }else{
9347                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9348             }
9349             return this;
9350         },
9351
9352         /**
9353          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9354          * @param {Number} x X value for new position (coordinates are page-based)
9355          * @param {Number} y Y value for new position (coordinates are page-based)
9356          * @param {Number} width The new width
9357          * @param {Number} height The new height
9358          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9359          * @return {Roo.Element} this
9360          */
9361         setBounds : function(x, y, width, height, animate){
9362             if(!animate || !A){
9363                 this.setSize(width, height);
9364                 this.setLocation(x, y);
9365             }else{
9366                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9367                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9368                               this.preanim(arguments, 4), 'motion');
9369             }
9370             return this;
9371         },
9372
9373         /**
9374          * 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.
9375          * @param {Roo.lib.Region} region The region to fill
9376          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9377          * @return {Roo.Element} this
9378          */
9379         setRegion : function(region, animate){
9380             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9381             return this;
9382         },
9383
9384         /**
9385          * Appends an event handler
9386          *
9387          * @param {String}   eventName     The type of event to append
9388          * @param {Function} fn        The method the event invokes
9389          * @param {Object} scope       (optional) The scope (this object) of the fn
9390          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9391          */
9392         addListener : function(eventName, fn, scope, options)
9393         {
9394             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9395                 this.addListener('touchstart', this.onTapHandler, this);
9396             }
9397             
9398             // we need to handle a special case where dom element is a svg element.
9399             // in this case we do not actua
9400             if (!this.dom) {
9401                 return;
9402             }
9403             
9404             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9405                 if (typeof(this.listeners[eventName]) == 'undefined') {
9406                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9407                 }
9408                 this.listeners[eventName].addListener(fn, scope, options);
9409                 return;
9410             }
9411             
9412                 
9413             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9414             
9415             
9416         },
9417         tapedTwice : false,
9418         onTapHandler : function(event)
9419         {
9420             if(!this.tapedTwice) {
9421                 this.tapedTwice = true;
9422                 var s = this;
9423                 setTimeout( function() {
9424                     s.tapedTwice = false;
9425                 }, 300 );
9426                 return;
9427             }
9428             event.preventDefault();
9429             var revent = new MouseEvent('dblclick',  {
9430                 view: window,
9431                 bubbles: true,
9432                 cancelable: true
9433             });
9434              
9435             this.dom.dispatchEvent(revent);
9436             //action on double tap goes below
9437              
9438         }, 
9439  
9440         /**
9441          * Removes an event handler from this element
9442          * @param {String} eventName the type of event to remove
9443          * @param {Function} fn the method the event invokes
9444          * @param {Function} scope (needed for svg fake listeners)
9445          * @return {Roo.Element} this
9446          */
9447         removeListener : function(eventName, fn, scope){
9448             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9449             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9450                 return this;
9451             }
9452             this.listeners[eventName].removeListener(fn, scope);
9453             return this;
9454         },
9455
9456         /**
9457          * Removes all previous added listeners from this element
9458          * @return {Roo.Element} this
9459          */
9460         removeAllListeners : function(){
9461             E.purgeElement(this.dom);
9462             this.listeners = {};
9463             return this;
9464         },
9465
9466         relayEvent : function(eventName, observable){
9467             this.on(eventName, function(e){
9468                 observable.fireEvent(eventName, e);
9469             });
9470         },
9471
9472         
9473         /**
9474          * Set the opacity of the element
9475          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9476          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9477          * @return {Roo.Element} this
9478          */
9479          setOpacity : function(opacity, animate){
9480             if(!animate || !A){
9481                 var s = this.dom.style;
9482                 if(Roo.isIE){
9483                     s.zoom = 1;
9484                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9485                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9486                 }else{
9487                     s.opacity = opacity;
9488                 }
9489             }else{
9490                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9491             }
9492             return this;
9493         },
9494
9495         /**
9496          * Gets the left X coordinate
9497          * @param {Boolean} local True to get the local css position instead of page coordinate
9498          * @return {Number}
9499          */
9500         getLeft : function(local){
9501             if(!local){
9502                 return this.getX();
9503             }else{
9504                 return parseInt(this.getStyle("left"), 10) || 0;
9505             }
9506         },
9507
9508         /**
9509          * Gets the right X coordinate of the element (element X position + element width)
9510          * @param {Boolean} local True to get the local css position instead of page coordinate
9511          * @return {Number}
9512          */
9513         getRight : function(local){
9514             if(!local){
9515                 return this.getX() + this.getWidth();
9516             }else{
9517                 return (this.getLeft(true) + this.getWidth()) || 0;
9518             }
9519         },
9520
9521         /**
9522          * Gets the top Y coordinate
9523          * @param {Boolean} local True to get the local css position instead of page coordinate
9524          * @return {Number}
9525          */
9526         getTop : function(local) {
9527             if(!local){
9528                 return this.getY();
9529             }else{
9530                 return parseInt(this.getStyle("top"), 10) || 0;
9531             }
9532         },
9533
9534         /**
9535          * Gets the bottom Y coordinate of the element (element Y position + element height)
9536          * @param {Boolean} local True to get the local css position instead of page coordinate
9537          * @return {Number}
9538          */
9539         getBottom : function(local){
9540             if(!local){
9541                 return this.getY() + this.getHeight();
9542             }else{
9543                 return (this.getTop(true) + this.getHeight()) || 0;
9544             }
9545         },
9546
9547         /**
9548         * Initializes positioning on this element. If a desired position is not passed, it will make the
9549         * the element positioned relative IF it is not already positioned.
9550         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9551         * @param {Number} zIndex (optional) The zIndex to apply
9552         * @param {Number} x (optional) Set the page X position
9553         * @param {Number} y (optional) Set the page Y position
9554         */
9555         position : function(pos, zIndex, x, y){
9556             if(!pos){
9557                if(this.getStyle('position') == 'static'){
9558                    this.setStyle('position', 'relative');
9559                }
9560             }else{
9561                 this.setStyle("position", pos);
9562             }
9563             if(zIndex){
9564                 this.setStyle("z-index", zIndex);
9565             }
9566             if(x !== undefined && y !== undefined){
9567                 this.setXY([x, y]);
9568             }else if(x !== undefined){
9569                 this.setX(x);
9570             }else if(y !== undefined){
9571                 this.setY(y);
9572             }
9573         },
9574
9575         /**
9576         * Clear positioning back to the default when the document was loaded
9577         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9578         * @return {Roo.Element} this
9579          */
9580         clearPositioning : function(value){
9581             value = value ||'';
9582             this.setStyle({
9583                 "left": value,
9584                 "right": value,
9585                 "top": value,
9586                 "bottom": value,
9587                 "z-index": "",
9588                 "position" : "static"
9589             });
9590             return this;
9591         },
9592
9593         /**
9594         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9595         * snapshot before performing an update and then restoring the element.
9596         * @return {Object}
9597         */
9598         getPositioning : function(){
9599             var l = this.getStyle("left");
9600             var t = this.getStyle("top");
9601             return {
9602                 "position" : this.getStyle("position"),
9603                 "left" : l,
9604                 "right" : l ? "" : this.getStyle("right"),
9605                 "top" : t,
9606                 "bottom" : t ? "" : this.getStyle("bottom"),
9607                 "z-index" : this.getStyle("z-index")
9608             };
9609         },
9610
9611         /**
9612          * Gets the width of the border(s) for the specified side(s)
9613          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9614          * passing lr would get the border (l)eft width + the border (r)ight width.
9615          * @return {Number} The width of the sides passed added together
9616          */
9617         getBorderWidth : function(side){
9618             return this.addStyles(side, El.borders);
9619         },
9620
9621         /**
9622          * Gets the width of the padding(s) for the specified side(s)
9623          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9624          * passing lr would get the padding (l)eft + the padding (r)ight.
9625          * @return {Number} The padding of the sides passed added together
9626          */
9627         getPadding : function(side){
9628             return this.addStyles(side, El.paddings);
9629         },
9630
9631         /**
9632         * Set positioning with an object returned by getPositioning().
9633         * @param {Object} posCfg
9634         * @return {Roo.Element} this
9635          */
9636         setPositioning : function(pc){
9637             this.applyStyles(pc);
9638             if(pc.right == "auto"){
9639                 this.dom.style.right = "";
9640             }
9641             if(pc.bottom == "auto"){
9642                 this.dom.style.bottom = "";
9643             }
9644             return this;
9645         },
9646
9647         // private
9648         fixDisplay : function(){
9649             if(this.getStyle("display") == "none"){
9650                 this.setStyle("visibility", "hidden");
9651                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9652                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9653                     this.setStyle("display", "block");
9654                 }
9655             }
9656         },
9657
9658         /**
9659          * Quick set left and top adding default units
9660          * @param {String} left The left CSS property value
9661          * @param {String} top The top CSS property value
9662          * @return {Roo.Element} this
9663          */
9664          setLeftTop : function(left, top){
9665             this.dom.style.left = this.addUnits(left);
9666             this.dom.style.top = this.addUnits(top);
9667             return this;
9668         },
9669
9670         /**
9671          * Move this element relative to its current position.
9672          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9673          * @param {Number} distance How far to move the element in pixels
9674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9675          * @return {Roo.Element} this
9676          */
9677          move : function(direction, distance, animate){
9678             var xy = this.getXY();
9679             direction = direction.toLowerCase();
9680             switch(direction){
9681                 case "l":
9682                 case "left":
9683                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9684                     break;
9685                case "r":
9686                case "right":
9687                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9688                     break;
9689                case "t":
9690                case "top":
9691                case "up":
9692                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9693                     break;
9694                case "b":
9695                case "bottom":
9696                case "down":
9697                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9698                     break;
9699             }
9700             return this;
9701         },
9702
9703         /**
9704          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9705          * @return {Roo.Element} this
9706          */
9707         clip : function(){
9708             if(!this.isClipped){
9709                this.isClipped = true;
9710                this.originalClip = {
9711                    "o": this.getStyle("overflow"),
9712                    "x": this.getStyle("overflow-x"),
9713                    "y": this.getStyle("overflow-y")
9714                };
9715                this.setStyle("overflow", "hidden");
9716                this.setStyle("overflow-x", "hidden");
9717                this.setStyle("overflow-y", "hidden");
9718             }
9719             return this;
9720         },
9721
9722         /**
9723          *  Return clipping (overflow) to original clipping before clip() was called
9724          * @return {Roo.Element} this
9725          */
9726         unclip : function(){
9727             if(this.isClipped){
9728                 this.isClipped = false;
9729                 var o = this.originalClip;
9730                 if(o.o){this.setStyle("overflow", o.o);}
9731                 if(o.x){this.setStyle("overflow-x", o.x);}
9732                 if(o.y){this.setStyle("overflow-y", o.y);}
9733             }
9734             return this;
9735         },
9736
9737
9738         /**
9739          * Gets the x,y coordinates specified by the anchor position on the element.
9740          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9741          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9742          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9743          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9744          * @return {Array} [x, y] An array containing the element's x and y coordinates
9745          */
9746         getAnchorXY : function(anchor, local, s){
9747             //Passing a different size is useful for pre-calculating anchors,
9748             //especially for anchored animations that change the el size.
9749
9750             var w, h, vp = false;
9751             if(!s){
9752                 var d = this.dom;
9753                 if(d == document.body || d == document){
9754                     vp = true;
9755                     w = D.getViewWidth(); h = D.getViewHeight();
9756                 }else{
9757                     w = this.getWidth(); h = this.getHeight();
9758                 }
9759             }else{
9760                 w = s.width;  h = s.height;
9761             }
9762             var x = 0, y = 0, r = Math.round;
9763             switch((anchor || "tl").toLowerCase()){
9764                 case "c":
9765                     x = r(w*.5);
9766                     y = r(h*.5);
9767                 break;
9768                 case "t":
9769                     x = r(w*.5);
9770                     y = 0;
9771                 break;
9772                 case "l":
9773                     x = 0;
9774                     y = r(h*.5);
9775                 break;
9776                 case "r":
9777                     x = w;
9778                     y = r(h*.5);
9779                 break;
9780                 case "b":
9781                     x = r(w*.5);
9782                     y = h;
9783                 break;
9784                 case "tl":
9785                     x = 0;
9786                     y = 0;
9787                 break;
9788                 case "bl":
9789                     x = 0;
9790                     y = h;
9791                 break;
9792                 case "br":
9793                     x = w;
9794                     y = h;
9795                 break;
9796                 case "tr":
9797                     x = w;
9798                     y = 0;
9799                 break;
9800             }
9801             if(local === true){
9802                 return [x, y];
9803             }
9804             if(vp){
9805                 var sc = this.getScroll();
9806                 return [x + sc.left, y + sc.top];
9807             }
9808             //Add the element's offset xy
9809             var o = this.getXY();
9810             return [x+o[0], y+o[1]];
9811         },
9812
9813         /**
9814          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9815          * supported position values.
9816          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9817          * @param {String} position The position to align to.
9818          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9819          * @return {Array} [x, y]
9820          */
9821         getAlignToXY : function(el, p, o)
9822         {
9823             el = Roo.get(el);
9824             var d = this.dom;
9825             if(!el.dom){
9826                 throw "Element.alignTo with an element that doesn't exist";
9827             }
9828             var c = false; //constrain to viewport
9829             var p1 = "", p2 = "";
9830             o = o || [0,0];
9831
9832             if(!p){
9833                 p = "tl-bl";
9834             }else if(p == "?"){
9835                 p = "tl-bl?";
9836             }else if(p.indexOf("-") == -1){
9837                 p = "tl-" + p;
9838             }
9839             p = p.toLowerCase();
9840             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9841             if(!m){
9842                throw "Element.alignTo with an invalid alignment " + p;
9843             }
9844             p1 = m[1]; p2 = m[2]; c = !!m[3];
9845
9846             //Subtract the aligned el's internal xy from the target's offset xy
9847             //plus custom offset to get the aligned el's new offset xy
9848             var a1 = this.getAnchorXY(p1, true);
9849             var a2 = el.getAnchorXY(p2, false);
9850             var x = a2[0] - a1[0] + o[0];
9851             var y = a2[1] - a1[1] + o[1];
9852             if(c){
9853                 //constrain the aligned el to viewport if necessary
9854                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9855                 // 5px of margin for ie
9856                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9857
9858                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9859                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9860                 //otherwise swap the aligned el to the opposite border of the target.
9861                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9862                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9863                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9864                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9865
9866                var doc = document;
9867                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9868                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9869
9870                if((x+w) > dw + scrollX){
9871                     x = swapX ? r.left-w : dw+scrollX-w;
9872                 }
9873                if(x < scrollX){
9874                    x = swapX ? r.right : scrollX;
9875                }
9876                if((y+h) > dh + scrollY){
9877                     y = swapY ? r.top-h : dh+scrollY-h;
9878                 }
9879                if (y < scrollY){
9880                    y = swapY ? r.bottom : scrollY;
9881                }
9882             }
9883             return [x,y];
9884         },
9885
9886         // private
9887         getConstrainToXY : function(){
9888             var os = {top:0, left:0, bottom:0, right: 0};
9889
9890             return function(el, local, offsets, proposedXY){
9891                 el = Roo.get(el);
9892                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9893
9894                 var vw, vh, vx = 0, vy = 0;
9895                 if(el.dom == document.body || el.dom == document){
9896                     vw = Roo.lib.Dom.getViewWidth();
9897                     vh = Roo.lib.Dom.getViewHeight();
9898                 }else{
9899                     vw = el.dom.clientWidth;
9900                     vh = el.dom.clientHeight;
9901                     if(!local){
9902                         var vxy = el.getXY();
9903                         vx = vxy[0];
9904                         vy = vxy[1];
9905                     }
9906                 }
9907
9908                 var s = el.getScroll();
9909
9910                 vx += offsets.left + s.left;
9911                 vy += offsets.top + s.top;
9912
9913                 vw -= offsets.right;
9914                 vh -= offsets.bottom;
9915
9916                 var vr = vx+vw;
9917                 var vb = vy+vh;
9918
9919                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9920                 var x = xy[0], y = xy[1];
9921                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9922
9923                 // only move it if it needs it
9924                 var moved = false;
9925
9926                 // first validate right/bottom
9927                 if((x + w) > vr){
9928                     x = vr - w;
9929                     moved = true;
9930                 }
9931                 if((y + h) > vb){
9932                     y = vb - h;
9933                     moved = true;
9934                 }
9935                 // then make sure top/left isn't negative
9936                 if(x < vx){
9937                     x = vx;
9938                     moved = true;
9939                 }
9940                 if(y < vy){
9941                     y = vy;
9942                     moved = true;
9943                 }
9944                 return moved ? [x, y] : false;
9945             };
9946         }(),
9947
9948         // private
9949         adjustForConstraints : function(xy, parent, offsets){
9950             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9951         },
9952
9953         /**
9954          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9955          * document it aligns it to the viewport.
9956          * The position parameter is optional, and can be specified in any one of the following formats:
9957          * <ul>
9958          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9959          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9960          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9961          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9962          *   <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
9963          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9964          * </ul>
9965          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9966          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9967          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9968          * that specified in order to enforce the viewport constraints.
9969          * Following are all of the supported anchor positions:
9970     <pre>
9971     Value  Description
9972     -----  -----------------------------
9973     tl     The top left corner (default)
9974     t      The center of the top edge
9975     tr     The top right corner
9976     l      The center of the left edge
9977     c      In the center of the element
9978     r      The center of the right edge
9979     bl     The bottom left corner
9980     b      The center of the bottom edge
9981     br     The bottom right corner
9982     </pre>
9983     Example Usage:
9984     <pre><code>
9985     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9986     el.alignTo("other-el");
9987
9988     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9989     el.alignTo("other-el", "tr?");
9990
9991     // align the bottom right corner of el with the center left edge of other-el
9992     el.alignTo("other-el", "br-l?");
9993
9994     // align the center of el with the bottom left corner of other-el and
9995     // adjust the x position by -6 pixels (and the y position by 0)
9996     el.alignTo("other-el", "c-bl", [-6, 0]);
9997     </code></pre>
9998          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9999          * @param {String} position The position to align to.
10000          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10001          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10002          * @return {Roo.Element} this
10003          */
10004         alignTo : function(element, position, offsets, animate){
10005             var xy = this.getAlignToXY(element, position, offsets);
10006             this.setXY(xy, this.preanim(arguments, 3));
10007             return this;
10008         },
10009
10010         /**
10011          * Anchors an element to another element and realigns it when the window is resized.
10012          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10013          * @param {String} position The position to align to.
10014          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10015          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10016          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10017          * is a number, it is used as the buffer delay (defaults to 50ms).
10018          * @param {Function} callback The function to call after the animation finishes
10019          * @return {Roo.Element} this
10020          */
10021         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10022             var action = function(){
10023                 this.alignTo(el, alignment, offsets, animate);
10024                 Roo.callback(callback, this);
10025             };
10026             Roo.EventManager.onWindowResize(action, this);
10027             var tm = typeof monitorScroll;
10028             if(tm != 'undefined'){
10029                 Roo.EventManager.on(window, 'scroll', action, this,
10030                     {buffer: tm == 'number' ? monitorScroll : 50});
10031             }
10032             action.call(this); // align immediately
10033             return this;
10034         },
10035         /**
10036          * Clears any opacity settings from this element. Required in some cases for IE.
10037          * @return {Roo.Element} this
10038          */
10039         clearOpacity : function(){
10040             if (window.ActiveXObject) {
10041                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10042                     this.dom.style.filter = "";
10043                 }
10044             } else {
10045                 this.dom.style.opacity = "";
10046                 this.dom.style["-moz-opacity"] = "";
10047                 this.dom.style["-khtml-opacity"] = "";
10048             }
10049             return this;
10050         },
10051
10052         /**
10053          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10054          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10055          * @return {Roo.Element} this
10056          */
10057         hide : function(animate){
10058             this.setVisible(false, this.preanim(arguments, 0));
10059             return this;
10060         },
10061
10062         /**
10063         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10064         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10065          * @return {Roo.Element} this
10066          */
10067         show : function(animate){
10068             this.setVisible(true, this.preanim(arguments, 0));
10069             return this;
10070         },
10071
10072         /**
10073          * @private Test if size has a unit, otherwise appends the default
10074          */
10075         addUnits : function(size){
10076             return Roo.Element.addUnits(size, this.defaultUnit);
10077         },
10078
10079         /**
10080          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10081          * @return {Roo.Element} this
10082          */
10083         beginMeasure : function(){
10084             var el = this.dom;
10085             if(el.offsetWidth || el.offsetHeight){
10086                 return this; // offsets work already
10087             }
10088             var changed = [];
10089             var p = this.dom, b = document.body; // start with this element
10090             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10091                 var pe = Roo.get(p);
10092                 if(pe.getStyle('display') == 'none'){
10093                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10094                     p.style.visibility = "hidden";
10095                     p.style.display = "block";
10096                 }
10097                 p = p.parentNode;
10098             }
10099             this._measureChanged = changed;
10100             return this;
10101
10102         },
10103
10104         /**
10105          * Restores displays to before beginMeasure was called
10106          * @return {Roo.Element} this
10107          */
10108         endMeasure : function(){
10109             var changed = this._measureChanged;
10110             if(changed){
10111                 for(var i = 0, len = changed.length; i < len; i++) {
10112                     var r = changed[i];
10113                     r.el.style.visibility = r.visibility;
10114                     r.el.style.display = "none";
10115                 }
10116                 this._measureChanged = null;
10117             }
10118             return this;
10119         },
10120
10121         /**
10122         * Update the innerHTML of this element, optionally searching for and processing scripts
10123         * @param {String} html The new HTML
10124         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10125         * @param {Function} callback For async script loading you can be noticed when the update completes
10126         * @return {Roo.Element} this
10127          */
10128         update : function(html, loadScripts, callback){
10129             if(typeof html == "undefined"){
10130                 html = "";
10131             }
10132             if(loadScripts !== true){
10133                 this.dom.innerHTML = html;
10134                 if(typeof callback == "function"){
10135                     callback();
10136                 }
10137                 return this;
10138             }
10139             var id = Roo.id();
10140             var dom = this.dom;
10141
10142             html += '<span id="' + id + '"></span>';
10143
10144             E.onAvailable(id, function(){
10145                 var hd = document.getElementsByTagName("head")[0];
10146                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10147                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10148                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10149
10150                 var match;
10151                 while(match = re.exec(html)){
10152                     var attrs = match[1];
10153                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10154                     if(srcMatch && srcMatch[2]){
10155                        var s = document.createElement("script");
10156                        s.src = srcMatch[2];
10157                        var typeMatch = attrs.match(typeRe);
10158                        if(typeMatch && typeMatch[2]){
10159                            s.type = typeMatch[2];
10160                        }
10161                        hd.appendChild(s);
10162                     }else if(match[2] && match[2].length > 0){
10163                         if(window.execScript) {
10164                            window.execScript(match[2]);
10165                         } else {
10166                             /**
10167                              * eval:var:id
10168                              * eval:var:dom
10169                              * eval:var:html
10170                              * 
10171                              */
10172                            window.eval(match[2]);
10173                         }
10174                     }
10175                 }
10176                 var el = document.getElementById(id);
10177                 if(el){el.parentNode.removeChild(el);}
10178                 if(typeof callback == "function"){
10179                     callback();
10180                 }
10181             });
10182             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10183             return this;
10184         },
10185
10186         /**
10187          * Direct access to the UpdateManager update() method (takes the same parameters).
10188          * @param {String/Function} url The url for this request or a function to call to get the url
10189          * @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}
10190          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10191          * @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.
10192          * @return {Roo.Element} this
10193          */
10194         load : function(){
10195             var um = this.getUpdateManager();
10196             um.update.apply(um, arguments);
10197             return this;
10198         },
10199
10200         /**
10201         * Gets this element's UpdateManager
10202         * @return {Roo.UpdateManager} The UpdateManager
10203         */
10204         getUpdateManager : function(){
10205             if(!this.updateManager){
10206                 this.updateManager = new Roo.UpdateManager(this);
10207             }
10208             return this.updateManager;
10209         },
10210
10211         /**
10212          * Disables text selection for this element (normalized across browsers)
10213          * @return {Roo.Element} this
10214          */
10215         unselectable : function(){
10216             this.dom.unselectable = "on";
10217             this.swallowEvent("selectstart", true);
10218             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10219             this.addClass("x-unselectable");
10220             return this;
10221         },
10222
10223         /**
10224         * Calculates the x, y to center this element on the screen
10225         * @return {Array} The x, y values [x, y]
10226         */
10227         getCenterXY : function(){
10228             return this.getAlignToXY(document, 'c-c');
10229         },
10230
10231         /**
10232         * Centers the Element in either the viewport, or another Element.
10233         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10234         */
10235         center : function(centerIn){
10236             this.alignTo(centerIn || document, 'c-c');
10237             return this;
10238         },
10239
10240         /**
10241          * Tests various css rules/browsers to determine if this element uses a border box
10242          * @return {Boolean}
10243          */
10244         isBorderBox : function(){
10245             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10246         },
10247
10248         /**
10249          * Return a box {x, y, width, height} that can be used to set another elements
10250          * size/location to match this element.
10251          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10252          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10253          * @return {Object} box An object in the format {x, y, width, height}
10254          */
10255         getBox : function(contentBox, local){
10256             var xy;
10257             if(!local){
10258                 xy = this.getXY();
10259             }else{
10260                 var left = parseInt(this.getStyle("left"), 10) || 0;
10261                 var top = parseInt(this.getStyle("top"), 10) || 0;
10262                 xy = [left, top];
10263             }
10264             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10265             if(!contentBox){
10266                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10267             }else{
10268                 var l = this.getBorderWidth("l")+this.getPadding("l");
10269                 var r = this.getBorderWidth("r")+this.getPadding("r");
10270                 var t = this.getBorderWidth("t")+this.getPadding("t");
10271                 var b = this.getBorderWidth("b")+this.getPadding("b");
10272                 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)};
10273             }
10274             bx.right = bx.x + bx.width;
10275             bx.bottom = bx.y + bx.height;
10276             return bx;
10277         },
10278
10279         /**
10280          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10281          for more information about the sides.
10282          * @param {String} sides
10283          * @return {Number}
10284          */
10285         getFrameWidth : function(sides, onlyContentBox){
10286             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10287         },
10288
10289         /**
10290          * 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.
10291          * @param {Object} box The box to fill {x, y, width, height}
10292          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10294          * @return {Roo.Element} this
10295          */
10296         setBox : function(box, adjust, animate){
10297             var w = box.width, h = box.height;
10298             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10299                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10300                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10301             }
10302             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10303             return this;
10304         },
10305
10306         /**
10307          * Forces the browser to repaint this element
10308          * @return {Roo.Element} this
10309          */
10310          repaint : function(){
10311             var dom = this.dom;
10312             this.addClass("x-repaint");
10313             setTimeout(function(){
10314                 Roo.get(dom).removeClass("x-repaint");
10315             }, 1);
10316             return this;
10317         },
10318
10319         /**
10320          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10321          * then it returns the calculated width of the sides (see getPadding)
10322          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10323          * @return {Object/Number}
10324          */
10325         getMargins : function(side){
10326             if(!side){
10327                 return {
10328                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10329                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10330                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10331                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10332                 };
10333             }else{
10334                 return this.addStyles(side, El.margins);
10335              }
10336         },
10337
10338         // private
10339         addStyles : function(sides, styles){
10340             var val = 0, v, w;
10341             for(var i = 0, len = sides.length; i < len; i++){
10342                 v = this.getStyle(styles[sides.charAt(i)]);
10343                 if(v){
10344                      w = parseInt(v, 10);
10345                      if(w){ val += w; }
10346                 }
10347             }
10348             return val;
10349         },
10350
10351         /**
10352          * Creates a proxy element of this element
10353          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10354          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10355          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10356          * @return {Roo.Element} The new proxy element
10357          */
10358         createProxy : function(config, renderTo, matchBox){
10359             if(renderTo){
10360                 renderTo = Roo.getDom(renderTo);
10361             }else{
10362                 renderTo = document.body;
10363             }
10364             config = typeof config == "object" ?
10365                 config : {tag : "div", cls: config};
10366             var proxy = Roo.DomHelper.append(renderTo, config, true);
10367             if(matchBox){
10368                proxy.setBox(this.getBox());
10369             }
10370             return proxy;
10371         },
10372
10373         /**
10374          * Puts a mask over this element to disable user interaction. Requires core.css.
10375          * This method can only be applied to elements which accept child nodes.
10376          * @param {String} msg (optional) A message to display in the mask
10377          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10378          * @return {Element} The mask  element
10379          */
10380         mask : function(msg, msgCls)
10381         {
10382             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10383                 this.setStyle("position", "relative");
10384             }
10385             if(!this._mask){
10386                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10387             }
10388             
10389             this.addClass("x-masked");
10390             this._mask.setDisplayed(true);
10391             
10392             // we wander
10393             var z = 0;
10394             var dom = this.dom;
10395             while (dom && dom.style) {
10396                 if (!isNaN(parseInt(dom.style.zIndex))) {
10397                     z = Math.max(z, parseInt(dom.style.zIndex));
10398                 }
10399                 dom = dom.parentNode;
10400             }
10401             // if we are masking the body - then it hides everything..
10402             if (this.dom == document.body) {
10403                 z = 1000000;
10404                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10405                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10406             }
10407            
10408             if(typeof msg == 'string'){
10409                 if(!this._maskMsg){
10410                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10411                         cls: "roo-el-mask-msg", 
10412                         cn: [
10413                             {
10414                                 tag: 'i',
10415                                 cls: 'fa fa-spinner fa-spin'
10416                             },
10417                             {
10418                                 tag: 'div'
10419                             }   
10420                         ]
10421                     }, true);
10422                 }
10423                 var mm = this._maskMsg;
10424                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10425                 if (mm.dom.lastChild) { // weird IE issue?
10426                     mm.dom.lastChild.innerHTML = msg;
10427                 }
10428                 mm.setDisplayed(true);
10429                 mm.center(this);
10430                 mm.setStyle('z-index', z + 102);
10431             }
10432             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10433                 this._mask.setHeight(this.getHeight());
10434             }
10435             this._mask.setStyle('z-index', z + 100);
10436             
10437             return this._mask;
10438         },
10439
10440         /**
10441          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10442          * it is cached for reuse.
10443          */
10444         unmask : function(removeEl){
10445             if(this._mask){
10446                 if(removeEl === true){
10447                     this._mask.remove();
10448                     delete this._mask;
10449                     if(this._maskMsg){
10450                         this._maskMsg.remove();
10451                         delete this._maskMsg;
10452                     }
10453                 }else{
10454                     this._mask.setDisplayed(false);
10455                     if(this._maskMsg){
10456                         this._maskMsg.setDisplayed(false);
10457                     }
10458                 }
10459             }
10460             this.removeClass("x-masked");
10461         },
10462
10463         /**
10464          * Returns true if this element is masked
10465          * @return {Boolean}
10466          */
10467         isMasked : function(){
10468             return this._mask && this._mask.isVisible();
10469         },
10470
10471         /**
10472          * Creates an iframe shim for this element to keep selects and other windowed objects from
10473          * showing through.
10474          * @return {Roo.Element} The new shim element
10475          */
10476         createShim : function(){
10477             var el = document.createElement('iframe');
10478             el.frameBorder = 'no';
10479             el.className = 'roo-shim';
10480             if(Roo.isIE && Roo.isSecure){
10481                 el.src = Roo.SSL_SECURE_URL;
10482             }
10483             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10484             shim.autoBoxAdjust = false;
10485             return shim;
10486         },
10487
10488         /**
10489          * Removes this element from the DOM and deletes it from the cache
10490          */
10491         remove : function(){
10492             if(this.dom.parentNode){
10493                 this.dom.parentNode.removeChild(this.dom);
10494             }
10495             delete El.cache[this.dom.id];
10496         },
10497
10498         /**
10499          * Sets up event handlers to add and remove a css class when the mouse is over this element
10500          * @param {String} className
10501          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10502          * mouseout events for children elements
10503          * @return {Roo.Element} this
10504          */
10505         addClassOnOver : function(className, preventFlicker){
10506             this.on("mouseover", function(){
10507                 Roo.fly(this, '_internal').addClass(className);
10508             }, this.dom);
10509             var removeFn = function(e){
10510                 if(preventFlicker !== true || !e.within(this, true)){
10511                     Roo.fly(this, '_internal').removeClass(className);
10512                 }
10513             };
10514             this.on("mouseout", removeFn, this.dom);
10515             return this;
10516         },
10517
10518         /**
10519          * Sets up event handlers to add and remove a css class when this element has the focus
10520          * @param {String} className
10521          * @return {Roo.Element} this
10522          */
10523         addClassOnFocus : function(className){
10524             this.on("focus", function(){
10525                 Roo.fly(this, '_internal').addClass(className);
10526             }, this.dom);
10527             this.on("blur", function(){
10528                 Roo.fly(this, '_internal').removeClass(className);
10529             }, this.dom);
10530             return this;
10531         },
10532         /**
10533          * 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)
10534          * @param {String} className
10535          * @return {Roo.Element} this
10536          */
10537         addClassOnClick : function(className){
10538             var dom = this.dom;
10539             this.on("mousedown", function(){
10540                 Roo.fly(dom, '_internal').addClass(className);
10541                 var d = Roo.get(document);
10542                 var fn = function(){
10543                     Roo.fly(dom, '_internal').removeClass(className);
10544                     d.removeListener("mouseup", fn);
10545                 };
10546                 d.on("mouseup", fn);
10547             });
10548             return this;
10549         },
10550
10551         /**
10552          * Stops the specified event from bubbling and optionally prevents the default action
10553          * @param {String} eventName
10554          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10555          * @return {Roo.Element} this
10556          */
10557         swallowEvent : function(eventName, preventDefault){
10558             var fn = function(e){
10559                 e.stopPropagation();
10560                 if(preventDefault){
10561                     e.preventDefault();
10562                 }
10563             };
10564             if(eventName instanceof Array){
10565                 for(var i = 0, len = eventName.length; i < len; i++){
10566                      this.on(eventName[i], fn);
10567                 }
10568                 return this;
10569             }
10570             this.on(eventName, fn);
10571             return this;
10572         },
10573
10574         /**
10575          * @private
10576          */
10577         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10578
10579         /**
10580          * Sizes this element to its parent element's dimensions performing
10581          * neccessary box adjustments.
10582          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10583          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10584          * @return {Roo.Element} this
10585          */
10586         fitToParent : function(monitorResize, targetParent) {
10587           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10588           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10589           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10590             return this;
10591           }
10592           var p = Roo.get(targetParent || this.dom.parentNode);
10593           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10594           if (monitorResize === true) {
10595             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10596             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10597           }
10598           return this;
10599         },
10600
10601         /**
10602          * Gets the next sibling, skipping text nodes
10603          * @return {HTMLElement} The next sibling or null
10604          */
10605         getNextSibling : function(){
10606             var n = this.dom.nextSibling;
10607             while(n && n.nodeType != 1){
10608                 n = n.nextSibling;
10609             }
10610             return n;
10611         },
10612
10613         /**
10614          * Gets the previous sibling, skipping text nodes
10615          * @return {HTMLElement} The previous sibling or null
10616          */
10617         getPrevSibling : function(){
10618             var n = this.dom.previousSibling;
10619             while(n && n.nodeType != 1){
10620                 n = n.previousSibling;
10621             }
10622             return n;
10623         },
10624
10625
10626         /**
10627          * Appends the passed element(s) to this element
10628          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10629          * @return {Roo.Element} this
10630          */
10631         appendChild: function(el){
10632             el = Roo.get(el);
10633             el.appendTo(this);
10634             return this;
10635         },
10636
10637         /**
10638          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10639          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10640          * automatically generated with the specified attributes.
10641          * @param {HTMLElement} insertBefore (optional) a child element of this element
10642          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10643          * @return {Roo.Element} The new child element
10644          */
10645         createChild: function(config, insertBefore, returnDom){
10646             config = config || {tag:'div'};
10647             if(insertBefore){
10648                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10649             }
10650             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10651         },
10652
10653         /**
10654          * Appends this element to the passed element
10655          * @param {String/HTMLElement/Element} el The new parent element
10656          * @return {Roo.Element} this
10657          */
10658         appendTo: function(el){
10659             el = Roo.getDom(el);
10660             el.appendChild(this.dom);
10661             return this;
10662         },
10663
10664         /**
10665          * Inserts this element before the passed element in the DOM
10666          * @param {String/HTMLElement/Element} el The element to insert before
10667          * @return {Roo.Element} this
10668          */
10669         insertBefore: function(el){
10670             el = Roo.getDom(el);
10671             el.parentNode.insertBefore(this.dom, el);
10672             return this;
10673         },
10674
10675         /**
10676          * Inserts this element after the passed element in the DOM
10677          * @param {String/HTMLElement/Element} el The element to insert after
10678          * @return {Roo.Element} this
10679          */
10680         insertAfter: function(el){
10681             el = Roo.getDom(el);
10682             el.parentNode.insertBefore(this.dom, el.nextSibling);
10683             return this;
10684         },
10685
10686         /**
10687          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10688          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10689          * @return {Roo.Element} The new child
10690          */
10691         insertFirst: function(el, returnDom){
10692             el = el || {};
10693             if(typeof el == 'object' && !el.nodeType){ // dh config
10694                 return this.createChild(el, this.dom.firstChild, returnDom);
10695             }else{
10696                 el = Roo.getDom(el);
10697                 this.dom.insertBefore(el, this.dom.firstChild);
10698                 return !returnDom ? Roo.get(el) : el;
10699             }
10700         },
10701
10702         /**
10703          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10704          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10705          * @param {String} where (optional) 'before' or 'after' defaults to before
10706          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10707          * @return {Roo.Element} the inserted Element
10708          */
10709         insertSibling: function(el, where, returnDom){
10710             where = where ? where.toLowerCase() : 'before';
10711             el = el || {};
10712             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10713
10714             if(typeof el == 'object' && !el.nodeType){ // dh config
10715                 if(where == 'after' && !this.dom.nextSibling){
10716                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10717                 }else{
10718                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10719                 }
10720
10721             }else{
10722                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10723                             where == 'before' ? this.dom : this.dom.nextSibling);
10724                 if(!returnDom){
10725                     rt = Roo.get(rt);
10726                 }
10727             }
10728             return rt;
10729         },
10730
10731         /**
10732          * Creates and wraps this element with another element
10733          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10734          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10735          * @return {HTMLElement/Element} The newly created wrapper element
10736          */
10737         wrap: function(config, returnDom){
10738             if(!config){
10739                 config = {tag: "div"};
10740             }
10741             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10742             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10743             return newEl;
10744         },
10745
10746         /**
10747          * Replaces the passed element with this element
10748          * @param {String/HTMLElement/Element} el The element to replace
10749          * @return {Roo.Element} this
10750          */
10751         replace: function(el){
10752             el = Roo.get(el);
10753             this.insertBefore(el);
10754             el.remove();
10755             return this;
10756         },
10757
10758         /**
10759          * Inserts an html fragment into this element
10760          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10761          * @param {String} html The HTML fragment
10762          * @param {Boolean} returnEl True to return an Roo.Element
10763          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10764          */
10765         insertHtml : function(where, html, returnEl){
10766             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10767             return returnEl ? Roo.get(el) : el;
10768         },
10769
10770         /**
10771          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10772          * @param {Object} o The object with the attributes
10773          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10774          * @return {Roo.Element} this
10775          */
10776         set : function(o, useSet){
10777             var el = this.dom;
10778             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10779             for(var attr in o){
10780                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10781                 if(attr=="cls"){
10782                     el.className = o["cls"];
10783                 }else{
10784                     if(useSet) {
10785                         el.setAttribute(attr, o[attr]);
10786                     } else {
10787                         el[attr] = o[attr];
10788                     }
10789                 }
10790             }
10791             if(o.style){
10792                 Roo.DomHelper.applyStyles(el, o.style);
10793             }
10794             return this;
10795         },
10796
10797         /**
10798          * Convenience method for constructing a KeyMap
10799          * @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:
10800          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10801          * @param {Function} fn The function to call
10802          * @param {Object} scope (optional) The scope of the function
10803          * @return {Roo.KeyMap} The KeyMap created
10804          */
10805         addKeyListener : function(key, fn, scope){
10806             var config;
10807             if(typeof key != "object" || key instanceof Array){
10808                 config = {
10809                     key: key,
10810                     fn: fn,
10811                     scope: scope
10812                 };
10813             }else{
10814                 config = {
10815                     key : key.key,
10816                     shift : key.shift,
10817                     ctrl : key.ctrl,
10818                     alt : key.alt,
10819                     fn: fn,
10820                     scope: scope
10821                 };
10822             }
10823             return new Roo.KeyMap(this, config);
10824         },
10825
10826         /**
10827          * Creates a KeyMap for this element
10828          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10829          * @return {Roo.KeyMap} The KeyMap created
10830          */
10831         addKeyMap : function(config){
10832             return new Roo.KeyMap(this, config);
10833         },
10834
10835         /**
10836          * Returns true if this element is scrollable.
10837          * @return {Boolean}
10838          */
10839          isScrollable : function(){
10840             var dom = this.dom;
10841             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10842         },
10843
10844         /**
10845          * 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().
10846          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10847          * @param {Number} value The new scroll value
10848          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10849          * @return {Element} this
10850          */
10851
10852         scrollTo : function(side, value, animate){
10853             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10854             if(!animate || !A){
10855                 this.dom[prop] = value;
10856             }else{
10857                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10858                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10859             }
10860             return this;
10861         },
10862
10863         /**
10864          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10865          * within this element's scrollable range.
10866          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10867          * @param {Number} distance How far to scroll the element in pixels
10868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10869          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10870          * was scrolled as far as it could go.
10871          */
10872          scroll : function(direction, distance, animate){
10873              if(!this.isScrollable()){
10874                  return;
10875              }
10876              var el = this.dom;
10877              var l = el.scrollLeft, t = el.scrollTop;
10878              var w = el.scrollWidth, h = el.scrollHeight;
10879              var cw = el.clientWidth, ch = el.clientHeight;
10880              direction = direction.toLowerCase();
10881              var scrolled = false;
10882              var a = this.preanim(arguments, 2);
10883              switch(direction){
10884                  case "l":
10885                  case "left":
10886                      if(w - l > cw){
10887                          var v = Math.min(l + distance, w-cw);
10888                          this.scrollTo("left", v, a);
10889                          scrolled = true;
10890                      }
10891                      break;
10892                 case "r":
10893                 case "right":
10894                      if(l > 0){
10895                          var v = Math.max(l - distance, 0);
10896                          this.scrollTo("left", v, a);
10897                          scrolled = true;
10898                      }
10899                      break;
10900                 case "t":
10901                 case "top":
10902                 case "up":
10903                      if(t > 0){
10904                          var v = Math.max(t - distance, 0);
10905                          this.scrollTo("top", v, a);
10906                          scrolled = true;
10907                      }
10908                      break;
10909                 case "b":
10910                 case "bottom":
10911                 case "down":
10912                      if(h - t > ch){
10913                          var v = Math.min(t + distance, h-ch);
10914                          this.scrollTo("top", v, a);
10915                          scrolled = true;
10916                      }
10917                      break;
10918              }
10919              return scrolled;
10920         },
10921
10922         /**
10923          * Translates the passed page coordinates into left/top css values for this element
10924          * @param {Number/Array} x The page x or an array containing [x, y]
10925          * @param {Number} y The page y
10926          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10927          */
10928         translatePoints : function(x, y){
10929             if(typeof x == 'object' || x instanceof Array){
10930                 y = x[1]; x = x[0];
10931             }
10932             var p = this.getStyle('position');
10933             var o = this.getXY();
10934
10935             var l = parseInt(this.getStyle('left'), 10);
10936             var t = parseInt(this.getStyle('top'), 10);
10937
10938             if(isNaN(l)){
10939                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10940             }
10941             if(isNaN(t)){
10942                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10943             }
10944
10945             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10946         },
10947
10948         /**
10949          * Returns the current scroll position of the element.
10950          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10951          */
10952         getScroll : function(){
10953             var d = this.dom, doc = document;
10954             if(d == doc || d == doc.body){
10955                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10956                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10957                 return {left: l, top: t};
10958             }else{
10959                 return {left: d.scrollLeft, top: d.scrollTop};
10960             }
10961         },
10962
10963         /**
10964          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10965          * are convert to standard 6 digit hex color.
10966          * @param {String} attr The css attribute
10967          * @param {String} defaultValue The default value to use when a valid color isn't found
10968          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10969          * YUI color anims.
10970          */
10971         getColor : function(attr, defaultValue, prefix){
10972             var v = this.getStyle(attr);
10973             if(!v || v == "transparent" || v == "inherit") {
10974                 return defaultValue;
10975             }
10976             var color = typeof prefix == "undefined" ? "#" : prefix;
10977             if(v.substr(0, 4) == "rgb("){
10978                 var rvs = v.slice(4, v.length -1).split(",");
10979                 for(var i = 0; i < 3; i++){
10980                     var h = parseInt(rvs[i]).toString(16);
10981                     if(h < 16){
10982                         h = "0" + h;
10983                     }
10984                     color += h;
10985                 }
10986             } else {
10987                 if(v.substr(0, 1) == "#"){
10988                     if(v.length == 4) {
10989                         for(var i = 1; i < 4; i++){
10990                             var c = v.charAt(i);
10991                             color +=  c + c;
10992                         }
10993                     }else if(v.length == 7){
10994                         color += v.substr(1);
10995                     }
10996                 }
10997             }
10998             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10999         },
11000
11001         /**
11002          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11003          * gradient background, rounded corners and a 4-way shadow.
11004          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11005          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11006          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11007          * @return {Roo.Element} this
11008          */
11009         boxWrap : function(cls){
11010             cls = cls || 'x-box';
11011             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11012             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11013             return el;
11014         },
11015
11016         /**
11017          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11018          * @param {String} namespace The namespace in which to look for the attribute
11019          * @param {String} name The attribute name
11020          * @return {String} The attribute value
11021          */
11022         getAttributeNS : Roo.isIE ? function(ns, name){
11023             var d = this.dom;
11024             var type = typeof d[ns+":"+name];
11025             if(type != 'undefined' && type != 'unknown'){
11026                 return d[ns+":"+name];
11027             }
11028             return d[name];
11029         } : function(ns, name){
11030             var d = this.dom;
11031             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11032         },
11033         
11034         
11035         /**
11036          * Sets or Returns the value the dom attribute value
11037          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11038          * @param {String} value (optional) The value to set the attribute to
11039          * @return {String} The attribute value
11040          */
11041         attr : function(name){
11042             if (arguments.length > 1) {
11043                 this.dom.setAttribute(name, arguments[1]);
11044                 return arguments[1];
11045             }
11046             if (typeof(name) == 'object') {
11047                 for(var i in name) {
11048                     this.attr(i, name[i]);
11049                 }
11050                 return name;
11051             }
11052             
11053             
11054             if (!this.dom.hasAttribute(name)) {
11055                 return undefined;
11056             }
11057             return this.dom.getAttribute(name);
11058         }
11059         
11060         
11061         
11062     };
11063
11064     var ep = El.prototype;
11065
11066     /**
11067      * Appends an event handler (Shorthand for addListener)
11068      * @param {String}   eventName     The type of event to append
11069      * @param {Function} fn        The method the event invokes
11070      * @param {Object} scope       (optional) The scope (this object) of the fn
11071      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11072      * @method
11073      */
11074     ep.on = ep.addListener;
11075         // backwards compat
11076     ep.mon = ep.addListener;
11077
11078     /**
11079      * Removes an event handler from this element (shorthand for removeListener)
11080      * @param {String} eventName the type of event to remove
11081      * @param {Function} fn the method the event invokes
11082      * @return {Roo.Element} this
11083      * @method
11084      */
11085     ep.un = ep.removeListener;
11086
11087     /**
11088      * true to automatically adjust width and height settings for box-model issues (default to true)
11089      */
11090     ep.autoBoxAdjust = true;
11091
11092     // private
11093     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11094
11095     // private
11096     El.addUnits = function(v, defaultUnit){
11097         if(v === "" || v == "auto"){
11098             return v;
11099         }
11100         if(v === undefined){
11101             return '';
11102         }
11103         if(typeof v == "number" || !El.unitPattern.test(v)){
11104             return v + (defaultUnit || 'px');
11105         }
11106         return v;
11107     };
11108
11109     // special markup used throughout Roo when box wrapping elements
11110     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>';
11111     /**
11112      * Visibility mode constant - Use visibility to hide element
11113      * @static
11114      * @type Number
11115      */
11116     El.VISIBILITY = 1;
11117     /**
11118      * Visibility mode constant - Use display to hide element
11119      * @static
11120      * @type Number
11121      */
11122     El.DISPLAY = 2;
11123
11124     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11125     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11126     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11127
11128
11129
11130     /**
11131      * @private
11132      */
11133     El.cache = {};
11134
11135     var docEl;
11136
11137     /**
11138      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11139      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11140      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11141      * @return {Element} The Element object
11142      * @static
11143      */
11144     El.get = function(el){
11145         var ex, elm, id;
11146         if(!el){ return null; }
11147         if(typeof el == "string"){ // element id
11148             if(!(elm = document.getElementById(el))){
11149                 return null;
11150             }
11151             if(ex = El.cache[el]){
11152                 ex.dom = elm;
11153             }else{
11154                 ex = El.cache[el] = new El(elm);
11155             }
11156             return ex;
11157         }else if(el.tagName){ // dom element
11158             if(!(id = el.id)){
11159                 id = Roo.id(el);
11160             }
11161             if(ex = El.cache[id]){
11162                 ex.dom = el;
11163             }else{
11164                 ex = El.cache[id] = new El(el);
11165             }
11166             return ex;
11167         }else if(el instanceof El){
11168             if(el != docEl){
11169                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11170                                                               // catch case where it hasn't been appended
11171                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11172             }
11173             return el;
11174         }else if(el.isComposite){
11175             return el;
11176         }else if(el instanceof Array){
11177             return El.select(el);
11178         }else if(el == document){
11179             // create a bogus element object representing the document object
11180             if(!docEl){
11181                 var f = function(){};
11182                 f.prototype = El.prototype;
11183                 docEl = new f();
11184                 docEl.dom = document;
11185             }
11186             return docEl;
11187         }
11188         return null;
11189     };
11190
11191     // private
11192     El.uncache = function(el){
11193         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11194             if(a[i]){
11195                 delete El.cache[a[i].id || a[i]];
11196             }
11197         }
11198     };
11199
11200     // private
11201     // Garbage collection - uncache elements/purge listeners on orphaned elements
11202     // so we don't hold a reference and cause the browser to retain them
11203     El.garbageCollect = function(){
11204         if(!Roo.enableGarbageCollector){
11205             clearInterval(El.collectorThread);
11206             return;
11207         }
11208         for(var eid in El.cache){
11209             var el = El.cache[eid], d = el.dom;
11210             // -------------------------------------------------------
11211             // Determining what is garbage:
11212             // -------------------------------------------------------
11213             // !d
11214             // dom node is null, definitely garbage
11215             // -------------------------------------------------------
11216             // !d.parentNode
11217             // no parentNode == direct orphan, definitely garbage
11218             // -------------------------------------------------------
11219             // !d.offsetParent && !document.getElementById(eid)
11220             // display none elements have no offsetParent so we will
11221             // also try to look it up by it's id. However, check
11222             // offsetParent first so we don't do unneeded lookups.
11223             // This enables collection of elements that are not orphans
11224             // directly, but somewhere up the line they have an orphan
11225             // parent.
11226             // -------------------------------------------------------
11227             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11228                 delete El.cache[eid];
11229                 if(d && Roo.enableListenerCollection){
11230                     E.purgeElement(d);
11231                 }
11232             }
11233         }
11234     }
11235     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11236
11237
11238     // dom is optional
11239     El.Flyweight = function(dom){
11240         this.dom = dom;
11241     };
11242     El.Flyweight.prototype = El.prototype;
11243
11244     El._flyweights = {};
11245     /**
11246      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11247      * the dom node can be overwritten by other code.
11248      * @param {String/HTMLElement} el The dom node or id
11249      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11250      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11251      * @static
11252      * @return {Element} The shared Element object
11253      */
11254     El.fly = function(el, named){
11255         named = named || '_global';
11256         el = Roo.getDom(el);
11257         if(!el){
11258             return null;
11259         }
11260         if(!El._flyweights[named]){
11261             El._flyweights[named] = new El.Flyweight();
11262         }
11263         El._flyweights[named].dom = el;
11264         return El._flyweights[named];
11265     };
11266
11267     /**
11268      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11269      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11270      * Shorthand of {@link Roo.Element#get}
11271      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11272      * @return {Element} The Element object
11273      * @member Roo
11274      * @method get
11275      */
11276     Roo.get = El.get;
11277     /**
11278      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11279      * the dom node can be overwritten by other code.
11280      * Shorthand of {@link Roo.Element#fly}
11281      * @param {String/HTMLElement} el The dom node or id
11282      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11283      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11284      * @static
11285      * @return {Element} The shared Element object
11286      * @member Roo
11287      * @method fly
11288      */
11289     Roo.fly = El.fly;
11290
11291     // speedy lookup for elements never to box adjust
11292     var noBoxAdjust = Roo.isStrict ? {
11293         select:1
11294     } : {
11295         input:1, select:1, textarea:1
11296     };
11297     if(Roo.isIE || Roo.isGecko){
11298         noBoxAdjust['button'] = 1;
11299     }
11300
11301
11302     Roo.EventManager.on(window, 'unload', function(){
11303         delete El.cache;
11304         delete El._flyweights;
11305     });
11306 })();
11307
11308
11309
11310
11311 if(Roo.DomQuery){
11312     Roo.Element.selectorFunction = Roo.DomQuery.select;
11313 }
11314
11315 Roo.Element.select = function(selector, unique, root){
11316     var els;
11317     if(typeof selector == "string"){
11318         els = Roo.Element.selectorFunction(selector, root);
11319     }else if(selector.length !== undefined){
11320         els = selector;
11321     }else{
11322         throw "Invalid selector";
11323     }
11324     if(unique === true){
11325         return new Roo.CompositeElement(els);
11326     }else{
11327         return new Roo.CompositeElementLite(els);
11328     }
11329 };
11330 /**
11331  * Selects elements based on the passed CSS selector to enable working on them as 1.
11332  * @param {String/Array} selector The CSS selector or an array of elements
11333  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11334  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11335  * @return {CompositeElementLite/CompositeElement}
11336  * @member Roo
11337  * @method select
11338  */
11339 Roo.select = Roo.Element.select;
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354 /*
11355  * Based on:
11356  * Ext JS Library 1.1.1
11357  * Copyright(c) 2006-2007, Ext JS, LLC.
11358  *
11359  * Originally Released Under LGPL - original licence link has changed is not relivant.
11360  *
11361  * Fork - LGPL
11362  * <script type="text/javascript">
11363  */
11364
11365
11366
11367 //Notifies Element that fx methods are available
11368 Roo.enableFx = true;
11369
11370 /**
11371  * @class Roo.Fx
11372  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11373  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11374  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11375  * Element effects to work.</p><br/>
11376  *
11377  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11378  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11379  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11380  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11381  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11382  * expected results and should be done with care.</p><br/>
11383  *
11384  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11385  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11386 <pre>
11387 Value  Description
11388 -----  -----------------------------
11389 tl     The top left corner
11390 t      The center of the top edge
11391 tr     The top right corner
11392 l      The center of the left edge
11393 r      The center of the right edge
11394 bl     The bottom left corner
11395 b      The center of the bottom edge
11396 br     The bottom right corner
11397 </pre>
11398  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11399  * below are common options that can be passed to any Fx method.</b>
11400  * @cfg {Function} callback A function called when the effect is finished
11401  * @cfg {Object} scope The scope of the effect function
11402  * @cfg {String} easing A valid Easing value for the effect
11403  * @cfg {String} afterCls A css class to apply after the effect
11404  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11405  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11406  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11407  * effects that end with the element being visually hidden, ignored otherwise)
11408  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11409  * a function which returns such a specification that will be applied to the Element after the effect finishes
11410  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11411  * @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
11412  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11413  */
11414 Roo.Fx = {
11415         /**
11416          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11417          * origin for the slide effect.  This function automatically handles wrapping the element with
11418          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11419          * Usage:
11420          *<pre><code>
11421 // default: slide the element in from the top
11422 el.slideIn();
11423
11424 // custom: slide the element in from the right with a 2-second duration
11425 el.slideIn('r', { duration: 2 });
11426
11427 // common config options shown with default values
11428 el.slideIn('t', {
11429     easing: 'easeOut',
11430     duration: .5
11431 });
11432 </code></pre>
11433          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11434          * @param {Object} options (optional) Object literal with any of the Fx config options
11435          * @return {Roo.Element} The Element
11436          */
11437     slideIn : function(anchor, o){
11438         var el = this.getFxEl();
11439         o = o || {};
11440
11441         el.queueFx(o, function(){
11442
11443             anchor = anchor || "t";
11444
11445             // fix display to visibility
11446             this.fixDisplay();
11447
11448             // restore values after effect
11449             var r = this.getFxRestore();
11450             var b = this.getBox();
11451             // fixed size for slide
11452             this.setSize(b);
11453
11454             // wrap if needed
11455             var wrap = this.fxWrap(r.pos, o, "hidden");
11456
11457             var st = this.dom.style;
11458             st.visibility = "visible";
11459             st.position = "absolute";
11460
11461             // clear out temp styles after slide and unwrap
11462             var after = function(){
11463                 el.fxUnwrap(wrap, r.pos, o);
11464                 st.width = r.width;
11465                 st.height = r.height;
11466                 el.afterFx(o);
11467             };
11468             // time to calc the positions
11469             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11470
11471             switch(anchor.toLowerCase()){
11472                 case "t":
11473                     wrap.setSize(b.width, 0);
11474                     st.left = st.bottom = "0";
11475                     a = {height: bh};
11476                 break;
11477                 case "l":
11478                     wrap.setSize(0, b.height);
11479                     st.right = st.top = "0";
11480                     a = {width: bw};
11481                 break;
11482                 case "r":
11483                     wrap.setSize(0, b.height);
11484                     wrap.setX(b.right);
11485                     st.left = st.top = "0";
11486                     a = {width: bw, points: pt};
11487                 break;
11488                 case "b":
11489                     wrap.setSize(b.width, 0);
11490                     wrap.setY(b.bottom);
11491                     st.left = st.top = "0";
11492                     a = {height: bh, points: pt};
11493                 break;
11494                 case "tl":
11495                     wrap.setSize(0, 0);
11496                     st.right = st.bottom = "0";
11497                     a = {width: bw, height: bh};
11498                 break;
11499                 case "bl":
11500                     wrap.setSize(0, 0);
11501                     wrap.setY(b.y+b.height);
11502                     st.right = st.top = "0";
11503                     a = {width: bw, height: bh, points: pt};
11504                 break;
11505                 case "br":
11506                     wrap.setSize(0, 0);
11507                     wrap.setXY([b.right, b.bottom]);
11508                     st.left = st.top = "0";
11509                     a = {width: bw, height: bh, points: pt};
11510                 break;
11511                 case "tr":
11512                     wrap.setSize(0, 0);
11513                     wrap.setX(b.x+b.width);
11514                     st.left = st.bottom = "0";
11515                     a = {width: bw, height: bh, points: pt};
11516                 break;
11517             }
11518             this.dom.style.visibility = "visible";
11519             wrap.show();
11520
11521             arguments.callee.anim = wrap.fxanim(a,
11522                 o,
11523                 'motion',
11524                 .5,
11525                 'easeOut', after);
11526         });
11527         return this;
11528     },
11529     
11530         /**
11531          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11532          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11533          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11534          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11535          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11536          * Usage:
11537          *<pre><code>
11538 // default: slide the element out to the top
11539 el.slideOut();
11540
11541 // custom: slide the element out to the right with a 2-second duration
11542 el.slideOut('r', { duration: 2 });
11543
11544 // common config options shown with default values
11545 el.slideOut('t', {
11546     easing: 'easeOut',
11547     duration: .5,
11548     remove: false,
11549     useDisplay: false
11550 });
11551 </code></pre>
11552          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11553          * @param {Object} options (optional) Object literal with any of the Fx config options
11554          * @return {Roo.Element} The Element
11555          */
11556     slideOut : function(anchor, o){
11557         var el = this.getFxEl();
11558         o = o || {};
11559
11560         el.queueFx(o, function(){
11561
11562             anchor = anchor || "t";
11563
11564             // restore values after effect
11565             var r = this.getFxRestore();
11566             
11567             var b = this.getBox();
11568             // fixed size for slide
11569             this.setSize(b);
11570
11571             // wrap if needed
11572             var wrap = this.fxWrap(r.pos, o, "visible");
11573
11574             var st = this.dom.style;
11575             st.visibility = "visible";
11576             st.position = "absolute";
11577
11578             wrap.setSize(b);
11579
11580             var after = function(){
11581                 if(o.useDisplay){
11582                     el.setDisplayed(false);
11583                 }else{
11584                     el.hide();
11585                 }
11586
11587                 el.fxUnwrap(wrap, r.pos, o);
11588
11589                 st.width = r.width;
11590                 st.height = r.height;
11591
11592                 el.afterFx(o);
11593             };
11594
11595             var a, zero = {to: 0};
11596             switch(anchor.toLowerCase()){
11597                 case "t":
11598                     st.left = st.bottom = "0";
11599                     a = {height: zero};
11600                 break;
11601                 case "l":
11602                     st.right = st.top = "0";
11603                     a = {width: zero};
11604                 break;
11605                 case "r":
11606                     st.left = st.top = "0";
11607                     a = {width: zero, points: {to:[b.right, b.y]}};
11608                 break;
11609                 case "b":
11610                     st.left = st.top = "0";
11611                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11612                 break;
11613                 case "tl":
11614                     st.right = st.bottom = "0";
11615                     a = {width: zero, height: zero};
11616                 break;
11617                 case "bl":
11618                     st.right = st.top = "0";
11619                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11620                 break;
11621                 case "br":
11622                     st.left = st.top = "0";
11623                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11624                 break;
11625                 case "tr":
11626                     st.left = st.bottom = "0";
11627                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11628                 break;
11629             }
11630
11631             arguments.callee.anim = wrap.fxanim(a,
11632                 o,
11633                 'motion',
11634                 .5,
11635                 "easeOut", after);
11636         });
11637         return this;
11638     },
11639
11640         /**
11641          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11642          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11643          * The element must be removed from the DOM using the 'remove' config option if desired.
11644          * Usage:
11645          *<pre><code>
11646 // default
11647 el.puff();
11648
11649 // common config options shown with default values
11650 el.puff({
11651     easing: 'easeOut',
11652     duration: .5,
11653     remove: false,
11654     useDisplay: false
11655 });
11656 </code></pre>
11657          * @param {Object} options (optional) Object literal with any of the Fx config options
11658          * @return {Roo.Element} The Element
11659          */
11660     puff : function(o){
11661         var el = this.getFxEl();
11662         o = o || {};
11663
11664         el.queueFx(o, function(){
11665             this.clearOpacity();
11666             this.show();
11667
11668             // restore values after effect
11669             var r = this.getFxRestore();
11670             var st = this.dom.style;
11671
11672             var after = function(){
11673                 if(o.useDisplay){
11674                     el.setDisplayed(false);
11675                 }else{
11676                     el.hide();
11677                 }
11678
11679                 el.clearOpacity();
11680
11681                 el.setPositioning(r.pos);
11682                 st.width = r.width;
11683                 st.height = r.height;
11684                 st.fontSize = '';
11685                 el.afterFx(o);
11686             };
11687
11688             var width = this.getWidth();
11689             var height = this.getHeight();
11690
11691             arguments.callee.anim = this.fxanim({
11692                     width : {to: this.adjustWidth(width * 2)},
11693                     height : {to: this.adjustHeight(height * 2)},
11694                     points : {by: [-(width * .5), -(height * .5)]},
11695                     opacity : {to: 0},
11696                     fontSize: {to:200, unit: "%"}
11697                 },
11698                 o,
11699                 'motion',
11700                 .5,
11701                 "easeOut", after);
11702         });
11703         return this;
11704     },
11705
11706         /**
11707          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11708          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11709          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11710          * Usage:
11711          *<pre><code>
11712 // default
11713 el.switchOff();
11714
11715 // all config options shown with default values
11716 el.switchOff({
11717     easing: 'easeIn',
11718     duration: .3,
11719     remove: false,
11720     useDisplay: false
11721 });
11722 </code></pre>
11723          * @param {Object} options (optional) Object literal with any of the Fx config options
11724          * @return {Roo.Element} The Element
11725          */
11726     switchOff : function(o){
11727         var el = this.getFxEl();
11728         o = o || {};
11729
11730         el.queueFx(o, function(){
11731             this.clearOpacity();
11732             this.clip();
11733
11734             // restore values after effect
11735             var r = this.getFxRestore();
11736             var st = this.dom.style;
11737
11738             var after = function(){
11739                 if(o.useDisplay){
11740                     el.setDisplayed(false);
11741                 }else{
11742                     el.hide();
11743                 }
11744
11745                 el.clearOpacity();
11746                 el.setPositioning(r.pos);
11747                 st.width = r.width;
11748                 st.height = r.height;
11749
11750                 el.afterFx(o);
11751             };
11752
11753             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11754                 this.clearOpacity();
11755                 (function(){
11756                     this.fxanim({
11757                         height:{to:1},
11758                         points:{by:[0, this.getHeight() * .5]}
11759                     }, o, 'motion', 0.3, 'easeIn', after);
11760                 }).defer(100, this);
11761             });
11762         });
11763         return this;
11764     },
11765
11766     /**
11767      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11768      * changed using the "attr" config option) and then fading back to the original color. If no original
11769      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11770      * Usage:
11771 <pre><code>
11772 // default: highlight background to yellow
11773 el.highlight();
11774
11775 // custom: highlight foreground text to blue for 2 seconds
11776 el.highlight("0000ff", { attr: 'color', duration: 2 });
11777
11778 // common config options shown with default values
11779 el.highlight("ffff9c", {
11780     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11781     endColor: (current color) or "ffffff",
11782     easing: 'easeIn',
11783     duration: 1
11784 });
11785 </code></pre>
11786      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11787      * @param {Object} options (optional) Object literal with any of the Fx config options
11788      * @return {Roo.Element} The Element
11789      */ 
11790     highlight : function(color, o){
11791         var el = this.getFxEl();
11792         o = o || {};
11793
11794         el.queueFx(o, function(){
11795             color = color || "ffff9c";
11796             attr = o.attr || "backgroundColor";
11797
11798             this.clearOpacity();
11799             this.show();
11800
11801             var origColor = this.getColor(attr);
11802             var restoreColor = this.dom.style[attr];
11803             endColor = (o.endColor || origColor) || "ffffff";
11804
11805             var after = function(){
11806                 el.dom.style[attr] = restoreColor;
11807                 el.afterFx(o);
11808             };
11809
11810             var a = {};
11811             a[attr] = {from: color, to: endColor};
11812             arguments.callee.anim = this.fxanim(a,
11813                 o,
11814                 'color',
11815                 1,
11816                 'easeIn', after);
11817         });
11818         return this;
11819     },
11820
11821    /**
11822     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11823     * Usage:
11824 <pre><code>
11825 // default: a single light blue ripple
11826 el.frame();
11827
11828 // custom: 3 red ripples lasting 3 seconds total
11829 el.frame("ff0000", 3, { duration: 3 });
11830
11831 // common config options shown with default values
11832 el.frame("C3DAF9", 1, {
11833     duration: 1 //duration of entire animation (not each individual ripple)
11834     // Note: Easing is not configurable and will be ignored if included
11835 });
11836 </code></pre>
11837     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11838     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11839     * @param {Object} options (optional) Object literal with any of the Fx config options
11840     * @return {Roo.Element} The Element
11841     */
11842     frame : function(color, count, o){
11843         var el = this.getFxEl();
11844         o = o || {};
11845
11846         el.queueFx(o, function(){
11847             color = color || "#C3DAF9";
11848             if(color.length == 6){
11849                 color = "#" + color;
11850             }
11851             count = count || 1;
11852             duration = o.duration || 1;
11853             this.show();
11854
11855             var b = this.getBox();
11856             var animFn = function(){
11857                 var proxy = this.createProxy({
11858
11859                      style:{
11860                         visbility:"hidden",
11861                         position:"absolute",
11862                         "z-index":"35000", // yee haw
11863                         border:"0px solid " + color
11864                      }
11865                   });
11866                 var scale = Roo.isBorderBox ? 2 : 1;
11867                 proxy.animate({
11868                     top:{from:b.y, to:b.y - 20},
11869                     left:{from:b.x, to:b.x - 20},
11870                     borderWidth:{from:0, to:10},
11871                     opacity:{from:1, to:0},
11872                     height:{from:b.height, to:(b.height + (20*scale))},
11873                     width:{from:b.width, to:(b.width + (20*scale))}
11874                 }, duration, function(){
11875                     proxy.remove();
11876                 });
11877                 if(--count > 0){
11878                      animFn.defer((duration/2)*1000, this);
11879                 }else{
11880                     el.afterFx(o);
11881                 }
11882             };
11883             animFn.call(this);
11884         });
11885         return this;
11886     },
11887
11888    /**
11889     * Creates a pause before any subsequent queued effects begin.  If there are
11890     * no effects queued after the pause it will have no effect.
11891     * Usage:
11892 <pre><code>
11893 el.pause(1);
11894 </code></pre>
11895     * @param {Number} seconds The length of time to pause (in seconds)
11896     * @return {Roo.Element} The Element
11897     */
11898     pause : function(seconds){
11899         var el = this.getFxEl();
11900         var o = {};
11901
11902         el.queueFx(o, function(){
11903             setTimeout(function(){
11904                 el.afterFx(o);
11905             }, seconds * 1000);
11906         });
11907         return this;
11908     },
11909
11910    /**
11911     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11912     * using the "endOpacity" config option.
11913     * Usage:
11914 <pre><code>
11915 // default: fade in from opacity 0 to 100%
11916 el.fadeIn();
11917
11918 // custom: fade in from opacity 0 to 75% over 2 seconds
11919 el.fadeIn({ endOpacity: .75, duration: 2});
11920
11921 // common config options shown with default values
11922 el.fadeIn({
11923     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11924     easing: 'easeOut',
11925     duration: .5
11926 });
11927 </code></pre>
11928     * @param {Object} options (optional) Object literal with any of the Fx config options
11929     * @return {Roo.Element} The Element
11930     */
11931     fadeIn : function(o){
11932         var el = this.getFxEl();
11933         o = o || {};
11934         el.queueFx(o, function(){
11935             this.setOpacity(0);
11936             this.fixDisplay();
11937             this.dom.style.visibility = 'visible';
11938             var to = o.endOpacity || 1;
11939             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11940                 o, null, .5, "easeOut", function(){
11941                 if(to == 1){
11942                     this.clearOpacity();
11943                 }
11944                 el.afterFx(o);
11945             });
11946         });
11947         return this;
11948     },
11949
11950    /**
11951     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11952     * using the "endOpacity" config option.
11953     * Usage:
11954 <pre><code>
11955 // default: fade out from the element's current opacity to 0
11956 el.fadeOut();
11957
11958 // custom: fade out from the element's current opacity to 25% over 2 seconds
11959 el.fadeOut({ endOpacity: .25, duration: 2});
11960
11961 // common config options shown with default values
11962 el.fadeOut({
11963     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11964     easing: 'easeOut',
11965     duration: .5
11966     remove: false,
11967     useDisplay: false
11968 });
11969 </code></pre>
11970     * @param {Object} options (optional) Object literal with any of the Fx config options
11971     * @return {Roo.Element} The Element
11972     */
11973     fadeOut : function(o){
11974         var el = this.getFxEl();
11975         o = o || {};
11976         el.queueFx(o, function(){
11977             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11978                 o, null, .5, "easeOut", function(){
11979                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11980                      this.dom.style.display = "none";
11981                 }else{
11982                      this.dom.style.visibility = "hidden";
11983                 }
11984                 this.clearOpacity();
11985                 el.afterFx(o);
11986             });
11987         });
11988         return this;
11989     },
11990
11991    /**
11992     * Animates the transition of an element's dimensions from a starting height/width
11993     * to an ending height/width.
11994     * Usage:
11995 <pre><code>
11996 // change height and width to 100x100 pixels
11997 el.scale(100, 100);
11998
11999 // common config options shown with default values.  The height and width will default to
12000 // the element's existing values if passed as null.
12001 el.scale(
12002     [element's width],
12003     [element's height], {
12004     easing: 'easeOut',
12005     duration: .35
12006 });
12007 </code></pre>
12008     * @param {Number} width  The new width (pass undefined to keep the original width)
12009     * @param {Number} height  The new height (pass undefined to keep the original height)
12010     * @param {Object} options (optional) Object literal with any of the Fx config options
12011     * @return {Roo.Element} The Element
12012     */
12013     scale : function(w, h, o){
12014         this.shift(Roo.apply({}, o, {
12015             width: w,
12016             height: h
12017         }));
12018         return this;
12019     },
12020
12021    /**
12022     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12023     * Any of these properties not specified in the config object will not be changed.  This effect 
12024     * requires that at least one new dimension, position or opacity setting must be passed in on
12025     * the config object in order for the function to have any effect.
12026     * Usage:
12027 <pre><code>
12028 // slide the element horizontally to x position 200 while changing the height and opacity
12029 el.shift({ x: 200, height: 50, opacity: .8 });
12030
12031 // common config options shown with default values.
12032 el.shift({
12033     width: [element's width],
12034     height: [element's height],
12035     x: [element's x position],
12036     y: [element's y position],
12037     opacity: [element's opacity],
12038     easing: 'easeOut',
12039     duration: .35
12040 });
12041 </code></pre>
12042     * @param {Object} options  Object literal with any of the Fx config options
12043     * @return {Roo.Element} The Element
12044     */
12045     shift : function(o){
12046         var el = this.getFxEl();
12047         o = o || {};
12048         el.queueFx(o, function(){
12049             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12050             if(w !== undefined){
12051                 a.width = {to: this.adjustWidth(w)};
12052             }
12053             if(h !== undefined){
12054                 a.height = {to: this.adjustHeight(h)};
12055             }
12056             if(x !== undefined || y !== undefined){
12057                 a.points = {to: [
12058                     x !== undefined ? x : this.getX(),
12059                     y !== undefined ? y : this.getY()
12060                 ]};
12061             }
12062             if(op !== undefined){
12063                 a.opacity = {to: op};
12064             }
12065             if(o.xy !== undefined){
12066                 a.points = {to: o.xy};
12067             }
12068             arguments.callee.anim = this.fxanim(a,
12069                 o, 'motion', .35, "easeOut", function(){
12070                 el.afterFx(o);
12071             });
12072         });
12073         return this;
12074     },
12075
12076         /**
12077          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12078          * ending point of the effect.
12079          * Usage:
12080          *<pre><code>
12081 // default: slide the element downward while fading out
12082 el.ghost();
12083
12084 // custom: slide the element out to the right with a 2-second duration
12085 el.ghost('r', { duration: 2 });
12086
12087 // common config options shown with default values
12088 el.ghost('b', {
12089     easing: 'easeOut',
12090     duration: .5
12091     remove: false,
12092     useDisplay: false
12093 });
12094 </code></pre>
12095          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12096          * @param {Object} options (optional) Object literal with any of the Fx config options
12097          * @return {Roo.Element} The Element
12098          */
12099     ghost : function(anchor, o){
12100         var el = this.getFxEl();
12101         o = o || {};
12102
12103         el.queueFx(o, function(){
12104             anchor = anchor || "b";
12105
12106             // restore values after effect
12107             var r = this.getFxRestore();
12108             var w = this.getWidth(),
12109                 h = this.getHeight();
12110
12111             var st = this.dom.style;
12112
12113             var after = function(){
12114                 if(o.useDisplay){
12115                     el.setDisplayed(false);
12116                 }else{
12117                     el.hide();
12118                 }
12119
12120                 el.clearOpacity();
12121                 el.setPositioning(r.pos);
12122                 st.width = r.width;
12123                 st.height = r.height;
12124
12125                 el.afterFx(o);
12126             };
12127
12128             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12129             switch(anchor.toLowerCase()){
12130                 case "t":
12131                     pt.by = [0, -h];
12132                 break;
12133                 case "l":
12134                     pt.by = [-w, 0];
12135                 break;
12136                 case "r":
12137                     pt.by = [w, 0];
12138                 break;
12139                 case "b":
12140                     pt.by = [0, h];
12141                 break;
12142                 case "tl":
12143                     pt.by = [-w, -h];
12144                 break;
12145                 case "bl":
12146                     pt.by = [-w, h];
12147                 break;
12148                 case "br":
12149                     pt.by = [w, h];
12150                 break;
12151                 case "tr":
12152                     pt.by = [w, -h];
12153                 break;
12154             }
12155
12156             arguments.callee.anim = this.fxanim(a,
12157                 o,
12158                 'motion',
12159                 .5,
12160                 "easeOut", after);
12161         });
12162         return this;
12163     },
12164
12165         /**
12166          * Ensures that all effects queued after syncFx is called on the element are
12167          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12168          * @return {Roo.Element} The Element
12169          */
12170     syncFx : function(){
12171         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12172             block : false,
12173             concurrent : true,
12174             stopFx : false
12175         });
12176         return this;
12177     },
12178
12179         /**
12180          * Ensures that all effects queued after sequenceFx is called on the element are
12181          * run in sequence.  This is the opposite of {@link #syncFx}.
12182          * @return {Roo.Element} The Element
12183          */
12184     sequenceFx : function(){
12185         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12186             block : false,
12187             concurrent : false,
12188             stopFx : false
12189         });
12190         return this;
12191     },
12192
12193         /* @private */
12194     nextFx : function(){
12195         var ef = this.fxQueue[0];
12196         if(ef){
12197             ef.call(this);
12198         }
12199     },
12200
12201         /**
12202          * Returns true if the element has any effects actively running or queued, else returns false.
12203          * @return {Boolean} True if element has active effects, else false
12204          */
12205     hasActiveFx : function(){
12206         return this.fxQueue && this.fxQueue[0];
12207     },
12208
12209         /**
12210          * Stops any running effects and clears the element's internal effects queue if it contains
12211          * any additional effects that haven't started yet.
12212          * @return {Roo.Element} The Element
12213          */
12214     stopFx : function(){
12215         if(this.hasActiveFx()){
12216             var cur = this.fxQueue[0];
12217             if(cur && cur.anim && cur.anim.isAnimated()){
12218                 this.fxQueue = [cur]; // clear out others
12219                 cur.anim.stop(true);
12220             }
12221         }
12222         return this;
12223     },
12224
12225         /* @private */
12226     beforeFx : function(o){
12227         if(this.hasActiveFx() && !o.concurrent){
12228            if(o.stopFx){
12229                this.stopFx();
12230                return true;
12231            }
12232            return false;
12233         }
12234         return true;
12235     },
12236
12237         /**
12238          * Returns true if the element is currently blocking so that no other effect can be queued
12239          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12240          * used to ensure that an effect initiated by a user action runs to completion prior to the
12241          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12242          * @return {Boolean} True if blocking, else false
12243          */
12244     hasFxBlock : function(){
12245         var q = this.fxQueue;
12246         return q && q[0] && q[0].block;
12247     },
12248
12249         /* @private */
12250     queueFx : function(o, fn){
12251         if(!this.fxQueue){
12252             this.fxQueue = [];
12253         }
12254         if(!this.hasFxBlock()){
12255             Roo.applyIf(o, this.fxDefaults);
12256             if(!o.concurrent){
12257                 var run = this.beforeFx(o);
12258                 fn.block = o.block;
12259                 this.fxQueue.push(fn);
12260                 if(run){
12261                     this.nextFx();
12262                 }
12263             }else{
12264                 fn.call(this);
12265             }
12266         }
12267         return this;
12268     },
12269
12270         /* @private */
12271     fxWrap : function(pos, o, vis){
12272         var wrap;
12273         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12274             var wrapXY;
12275             if(o.fixPosition){
12276                 wrapXY = this.getXY();
12277             }
12278             var div = document.createElement("div");
12279             div.style.visibility = vis;
12280             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12281             wrap.setPositioning(pos);
12282             if(wrap.getStyle("position") == "static"){
12283                 wrap.position("relative");
12284             }
12285             this.clearPositioning('auto');
12286             wrap.clip();
12287             wrap.dom.appendChild(this.dom);
12288             if(wrapXY){
12289                 wrap.setXY(wrapXY);
12290             }
12291         }
12292         return wrap;
12293     },
12294
12295         /* @private */
12296     fxUnwrap : function(wrap, pos, o){
12297         this.clearPositioning();
12298         this.setPositioning(pos);
12299         if(!o.wrap){
12300             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12301             wrap.remove();
12302         }
12303     },
12304
12305         /* @private */
12306     getFxRestore : function(){
12307         var st = this.dom.style;
12308         return {pos: this.getPositioning(), width: st.width, height : st.height};
12309     },
12310
12311         /* @private */
12312     afterFx : function(o){
12313         if(o.afterStyle){
12314             this.applyStyles(o.afterStyle);
12315         }
12316         if(o.afterCls){
12317             this.addClass(o.afterCls);
12318         }
12319         if(o.remove === true){
12320             this.remove();
12321         }
12322         Roo.callback(o.callback, o.scope, [this]);
12323         if(!o.concurrent){
12324             this.fxQueue.shift();
12325             this.nextFx();
12326         }
12327     },
12328
12329         /* @private */
12330     getFxEl : function(){ // support for composite element fx
12331         return Roo.get(this.dom);
12332     },
12333
12334         /* @private */
12335     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12336         animType = animType || 'run';
12337         opt = opt || {};
12338         var anim = Roo.lib.Anim[animType](
12339             this.dom, args,
12340             (opt.duration || defaultDur) || .35,
12341             (opt.easing || defaultEase) || 'easeOut',
12342             function(){
12343                 Roo.callback(cb, this);
12344             },
12345             this
12346         );
12347         opt.anim = anim;
12348         return anim;
12349     }
12350 };
12351
12352 // backwords compat
12353 Roo.Fx.resize = Roo.Fx.scale;
12354
12355 //When included, Roo.Fx is automatically applied to Element so that all basic
12356 //effects are available directly via the Element API
12357 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12358  * Based on:
12359  * Ext JS Library 1.1.1
12360  * Copyright(c) 2006-2007, Ext JS, LLC.
12361  *
12362  * Originally Released Under LGPL - original licence link has changed is not relivant.
12363  *
12364  * Fork - LGPL
12365  * <script type="text/javascript">
12366  */
12367
12368
12369 /**
12370  * @class Roo.CompositeElement
12371  * Standard composite class. Creates a Roo.Element for every element in the collection.
12372  * <br><br>
12373  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12374  * actions will be performed on all the elements in this collection.</b>
12375  * <br><br>
12376  * All methods return <i>this</i> and can be chained.
12377  <pre><code>
12378  var els = Roo.select("#some-el div.some-class", true);
12379  // or select directly from an existing element
12380  var el = Roo.get('some-el');
12381  el.select('div.some-class', true);
12382
12383  els.setWidth(100); // all elements become 100 width
12384  els.hide(true); // all elements fade out and hide
12385  // or
12386  els.setWidth(100).hide(true);
12387  </code></pre>
12388  */
12389 Roo.CompositeElement = function(els){
12390     this.elements = [];
12391     this.addElements(els);
12392 };
12393 Roo.CompositeElement.prototype = {
12394     isComposite: true,
12395     addElements : function(els){
12396         if(!els) {
12397             return this;
12398         }
12399         if(typeof els == "string"){
12400             els = Roo.Element.selectorFunction(els);
12401         }
12402         var yels = this.elements;
12403         var index = yels.length-1;
12404         for(var i = 0, len = els.length; i < len; i++) {
12405                 yels[++index] = Roo.get(els[i]);
12406         }
12407         return this;
12408     },
12409
12410     /**
12411     * Clears this composite and adds the elements returned by the passed selector.
12412     * @param {String/Array} els A string CSS selector, an array of elements or an element
12413     * @return {CompositeElement} this
12414     */
12415     fill : function(els){
12416         this.elements = [];
12417         this.add(els);
12418         return this;
12419     },
12420
12421     /**
12422     * Filters this composite to only elements that match the passed selector.
12423     * @param {String} selector A string CSS selector
12424     * @param {Boolean} inverse return inverse filter (not matches)
12425     * @return {CompositeElement} this
12426     */
12427     filter : function(selector, inverse){
12428         var els = [];
12429         inverse = inverse || false;
12430         this.each(function(el){
12431             var match = inverse ? !el.is(selector) : el.is(selector);
12432             if(match){
12433                 els[els.length] = el.dom;
12434             }
12435         });
12436         this.fill(els);
12437         return this;
12438     },
12439
12440     invoke : function(fn, args){
12441         var els = this.elements;
12442         for(var i = 0, len = els.length; i < len; i++) {
12443                 Roo.Element.prototype[fn].apply(els[i], args);
12444         }
12445         return this;
12446     },
12447     /**
12448     * Adds elements to this composite.
12449     * @param {String/Array} els A string CSS selector, an array of elements or an element
12450     * @return {CompositeElement} this
12451     */
12452     add : function(els){
12453         if(typeof els == "string"){
12454             this.addElements(Roo.Element.selectorFunction(els));
12455         }else if(els.length !== undefined){
12456             this.addElements(els);
12457         }else{
12458             this.addElements([els]);
12459         }
12460         return this;
12461     },
12462     /**
12463     * Calls the passed function passing (el, this, index) for each element in this composite.
12464     * @param {Function} fn The function to call
12465     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12466     * @return {CompositeElement} this
12467     */
12468     each : function(fn, scope){
12469         var els = this.elements;
12470         for(var i = 0, len = els.length; i < len; i++){
12471             if(fn.call(scope || els[i], els[i], this, i) === false) {
12472                 break;
12473             }
12474         }
12475         return this;
12476     },
12477
12478     /**
12479      * Returns the Element object at the specified index
12480      * @param {Number} index
12481      * @return {Roo.Element}
12482      */
12483     item : function(index){
12484         return this.elements[index] || null;
12485     },
12486
12487     /**
12488      * Returns the first Element
12489      * @return {Roo.Element}
12490      */
12491     first : function(){
12492         return this.item(0);
12493     },
12494
12495     /**
12496      * Returns the last Element
12497      * @return {Roo.Element}
12498      */
12499     last : function(){
12500         return this.item(this.elements.length-1);
12501     },
12502
12503     /**
12504      * Returns the number of elements in this composite
12505      * @return Number
12506      */
12507     getCount : function(){
12508         return this.elements.length;
12509     },
12510
12511     /**
12512      * Returns true if this composite contains the passed element
12513      * @return Boolean
12514      */
12515     contains : function(el){
12516         return this.indexOf(el) !== -1;
12517     },
12518
12519     /**
12520      * Returns true if this composite contains the passed element
12521      * @return Boolean
12522      */
12523     indexOf : function(el){
12524         return this.elements.indexOf(Roo.get(el));
12525     },
12526
12527
12528     /**
12529     * Removes the specified element(s).
12530     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12531     * or an array of any of those.
12532     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12533     * @return {CompositeElement} this
12534     */
12535     removeElement : function(el, removeDom){
12536         if(el instanceof Array){
12537             for(var i = 0, len = el.length; i < len; i++){
12538                 this.removeElement(el[i]);
12539             }
12540             return this;
12541         }
12542         var index = typeof el == 'number' ? el : this.indexOf(el);
12543         if(index !== -1){
12544             if(removeDom){
12545                 var d = this.elements[index];
12546                 if(d.dom){
12547                     d.remove();
12548                 }else{
12549                     d.parentNode.removeChild(d);
12550                 }
12551             }
12552             this.elements.splice(index, 1);
12553         }
12554         return this;
12555     },
12556
12557     /**
12558     * Replaces the specified element with the passed element.
12559     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12560     * to replace.
12561     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12562     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12563     * @return {CompositeElement} this
12564     */
12565     replaceElement : function(el, replacement, domReplace){
12566         var index = typeof el == 'number' ? el : this.indexOf(el);
12567         if(index !== -1){
12568             if(domReplace){
12569                 this.elements[index].replaceWith(replacement);
12570             }else{
12571                 this.elements.splice(index, 1, Roo.get(replacement))
12572             }
12573         }
12574         return this;
12575     },
12576
12577     /**
12578      * Removes all elements.
12579      */
12580     clear : function(){
12581         this.elements = [];
12582     }
12583 };
12584 (function(){
12585     Roo.CompositeElement.createCall = function(proto, fnName){
12586         if(!proto[fnName]){
12587             proto[fnName] = function(){
12588                 return this.invoke(fnName, arguments);
12589             };
12590         }
12591     };
12592     for(var fnName in Roo.Element.prototype){
12593         if(typeof Roo.Element.prototype[fnName] == "function"){
12594             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12595         }
12596     };
12597 })();
12598 /*
12599  * Based on:
12600  * Ext JS Library 1.1.1
12601  * Copyright(c) 2006-2007, Ext JS, LLC.
12602  *
12603  * Originally Released Under LGPL - original licence link has changed is not relivant.
12604  *
12605  * Fork - LGPL
12606  * <script type="text/javascript">
12607  */
12608
12609 /**
12610  * @class Roo.CompositeElementLite
12611  * @extends Roo.CompositeElement
12612  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12613  <pre><code>
12614  var els = Roo.select("#some-el div.some-class");
12615  // or select directly from an existing element
12616  var el = Roo.get('some-el');
12617  el.select('div.some-class');
12618
12619  els.setWidth(100); // all elements become 100 width
12620  els.hide(true); // all elements fade out and hide
12621  // or
12622  els.setWidth(100).hide(true);
12623  </code></pre><br><br>
12624  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12625  * actions will be performed on all the elements in this collection.</b>
12626  */
12627 Roo.CompositeElementLite = function(els){
12628     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12629     this.el = new Roo.Element.Flyweight();
12630 };
12631 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12632     addElements : function(els){
12633         if(els){
12634             if(els instanceof Array){
12635                 this.elements = this.elements.concat(els);
12636             }else{
12637                 var yels = this.elements;
12638                 var index = yels.length-1;
12639                 for(var i = 0, len = els.length; i < len; i++) {
12640                     yels[++index] = els[i];
12641                 }
12642             }
12643         }
12644         return this;
12645     },
12646     invoke : function(fn, args){
12647         var els = this.elements;
12648         var el = this.el;
12649         for(var i = 0, len = els.length; i < len; i++) {
12650             el.dom = els[i];
12651                 Roo.Element.prototype[fn].apply(el, args);
12652         }
12653         return this;
12654     },
12655     /**
12656      * Returns a flyweight Element of the dom element object at the specified index
12657      * @param {Number} index
12658      * @return {Roo.Element}
12659      */
12660     item : function(index){
12661         if(!this.elements[index]){
12662             return null;
12663         }
12664         this.el.dom = this.elements[index];
12665         return this.el;
12666     },
12667
12668     // fixes scope with flyweight
12669     addListener : function(eventName, handler, scope, opt){
12670         var els = this.elements;
12671         for(var i = 0, len = els.length; i < len; i++) {
12672             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12673         }
12674         return this;
12675     },
12676
12677     /**
12678     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12679     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12680     * a reference to the dom node, use el.dom.</b>
12681     * @param {Function} fn The function to call
12682     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12683     * @return {CompositeElement} this
12684     */
12685     each : function(fn, scope){
12686         var els = this.elements;
12687         var el = this.el;
12688         for(var i = 0, len = els.length; i < len; i++){
12689             el.dom = els[i];
12690                 if(fn.call(scope || el, el, this, i) === false){
12691                 break;
12692             }
12693         }
12694         return this;
12695     },
12696
12697     indexOf : function(el){
12698         return this.elements.indexOf(Roo.getDom(el));
12699     },
12700
12701     replaceElement : function(el, replacement, domReplace){
12702         var index = typeof el == 'number' ? el : this.indexOf(el);
12703         if(index !== -1){
12704             replacement = Roo.getDom(replacement);
12705             if(domReplace){
12706                 var d = this.elements[index];
12707                 d.parentNode.insertBefore(replacement, d);
12708                 d.parentNode.removeChild(d);
12709             }
12710             this.elements.splice(index, 1, replacement);
12711         }
12712         return this;
12713     }
12714 });
12715 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12716
12717 /*
12718  * Based on:
12719  * Ext JS Library 1.1.1
12720  * Copyright(c) 2006-2007, Ext JS, LLC.
12721  *
12722  * Originally Released Under LGPL - original licence link has changed is not relivant.
12723  *
12724  * Fork - LGPL
12725  * <script type="text/javascript">
12726  */
12727
12728  
12729
12730 /**
12731  * @class Roo.data.Connection
12732  * @extends Roo.util.Observable
12733  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12734  * either to a configured URL, or to a URL specified at request time. 
12735  * 
12736  * Requests made by this class are asynchronous, and will return immediately. No data from
12737  * the server will be available to the statement immediately following the {@link #request} call.
12738  * To process returned data, use a callback in the request options object, or an event listener.
12739  * 
12740  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12741  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12742  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12743  * property and, if present, the IFRAME's XML document as the responseXML property.
12744  * 
12745  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12746  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12747  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12748  * standard DOM methods.
12749  * @constructor
12750  * @param {Object} config a configuration object.
12751  */
12752 Roo.data.Connection = function(config){
12753     Roo.apply(this, config);
12754     this.addEvents({
12755         /**
12756          * @event beforerequest
12757          * Fires before a network request is made to retrieve a data object.
12758          * @param {Connection} conn This Connection object.
12759          * @param {Object} options The options config object passed to the {@link #request} method.
12760          */
12761         "beforerequest" : true,
12762         /**
12763          * @event requestcomplete
12764          * Fires if the request was successfully completed.
12765          * @param {Connection} conn This Connection object.
12766          * @param {Object} response The XHR object containing the response data.
12767          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12768          * @param {Object} options The options config object passed to the {@link #request} method.
12769          */
12770         "requestcomplete" : true,
12771         /**
12772          * @event requestexception
12773          * Fires if an error HTTP status was returned from the server.
12774          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12775          * @param {Connection} conn This Connection object.
12776          * @param {Object} response The XHR object containing the response data.
12777          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12778          * @param {Object} options The options config object passed to the {@link #request} method.
12779          */
12780         "requestexception" : true
12781     });
12782     Roo.data.Connection.superclass.constructor.call(this);
12783 };
12784
12785 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12786     /**
12787      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12788      */
12789     /**
12790      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12791      * extra parameters to each request made by this object. (defaults to undefined)
12792      */
12793     /**
12794      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12795      *  to each request made by this object. (defaults to undefined)
12796      */
12797     /**
12798      * @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)
12799      */
12800     /**
12801      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12802      */
12803     timeout : 30000,
12804     /**
12805      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12806      * @type Boolean
12807      */
12808     autoAbort:false,
12809
12810     /**
12811      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12812      * @type Boolean
12813      */
12814     disableCaching: true,
12815
12816     /**
12817      * Sends an HTTP request to a remote server.
12818      * @param {Object} options An object which may contain the following properties:<ul>
12819      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12820      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12821      * request, a url encoded string or a function to call to get either.</li>
12822      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12823      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12824      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12825      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12826      * <li>options {Object} The parameter to the request call.</li>
12827      * <li>success {Boolean} True if the request succeeded.</li>
12828      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12829      * </ul></li>
12830      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12831      * The callback is passed the following parameters:<ul>
12832      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12833      * <li>options {Object} The parameter to the request call.</li>
12834      * </ul></li>
12835      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12836      * The callback is passed the following parameters:<ul>
12837      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12838      * <li>options {Object} The parameter to the request call.</li>
12839      * </ul></li>
12840      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12841      * for the callback function. Defaults to the browser window.</li>
12842      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12843      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12844      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12845      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12846      * params for the post data. Any params will be appended to the URL.</li>
12847      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12848      * </ul>
12849      * @return {Number} transactionId
12850      */
12851     request : function(o){
12852         if(this.fireEvent("beforerequest", this, o) !== false){
12853             var p = o.params;
12854
12855             if(typeof p == "function"){
12856                 p = p.call(o.scope||window, o);
12857             }
12858             if(typeof p == "object"){
12859                 p = Roo.urlEncode(o.params);
12860             }
12861             if(this.extraParams){
12862                 var extras = Roo.urlEncode(this.extraParams);
12863                 p = p ? (p + '&' + extras) : extras;
12864             }
12865
12866             var url = o.url || this.url;
12867             if(typeof url == 'function'){
12868                 url = url.call(o.scope||window, o);
12869             }
12870
12871             if(o.form){
12872                 var form = Roo.getDom(o.form);
12873                 url = url || form.action;
12874
12875                 var enctype = form.getAttribute("enctype");
12876                 
12877                 if (o.formData) {
12878                     return this.doFormDataUpload(o, url);
12879                 }
12880                 
12881                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12882                     return this.doFormUpload(o, p, url);
12883                 }
12884                 var f = Roo.lib.Ajax.serializeForm(form);
12885                 p = p ? (p + '&' + f) : f;
12886             }
12887             
12888             if (!o.form && o.formData) {
12889                 o.formData = o.formData === true ? new FormData() : o.formData;
12890                 for (var k in o.params) {
12891                     o.formData.append(k,o.params[k]);
12892                 }
12893                     
12894                 return this.doFormDataUpload(o, url);
12895             }
12896             
12897
12898             var hs = o.headers;
12899             if(this.defaultHeaders){
12900                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12901                 if(!o.headers){
12902                     o.headers = hs;
12903                 }
12904             }
12905
12906             var cb = {
12907                 success: this.handleResponse,
12908                 failure: this.handleFailure,
12909                 scope: this,
12910                 argument: {options: o},
12911                 timeout : o.timeout || this.timeout
12912             };
12913
12914             var method = o.method||this.method||(p ? "POST" : "GET");
12915
12916             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12917                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12918             }
12919
12920             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12921                 if(o.autoAbort){
12922                     this.abort();
12923                 }
12924             }else if(this.autoAbort !== false){
12925                 this.abort();
12926             }
12927
12928             if((method == 'GET' && p) || o.xmlData){
12929                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12930                 p = '';
12931             }
12932             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12933             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12934             Roo.lib.Ajax.useDefaultHeader == true;
12935             return this.transId;
12936         }else{
12937             Roo.callback(o.callback, o.scope, [o, null, null]);
12938             return null;
12939         }
12940     },
12941
12942     /**
12943      * Determine whether this object has a request outstanding.
12944      * @param {Number} transactionId (Optional) defaults to the last transaction
12945      * @return {Boolean} True if there is an outstanding request.
12946      */
12947     isLoading : function(transId){
12948         if(transId){
12949             return Roo.lib.Ajax.isCallInProgress(transId);
12950         }else{
12951             return this.transId ? true : false;
12952         }
12953     },
12954
12955     /**
12956      * Aborts any outstanding request.
12957      * @param {Number} transactionId (Optional) defaults to the last transaction
12958      */
12959     abort : function(transId){
12960         if(transId || this.isLoading()){
12961             Roo.lib.Ajax.abort(transId || this.transId);
12962         }
12963     },
12964
12965     // private
12966     handleResponse : function(response){
12967         this.transId = false;
12968         var options = response.argument.options;
12969         response.argument = options ? options.argument : null;
12970         this.fireEvent("requestcomplete", this, response, options);
12971         Roo.callback(options.success, options.scope, [response, options]);
12972         Roo.callback(options.callback, options.scope, [options, true, response]);
12973     },
12974
12975     // private
12976     handleFailure : function(response, e){
12977         this.transId = false;
12978         var options = response.argument.options;
12979         response.argument = options ? options.argument : null;
12980         this.fireEvent("requestexception", this, response, options, e);
12981         Roo.callback(options.failure, options.scope, [response, options]);
12982         Roo.callback(options.callback, options.scope, [options, false, response]);
12983     },
12984
12985     // private
12986     doFormUpload : function(o, ps, url){
12987         var id = Roo.id();
12988         var frame = document.createElement('iframe');
12989         frame.id = id;
12990         frame.name = id;
12991         frame.className = 'x-hidden';
12992         if(Roo.isIE){
12993             frame.src = Roo.SSL_SECURE_URL;
12994         }
12995         document.body.appendChild(frame);
12996
12997         if(Roo.isIE){
12998            document.frames[id].name = id;
12999         }
13000
13001         var form = Roo.getDom(o.form);
13002         form.target = id;
13003         form.method = 'POST';
13004         form.enctype = form.encoding = 'multipart/form-data';
13005         if(url){
13006             form.action = url;
13007         }
13008
13009         var hiddens, hd;
13010         if(ps){ // add dynamic params
13011             hiddens = [];
13012             ps = Roo.urlDecode(ps, false);
13013             for(var k in ps){
13014                 if(ps.hasOwnProperty(k)){
13015                     hd = document.createElement('input');
13016                     hd.type = 'hidden';
13017                     hd.name = k;
13018                     hd.value = ps[k];
13019                     form.appendChild(hd);
13020                     hiddens.push(hd);
13021                 }
13022             }
13023         }
13024
13025         function cb(){
13026             var r = {  // bogus response object
13027                 responseText : '',
13028                 responseXML : null
13029             };
13030
13031             r.argument = o ? o.argument : null;
13032
13033             try { //
13034                 var doc;
13035                 if(Roo.isIE){
13036                     doc = frame.contentWindow.document;
13037                 }else {
13038                     doc = (frame.contentDocument || window.frames[id].document);
13039                 }
13040                 if(doc && doc.body){
13041                     r.responseText = doc.body.innerHTML;
13042                 }
13043                 if(doc && doc.XMLDocument){
13044                     r.responseXML = doc.XMLDocument;
13045                 }else {
13046                     r.responseXML = doc;
13047                 }
13048             }
13049             catch(e) {
13050                 // ignore
13051             }
13052
13053             Roo.EventManager.removeListener(frame, 'load', cb, this);
13054
13055             this.fireEvent("requestcomplete", this, r, o);
13056             Roo.callback(o.success, o.scope, [r, o]);
13057             Roo.callback(o.callback, o.scope, [o, true, r]);
13058
13059             setTimeout(function(){document.body.removeChild(frame);}, 100);
13060         }
13061
13062         Roo.EventManager.on(frame, 'load', cb, this);
13063         form.submit();
13064
13065         if(hiddens){ // remove dynamic params
13066             for(var i = 0, len = hiddens.length; i < len; i++){
13067                 form.removeChild(hiddens[i]);
13068             }
13069         }
13070     },
13071     // this is a 'formdata version???'
13072     
13073     
13074     doFormDataUpload : function(o,  url)
13075     {
13076         var formData;
13077         if (o.form) {
13078             var form =  Roo.getDom(o.form);
13079             form.enctype = form.encoding = 'multipart/form-data';
13080             formData = o.formData === true ? new FormData(form) : o.formData;
13081         } else {
13082             formData = o.formData === true ? new FormData() : o.formData;
13083         }
13084         
13085       
13086         var cb = {
13087             success: this.handleResponse,
13088             failure: this.handleFailure,
13089             scope: this,
13090             argument: {options: o},
13091             timeout : o.timeout || this.timeout
13092         };
13093  
13094         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13095             if(o.autoAbort){
13096                 this.abort();
13097             }
13098         }else if(this.autoAbort !== false){
13099             this.abort();
13100         }
13101
13102         //Roo.lib.Ajax.defaultPostHeader = null;
13103         Roo.lib.Ajax.useDefaultHeader = false;
13104         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13105         Roo.lib.Ajax.useDefaultHeader = true;
13106  
13107          
13108     }
13109     
13110 });
13111 /*
13112  * Based on:
13113  * Ext JS Library 1.1.1
13114  * Copyright(c) 2006-2007, Ext JS, LLC.
13115  *
13116  * Originally Released Under LGPL - original licence link has changed is not relivant.
13117  *
13118  * Fork - LGPL
13119  * <script type="text/javascript">
13120  */
13121  
13122 /**
13123  * Global Ajax request class.
13124  * 
13125  * @class Roo.Ajax
13126  * @extends Roo.data.Connection
13127  * @static
13128  * 
13129  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13130  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13131  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13132  * @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)
13133  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13134  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13135  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13136  */
13137 Roo.Ajax = new Roo.data.Connection({
13138     // fix up the docs
13139     /**
13140      * @scope Roo.Ajax
13141      * @type {Boolear} 
13142      */
13143     autoAbort : false,
13144
13145     /**
13146      * Serialize the passed form into a url encoded string
13147      * @scope Roo.Ajax
13148      * @param {String/HTMLElement} form
13149      * @return {String}
13150      */
13151     serializeForm : function(form){
13152         return Roo.lib.Ajax.serializeForm(form);
13153     }
13154 });/*
13155  * Based on:
13156  * Ext JS Library 1.1.1
13157  * Copyright(c) 2006-2007, Ext JS, LLC.
13158  *
13159  * Originally Released Under LGPL - original licence link has changed is not relivant.
13160  *
13161  * Fork - LGPL
13162  * <script type="text/javascript">
13163  */
13164
13165  
13166 /**
13167  * @class Roo.UpdateManager
13168  * @extends Roo.util.Observable
13169  * Provides AJAX-style update for Element object.<br><br>
13170  * Usage:<br>
13171  * <pre><code>
13172  * // Get it from a Roo.Element object
13173  * var el = Roo.get("foo");
13174  * var mgr = el.getUpdateManager();
13175  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13176  * ...
13177  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13178  * <br>
13179  * // or directly (returns the same UpdateManager instance)
13180  * var mgr = new Roo.UpdateManager("myElementId");
13181  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13182  * mgr.on("update", myFcnNeedsToKnow);
13183  * <br>
13184    // short handed call directly from the element object
13185    Roo.get("foo").load({
13186         url: "bar.php",
13187         scripts:true,
13188         params: "for=bar",
13189         text: "Loading Foo..."
13190    });
13191  * </code></pre>
13192  * @constructor
13193  * Create new UpdateManager directly.
13194  * @param {String/HTMLElement/Roo.Element} el The element to update
13195  * @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).
13196  */
13197 Roo.UpdateManager = function(el, forceNew){
13198     el = Roo.get(el);
13199     if(!forceNew && el.updateManager){
13200         return el.updateManager;
13201     }
13202     /**
13203      * The Element object
13204      * @type Roo.Element
13205      */
13206     this.el = el;
13207     /**
13208      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13209      * @type String
13210      */
13211     this.defaultUrl = null;
13212
13213     this.addEvents({
13214         /**
13215          * @event beforeupdate
13216          * Fired before an update is made, return false from your handler and the update is cancelled.
13217          * @param {Roo.Element} el
13218          * @param {String/Object/Function} url
13219          * @param {String/Object} params
13220          */
13221         "beforeupdate": true,
13222         /**
13223          * @event update
13224          * Fired after successful update is made.
13225          * @param {Roo.Element} el
13226          * @param {Object} oResponseObject The response Object
13227          */
13228         "update": true,
13229         /**
13230          * @event failure
13231          * Fired on update failure.
13232          * @param {Roo.Element} el
13233          * @param {Object} oResponseObject The response Object
13234          */
13235         "failure": true
13236     });
13237     var d = Roo.UpdateManager.defaults;
13238     /**
13239      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13240      * @type String
13241      */
13242     this.sslBlankUrl = d.sslBlankUrl;
13243     /**
13244      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13245      * @type Boolean
13246      */
13247     this.disableCaching = d.disableCaching;
13248     /**
13249      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13250      * @type String
13251      */
13252     this.indicatorText = d.indicatorText;
13253     /**
13254      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13255      * @type String
13256      */
13257     this.showLoadIndicator = d.showLoadIndicator;
13258     /**
13259      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13260      * @type Number
13261      */
13262     this.timeout = d.timeout;
13263
13264     /**
13265      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13266      * @type Boolean
13267      */
13268     this.loadScripts = d.loadScripts;
13269
13270     /**
13271      * Transaction object of current executing transaction
13272      */
13273     this.transaction = null;
13274
13275     /**
13276      * @private
13277      */
13278     this.autoRefreshProcId = null;
13279     /**
13280      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13281      * @type Function
13282      */
13283     this.refreshDelegate = this.refresh.createDelegate(this);
13284     /**
13285      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13286      * @type Function
13287      */
13288     this.updateDelegate = this.update.createDelegate(this);
13289     /**
13290      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13291      * @type Function
13292      */
13293     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13294     /**
13295      * @private
13296      */
13297     this.successDelegate = this.processSuccess.createDelegate(this);
13298     /**
13299      * @private
13300      */
13301     this.failureDelegate = this.processFailure.createDelegate(this);
13302
13303     if(!this.renderer){
13304      /**
13305       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13306       */
13307     this.renderer = new Roo.UpdateManager.BasicRenderer();
13308     }
13309     
13310     Roo.UpdateManager.superclass.constructor.call(this);
13311 };
13312
13313 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13314     /**
13315      * Get the Element this UpdateManager is bound to
13316      * @return {Roo.Element} The element
13317      */
13318     getEl : function(){
13319         return this.el;
13320     },
13321     /**
13322      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13323      * @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:
13324 <pre><code>
13325 um.update({<br/>
13326     url: "your-url.php",<br/>
13327     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13328     callback: yourFunction,<br/>
13329     scope: yourObject, //(optional scope)  <br/>
13330     discardUrl: false, <br/>
13331     nocache: false,<br/>
13332     text: "Loading...",<br/>
13333     timeout: 30,<br/>
13334     scripts: false<br/>
13335 });
13336 </code></pre>
13337      * The only required property is url. The optional properties nocache, text and scripts
13338      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13339      * @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}
13340      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13341      * @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.
13342      */
13343     update : function(url, params, callback, discardUrl){
13344         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13345             var method = this.method,
13346                 cfg;
13347             if(typeof url == "object"){ // must be config object
13348                 cfg = url;
13349                 url = cfg.url;
13350                 params = params || cfg.params;
13351                 callback = callback || cfg.callback;
13352                 discardUrl = discardUrl || cfg.discardUrl;
13353                 if(callback && cfg.scope){
13354                     callback = callback.createDelegate(cfg.scope);
13355                 }
13356                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13357                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13358                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13359                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13360                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13361             }
13362             this.showLoading();
13363             if(!discardUrl){
13364                 this.defaultUrl = url;
13365             }
13366             if(typeof url == "function"){
13367                 url = url.call(this);
13368             }
13369
13370             method = method || (params ? "POST" : "GET");
13371             if(method == "GET"){
13372                 url = this.prepareUrl(url);
13373             }
13374
13375             var o = Roo.apply(cfg ||{}, {
13376                 url : url,
13377                 params: params,
13378                 success: this.successDelegate,
13379                 failure: this.failureDelegate,
13380                 callback: undefined,
13381                 timeout: (this.timeout*1000),
13382                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13383             });
13384             Roo.log("updated manager called with timeout of " + o.timeout);
13385             this.transaction = Roo.Ajax.request(o);
13386         }
13387     },
13388
13389     /**
13390      * 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.
13391      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13392      * @param {String/HTMLElement} form The form Id or form element
13393      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13394      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13395      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13396      */
13397     formUpdate : function(form, url, reset, callback){
13398         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13399             if(typeof url == "function"){
13400                 url = url.call(this);
13401             }
13402             form = Roo.getDom(form);
13403             this.transaction = Roo.Ajax.request({
13404                 form: form,
13405                 url:url,
13406                 success: this.successDelegate,
13407                 failure: this.failureDelegate,
13408                 timeout: (this.timeout*1000),
13409                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13410             });
13411             this.showLoading.defer(1, this);
13412         }
13413     },
13414
13415     /**
13416      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13417      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13418      */
13419     refresh : function(callback){
13420         if(this.defaultUrl == null){
13421             return;
13422         }
13423         this.update(this.defaultUrl, null, callback, true);
13424     },
13425
13426     /**
13427      * Set this element to auto refresh.
13428      * @param {Number} interval How often to update (in seconds).
13429      * @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)
13430      * @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}
13431      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13432      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13433      */
13434     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13435         if(refreshNow){
13436             this.update(url || this.defaultUrl, params, callback, true);
13437         }
13438         if(this.autoRefreshProcId){
13439             clearInterval(this.autoRefreshProcId);
13440         }
13441         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13442     },
13443
13444     /**
13445      * Stop auto refresh on this element.
13446      */
13447      stopAutoRefresh : function(){
13448         if(this.autoRefreshProcId){
13449             clearInterval(this.autoRefreshProcId);
13450             delete this.autoRefreshProcId;
13451         }
13452     },
13453
13454     isAutoRefreshing : function(){
13455        return this.autoRefreshProcId ? true : false;
13456     },
13457     /**
13458      * Called to update the element to "Loading" state. Override to perform custom action.
13459      */
13460     showLoading : function(){
13461         if(this.showLoadIndicator){
13462             this.el.update(this.indicatorText);
13463         }
13464     },
13465
13466     /**
13467      * Adds unique parameter to query string if disableCaching = true
13468      * @private
13469      */
13470     prepareUrl : function(url){
13471         if(this.disableCaching){
13472             var append = "_dc=" + (new Date().getTime());
13473             if(url.indexOf("?") !== -1){
13474                 url += "&" + append;
13475             }else{
13476                 url += "?" + append;
13477             }
13478         }
13479         return url;
13480     },
13481
13482     /**
13483      * @private
13484      */
13485     processSuccess : function(response){
13486         this.transaction = null;
13487         if(response.argument.form && response.argument.reset){
13488             try{ // put in try/catch since some older FF releases had problems with this
13489                 response.argument.form.reset();
13490             }catch(e){}
13491         }
13492         if(this.loadScripts){
13493             this.renderer.render(this.el, response, this,
13494                 this.updateComplete.createDelegate(this, [response]));
13495         }else{
13496             this.renderer.render(this.el, response, this);
13497             this.updateComplete(response);
13498         }
13499     },
13500
13501     updateComplete : function(response){
13502         this.fireEvent("update", this.el, response);
13503         if(typeof response.argument.callback == "function"){
13504             response.argument.callback(this.el, true, response);
13505         }
13506     },
13507
13508     /**
13509      * @private
13510      */
13511     processFailure : function(response){
13512         this.transaction = null;
13513         this.fireEvent("failure", this.el, response);
13514         if(typeof response.argument.callback == "function"){
13515             response.argument.callback(this.el, false, response);
13516         }
13517     },
13518
13519     /**
13520      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13521      * @param {Object} renderer The object implementing the render() method
13522      */
13523     setRenderer : function(renderer){
13524         this.renderer = renderer;
13525     },
13526
13527     getRenderer : function(){
13528        return this.renderer;
13529     },
13530
13531     /**
13532      * Set the defaultUrl used for updates
13533      * @param {String/Function} defaultUrl The url or a function to call to get the url
13534      */
13535     setDefaultUrl : function(defaultUrl){
13536         this.defaultUrl = defaultUrl;
13537     },
13538
13539     /**
13540      * Aborts the executing transaction
13541      */
13542     abort : function(){
13543         if(this.transaction){
13544             Roo.Ajax.abort(this.transaction);
13545         }
13546     },
13547
13548     /**
13549      * Returns true if an update is in progress
13550      * @return {Boolean}
13551      */
13552     isUpdating : function(){
13553         if(this.transaction){
13554             return Roo.Ajax.isLoading(this.transaction);
13555         }
13556         return false;
13557     }
13558 });
13559
13560 /**
13561  * @class Roo.UpdateManager.defaults
13562  * @static (not really - but it helps the doc tool)
13563  * The defaults collection enables customizing the default properties of UpdateManager
13564  */
13565    Roo.UpdateManager.defaults = {
13566        /**
13567          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13568          * @type Number
13569          */
13570          timeout : 30,
13571
13572          /**
13573          * True to process scripts by default (Defaults to false).
13574          * @type Boolean
13575          */
13576         loadScripts : false,
13577
13578         /**
13579         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13580         * @type String
13581         */
13582         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13583         /**
13584          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13585          * @type Boolean
13586          */
13587         disableCaching : false,
13588         /**
13589          * Whether to show indicatorText when loading (Defaults to true).
13590          * @type Boolean
13591          */
13592         showLoadIndicator : true,
13593         /**
13594          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13595          * @type String
13596          */
13597         indicatorText : '<div class="loading-indicator">Loading...</div>'
13598    };
13599
13600 /**
13601  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13602  *Usage:
13603  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13604  * @param {String/HTMLElement/Roo.Element} el The element to update
13605  * @param {String} url The url
13606  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13607  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13608  * @static
13609  * @deprecated
13610  * @member Roo.UpdateManager
13611  */
13612 Roo.UpdateManager.updateElement = function(el, url, params, options){
13613     var um = Roo.get(el, true).getUpdateManager();
13614     Roo.apply(um, options);
13615     um.update(url, params, options ? options.callback : null);
13616 };
13617 // alias for backwards compat
13618 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13619 /**
13620  * @class Roo.UpdateManager.BasicRenderer
13621  * Default Content renderer. Updates the elements innerHTML with the responseText.
13622  */
13623 Roo.UpdateManager.BasicRenderer = function(){};
13624
13625 Roo.UpdateManager.BasicRenderer.prototype = {
13626     /**
13627      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13628      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13629      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13630      * @param {Roo.Element} el The element being rendered
13631      * @param {Object} response The YUI Connect response object
13632      * @param {UpdateManager} updateManager The calling update manager
13633      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13634      */
13635      render : function(el, response, updateManager, callback){
13636         el.update(response.responseText, updateManager.loadScripts, callback);
13637     }
13638 };
13639 /*
13640  * Based on:
13641  * Roo JS
13642  * (c)) Alan Knowles
13643  * Licence : LGPL
13644  */
13645
13646
13647 /**
13648  * @class Roo.DomTemplate
13649  * @extends Roo.Template
13650  * An effort at a dom based template engine..
13651  *
13652  * Similar to XTemplate, except it uses dom parsing to create the template..
13653  *
13654  * Supported features:
13655  *
13656  *  Tags:
13657
13658 <pre><code>
13659       {a_variable} - output encoded.
13660       {a_variable.format:("Y-m-d")} - call a method on the variable
13661       {a_variable:raw} - unencoded output
13662       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13663       {a_variable:this.method_on_template(...)} - call a method on the template object.
13664  
13665 </code></pre>
13666  *  The tpl tag:
13667 <pre><code>
13668         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13669         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13670         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13671         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13672   
13673 </code></pre>
13674  *      
13675  */
13676 Roo.DomTemplate = function()
13677 {
13678      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13679      if (this.html) {
13680         this.compile();
13681      }
13682 };
13683
13684
13685 Roo.extend(Roo.DomTemplate, Roo.Template, {
13686     /**
13687      * id counter for sub templates.
13688      */
13689     id : 0,
13690     /**
13691      * flag to indicate if dom parser is inside a pre,
13692      * it will strip whitespace if not.
13693      */
13694     inPre : false,
13695     
13696     /**
13697      * The various sub templates
13698      */
13699     tpls : false,
13700     
13701     
13702     
13703     /**
13704      *
13705      * basic tag replacing syntax
13706      * WORD:WORD()
13707      *
13708      * // you can fake an object call by doing this
13709      *  x.t:(test,tesT) 
13710      * 
13711      */
13712     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13713     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13714     
13715     iterChild : function (node, method) {
13716         
13717         var oldPre = this.inPre;
13718         if (node.tagName == 'PRE') {
13719             this.inPre = true;
13720         }
13721         for( var i = 0; i < node.childNodes.length; i++) {
13722             method.call(this, node.childNodes[i]);
13723         }
13724         this.inPre = oldPre;
13725     },
13726     
13727     
13728     
13729     /**
13730      * compile the template
13731      *
13732      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13733      *
13734      */
13735     compile: function()
13736     {
13737         var s = this.html;
13738         
13739         // covert the html into DOM...
13740         var doc = false;
13741         var div =false;
13742         try {
13743             doc = document.implementation.createHTMLDocument("");
13744             doc.documentElement.innerHTML =   this.html  ;
13745             div = doc.documentElement;
13746         } catch (e) {
13747             // old IE... - nasty -- it causes all sorts of issues.. with
13748             // images getting pulled from server..
13749             div = document.createElement('div');
13750             div.innerHTML = this.html;
13751         }
13752         //doc.documentElement.innerHTML = htmlBody
13753          
13754         
13755         
13756         this.tpls = [];
13757         var _t = this;
13758         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13759         
13760         var tpls = this.tpls;
13761         
13762         // create a top level template from the snippet..
13763         
13764         //Roo.log(div.innerHTML);
13765         
13766         var tpl = {
13767             uid : 'master',
13768             id : this.id++,
13769             attr : false,
13770             value : false,
13771             body : div.innerHTML,
13772             
13773             forCall : false,
13774             execCall : false,
13775             dom : div,
13776             isTop : true
13777             
13778         };
13779         tpls.unshift(tpl);
13780         
13781         
13782         // compile them...
13783         this.tpls = [];
13784         Roo.each(tpls, function(tp){
13785             this.compileTpl(tp);
13786             this.tpls[tp.id] = tp;
13787         }, this);
13788         
13789         this.master = tpls[0];
13790         return this;
13791         
13792         
13793     },
13794     
13795     compileNode : function(node, istop) {
13796         // test for
13797         //Roo.log(node);
13798         
13799         
13800         // skip anything not a tag..
13801         if (node.nodeType != 1) {
13802             if (node.nodeType == 3 && !this.inPre) {
13803                 // reduce white space..
13804                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13805                 
13806             }
13807             return;
13808         }
13809         
13810         var tpl = {
13811             uid : false,
13812             id : false,
13813             attr : false,
13814             value : false,
13815             body : '',
13816             
13817             forCall : false,
13818             execCall : false,
13819             dom : false,
13820             isTop : istop
13821             
13822             
13823         };
13824         
13825         
13826         switch(true) {
13827             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13828             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13829             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13830             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13831             // no default..
13832         }
13833         
13834         
13835         if (!tpl.attr) {
13836             // just itterate children..
13837             this.iterChild(node,this.compileNode);
13838             return;
13839         }
13840         tpl.uid = this.id++;
13841         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13842         node.removeAttribute('roo-'+ tpl.attr);
13843         if (tpl.attr != 'name') {
13844             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13845             node.parentNode.replaceChild(placeholder,  node);
13846         } else {
13847             
13848             var placeholder =  document.createElement('span');
13849             placeholder.className = 'roo-tpl-' + tpl.value;
13850             node.parentNode.replaceChild(placeholder,  node);
13851         }
13852         
13853         // parent now sees '{domtplXXXX}
13854         this.iterChild(node,this.compileNode);
13855         
13856         // we should now have node body...
13857         var div = document.createElement('div');
13858         div.appendChild(node);
13859         tpl.dom = node;
13860         // this has the unfortunate side effect of converting tagged attributes
13861         // eg. href="{...}" into %7C...%7D
13862         // this has been fixed by searching for those combo's although it's a bit hacky..
13863         
13864         
13865         tpl.body = div.innerHTML;
13866         
13867         
13868          
13869         tpl.id = tpl.uid;
13870         switch(tpl.attr) {
13871             case 'for' :
13872                 switch (tpl.value) {
13873                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13874                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13875                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13876                 }
13877                 break;
13878             
13879             case 'exec':
13880                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13881                 break;
13882             
13883             case 'if':     
13884                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13885                 break;
13886             
13887             case 'name':
13888                 tpl.id  = tpl.value; // replace non characters???
13889                 break;
13890             
13891         }
13892         
13893         
13894         this.tpls.push(tpl);
13895         
13896         
13897         
13898     },
13899     
13900     
13901     
13902     
13903     /**
13904      * Compile a segment of the template into a 'sub-template'
13905      *
13906      * 
13907      * 
13908      *
13909      */
13910     compileTpl : function(tpl)
13911     {
13912         var fm = Roo.util.Format;
13913         var useF = this.disableFormats !== true;
13914         
13915         var sep = Roo.isGecko ? "+\n" : ",\n";
13916         
13917         var undef = function(str) {
13918             Roo.debug && Roo.log("Property not found :"  + str);
13919             return '';
13920         };
13921           
13922         //Roo.log(tpl.body);
13923         
13924         
13925         
13926         var fn = function(m, lbrace, name, format, args)
13927         {
13928             //Roo.log("ARGS");
13929             //Roo.log(arguments);
13930             args = args ? args.replace(/\\'/g,"'") : args;
13931             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13932             if (typeof(format) == 'undefined') {
13933                 format =  'htmlEncode'; 
13934             }
13935             if (format == 'raw' ) {
13936                 format = false;
13937             }
13938             
13939             if(name.substr(0, 6) == 'domtpl'){
13940                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13941             }
13942             
13943             // build an array of options to determine if value is undefined..
13944             
13945             // basically get 'xxxx.yyyy' then do
13946             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13947             //    (function () { Roo.log("Property not found"); return ''; })() :
13948             //    ......
13949             
13950             var udef_ar = [];
13951             var lookfor = '';
13952             Roo.each(name.split('.'), function(st) {
13953                 lookfor += (lookfor.length ? '.': '') + st;
13954                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13955             });
13956             
13957             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13958             
13959             
13960             if(format && useF){
13961                 
13962                 args = args ? ',' + args : "";
13963                  
13964                 if(format.substr(0, 5) != "this."){
13965                     format = "fm." + format + '(';
13966                 }else{
13967                     format = 'this.call("'+ format.substr(5) + '", ';
13968                     args = ", values";
13969                 }
13970                 
13971                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13972             }
13973              
13974             if (args && args.length) {
13975                 // called with xxyx.yuu:(test,test)
13976                 // change to ()
13977                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13978             }
13979             // raw.. - :raw modifier..
13980             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13981             
13982         };
13983         var body;
13984         // branched to use + in gecko and [].join() in others
13985         if(Roo.isGecko){
13986             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13987                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13988                     "';};};";
13989         }else{
13990             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13991             body.push(tpl.body.replace(/(\r\n|\n)/g,
13992                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13993             body.push("'].join('');};};");
13994             body = body.join('');
13995         }
13996         
13997         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13998        
13999         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14000         eval(body);
14001         
14002         return this;
14003     },
14004      
14005     /**
14006      * same as applyTemplate, except it's done to one of the subTemplates
14007      * when using named templates, you can do:
14008      *
14009      * var str = pl.applySubTemplate('your-name', values);
14010      *
14011      * 
14012      * @param {Number} id of the template
14013      * @param {Object} values to apply to template
14014      * @param {Object} parent (normaly the instance of this object)
14015      */
14016     applySubTemplate : function(id, values, parent)
14017     {
14018         
14019         
14020         var t = this.tpls[id];
14021         
14022         
14023         try { 
14024             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14025                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14026                 return '';
14027             }
14028         } catch(e) {
14029             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14030             Roo.log(values);
14031           
14032             return '';
14033         }
14034         try { 
14035             
14036             if(t.execCall && t.execCall.call(this, values, parent)){
14037                 return '';
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         
14045         try {
14046             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14047             parent = t.target ? values : parent;
14048             if(t.forCall && vs instanceof Array){
14049                 var buf = [];
14050                 for(var i = 0, len = vs.length; i < len; i++){
14051                     try {
14052                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14053                     } catch (e) {
14054                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14055                         Roo.log(e.body);
14056                         //Roo.log(t.compiled);
14057                         Roo.log(vs[i]);
14058                     }   
14059                 }
14060                 return buf.join('');
14061             }
14062         } catch (e) {
14063             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14064             Roo.log(values);
14065             return '';
14066         }
14067         try {
14068             return t.compiled.call(this, vs, parent);
14069         } catch (e) {
14070             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14071             Roo.log(e.body);
14072             //Roo.log(t.compiled);
14073             Roo.log(values);
14074             return '';
14075         }
14076     },
14077
14078    
14079
14080     applyTemplate : function(values){
14081         return this.master.compiled.call(this, values, {});
14082         //var s = this.subs;
14083     },
14084
14085     apply : function(){
14086         return this.applyTemplate.apply(this, arguments);
14087     }
14088
14089  });
14090
14091 Roo.DomTemplate.from = function(el){
14092     el = Roo.getDom(el);
14093     return new Roo.Domtemplate(el.value || el.innerHTML);
14094 };/*
14095  * Based on:
14096  * Ext JS Library 1.1.1
14097  * Copyright(c) 2006-2007, Ext JS, LLC.
14098  *
14099  * Originally Released Under LGPL - original licence link has changed is not relivant.
14100  *
14101  * Fork - LGPL
14102  * <script type="text/javascript">
14103  */
14104
14105 /**
14106  * @class Roo.util.DelayedTask
14107  * Provides a convenient method of performing setTimeout where a new
14108  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14109  * You can use this class to buffer
14110  * the keypress events for a certain number of milliseconds, and perform only if they stop
14111  * for that amount of time.
14112  * @constructor The parameters to this constructor serve as defaults and are not required.
14113  * @param {Function} fn (optional) The default function to timeout
14114  * @param {Object} scope (optional) The default scope of that timeout
14115  * @param {Array} args (optional) The default Array of arguments
14116  */
14117 Roo.util.DelayedTask = function(fn, scope, args){
14118     var id = null, d, t;
14119
14120     var call = function(){
14121         var now = new Date().getTime();
14122         if(now - t >= d){
14123             clearInterval(id);
14124             id = null;
14125             fn.apply(scope, args || []);
14126         }
14127     };
14128     /**
14129      * Cancels any pending timeout and queues a new one
14130      * @param {Number} delay The milliseconds to delay
14131      * @param {Function} newFn (optional) Overrides function passed to constructor
14132      * @param {Object} newScope (optional) Overrides scope passed to constructor
14133      * @param {Array} newArgs (optional) Overrides args passed to constructor
14134      */
14135     this.delay = function(delay, newFn, newScope, newArgs){
14136         if(id && delay != d){
14137             this.cancel();
14138         }
14139         d = delay;
14140         t = new Date().getTime();
14141         fn = newFn || fn;
14142         scope = newScope || scope;
14143         args = newArgs || args;
14144         if(!id){
14145             id = setInterval(call, d);
14146         }
14147     };
14148
14149     /**
14150      * Cancel the last queued timeout
14151      */
14152     this.cancel = function(){
14153         if(id){
14154             clearInterval(id);
14155             id = null;
14156         }
14157     };
14158 };/*
14159  * Based on:
14160  * Ext JS Library 1.1.1
14161  * Copyright(c) 2006-2007, Ext JS, LLC.
14162  *
14163  * Originally Released Under LGPL - original licence link has changed is not relivant.
14164  *
14165  * Fork - LGPL
14166  * <script type="text/javascript">
14167  */
14168 /**
14169  * @class Roo.util.TaskRunner
14170  * Manage background tasks - not sure why this is better that setInterval?
14171  * @static
14172  *
14173  */
14174  
14175 Roo.util.TaskRunner = function(interval){
14176     interval = interval || 10;
14177     var tasks = [], removeQueue = [];
14178     var id = 0;
14179     var running = false;
14180
14181     var stopThread = function(){
14182         running = false;
14183         clearInterval(id);
14184         id = 0;
14185     };
14186
14187     var startThread = function(){
14188         if(!running){
14189             running = true;
14190             id = setInterval(runTasks, interval);
14191         }
14192     };
14193
14194     var removeTask = function(task){
14195         removeQueue.push(task);
14196         if(task.onStop){
14197             task.onStop();
14198         }
14199     };
14200
14201     var runTasks = function(){
14202         if(removeQueue.length > 0){
14203             for(var i = 0, len = removeQueue.length; i < len; i++){
14204                 tasks.remove(removeQueue[i]);
14205             }
14206             removeQueue = [];
14207             if(tasks.length < 1){
14208                 stopThread();
14209                 return;
14210             }
14211         }
14212         var now = new Date().getTime();
14213         for(var i = 0, len = tasks.length; i < len; ++i){
14214             var t = tasks[i];
14215             var itime = now - t.taskRunTime;
14216             if(t.interval <= itime){
14217                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14218                 t.taskRunTime = now;
14219                 if(rt === false || t.taskRunCount === t.repeat){
14220                     removeTask(t);
14221                     return;
14222                 }
14223             }
14224             if(t.duration && t.duration <= (now - t.taskStartTime)){
14225                 removeTask(t);
14226             }
14227         }
14228     };
14229
14230     /**
14231      * Queues a new task.
14232      * @param {Object} task
14233      *
14234      * Task property : interval = how frequent to run.
14235      * Task object should implement
14236      * function run()
14237      * Task object may implement
14238      * function onStop()
14239      */
14240     this.start = function(task){
14241         tasks.push(task);
14242         task.taskStartTime = new Date().getTime();
14243         task.taskRunTime = 0;
14244         task.taskRunCount = 0;
14245         startThread();
14246         return task;
14247     };
14248     /**
14249      * Stop  new task.
14250      * @param {Object} task
14251      */
14252     this.stop = function(task){
14253         removeTask(task);
14254         return task;
14255     };
14256     /**
14257      * Stop all Tasks
14258      */
14259     this.stopAll = function(){
14260         stopThread();
14261         for(var i = 0, len = tasks.length; i < len; i++){
14262             if(tasks[i].onStop){
14263                 tasks[i].onStop();
14264             }
14265         }
14266         tasks = [];
14267         removeQueue = [];
14268     };
14269 };
14270
14271 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281
14282  
14283 /**
14284  * @class Roo.util.MixedCollection
14285  * @extends Roo.util.Observable
14286  * A Collection class that maintains both numeric indexes and keys and exposes events.
14287  * @constructor
14288  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14289  * collection (defaults to false)
14290  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14291  * and return the key value for that item.  This is used when available to look up the key on items that
14292  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14293  * equivalent to providing an implementation for the {@link #getKey} method.
14294  */
14295 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14296     this.items = [];
14297     this.map = {};
14298     this.keys = [];
14299     this.length = 0;
14300     this.addEvents({
14301         /**
14302          * @event clear
14303          * Fires when the collection is cleared.
14304          */
14305         "clear" : true,
14306         /**
14307          * @event add
14308          * Fires when an item is added to the collection.
14309          * @param {Number} index The index at which the item was added.
14310          * @param {Object} o The item added.
14311          * @param {String} key The key associated with the added item.
14312          */
14313         "add" : true,
14314         /**
14315          * @event replace
14316          * Fires when an item is replaced in the collection.
14317          * @param {String} key he key associated with the new added.
14318          * @param {Object} old The item being replaced.
14319          * @param {Object} new The new item.
14320          */
14321         "replace" : true,
14322         /**
14323          * @event remove
14324          * Fires when an item is removed from the collection.
14325          * @param {Object} o The item being removed.
14326          * @param {String} key (optional) The key associated with the removed item.
14327          */
14328         "remove" : true,
14329         "sort" : true
14330     });
14331     this.allowFunctions = allowFunctions === true;
14332     if(keyFn){
14333         this.getKey = keyFn;
14334     }
14335     Roo.util.MixedCollection.superclass.constructor.call(this);
14336 };
14337
14338 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14339     allowFunctions : false,
14340     
14341 /**
14342  * Adds an item to the collection.
14343  * @param {String} key The key to associate with the item
14344  * @param {Object} o The item to add.
14345  * @return {Object} The item added.
14346  */
14347     add : function(key, o){
14348         if(arguments.length == 1){
14349             o = arguments[0];
14350             key = this.getKey(o);
14351         }
14352         if(typeof key == "undefined" || key === null){
14353             this.length++;
14354             this.items.push(o);
14355             this.keys.push(null);
14356         }else{
14357             var old = this.map[key];
14358             if(old){
14359                 return this.replace(key, o);
14360             }
14361             this.length++;
14362             this.items.push(o);
14363             this.map[key] = o;
14364             this.keys.push(key);
14365         }
14366         this.fireEvent("add", this.length-1, o, key);
14367         return o;
14368     },
14369        
14370 /**
14371   * MixedCollection has a generic way to fetch keys if you implement getKey.
14372 <pre><code>
14373 // normal way
14374 var mc = new Roo.util.MixedCollection();
14375 mc.add(someEl.dom.id, someEl);
14376 mc.add(otherEl.dom.id, otherEl);
14377 //and so on
14378
14379 // using getKey
14380 var mc = new Roo.util.MixedCollection();
14381 mc.getKey = function(el){
14382    return el.dom.id;
14383 };
14384 mc.add(someEl);
14385 mc.add(otherEl);
14386
14387 // or via the constructor
14388 var mc = new Roo.util.MixedCollection(false, function(el){
14389    return el.dom.id;
14390 });
14391 mc.add(someEl);
14392 mc.add(otherEl);
14393 </code></pre>
14394  * @param o {Object} The item for which to find the key.
14395  * @return {Object} The key for the passed item.
14396  */
14397     getKey : function(o){
14398          return o.id; 
14399     },
14400    
14401 /**
14402  * Replaces an item in the collection.
14403  * @param {String} key The key associated with the item to replace, or the item to replace.
14404  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14405  * @return {Object}  The new item.
14406  */
14407     replace : function(key, o){
14408         if(arguments.length == 1){
14409             o = arguments[0];
14410             key = this.getKey(o);
14411         }
14412         var old = this.item(key);
14413         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14414              return this.add(key, o);
14415         }
14416         var index = this.indexOfKey(key);
14417         this.items[index] = o;
14418         this.map[key] = o;
14419         this.fireEvent("replace", key, old, o);
14420         return o;
14421     },
14422    
14423 /**
14424  * Adds all elements of an Array or an Object to the collection.
14425  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14426  * an Array of values, each of which are added to the collection.
14427  */
14428     addAll : function(objs){
14429         if(arguments.length > 1 || objs instanceof Array){
14430             var args = arguments.length > 1 ? arguments : objs;
14431             for(var i = 0, len = args.length; i < len; i++){
14432                 this.add(args[i]);
14433             }
14434         }else{
14435             for(var key in objs){
14436                 if(this.allowFunctions || typeof objs[key] != "function"){
14437                     this.add(key, objs[key]);
14438                 }
14439             }
14440         }
14441     },
14442    
14443 /**
14444  * Executes the specified function once for every item in the collection, passing each
14445  * item as the first and only parameter. returning false from the function will stop the iteration.
14446  * @param {Function} fn The function to execute for each item.
14447  * @param {Object} scope (optional) The scope in which to execute the function.
14448  */
14449     each : function(fn, scope){
14450         var items = [].concat(this.items); // each safe for removal
14451         for(var i = 0, len = items.length; i < len; i++){
14452             if(fn.call(scope || items[i], items[i], i, len) === false){
14453                 break;
14454             }
14455         }
14456     },
14457    
14458 /**
14459  * Executes the specified function once for every key in the collection, passing each
14460  * key, and its associated item as the first two parameters.
14461  * @param {Function} fn The function to execute for each item.
14462  * @param {Object} scope (optional) The scope in which to execute the function.
14463  */
14464     eachKey : function(fn, scope){
14465         for(var i = 0, len = this.keys.length; i < len; i++){
14466             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14467         }
14468     },
14469    
14470 /**
14471  * Returns the first item in the collection which elicits a true return value from the
14472  * passed selection function.
14473  * @param {Function} fn The selection function to execute for each item.
14474  * @param {Object} scope (optional) The scope in which to execute the function.
14475  * @return {Object} The first item in the collection which returned true from the selection function.
14476  */
14477     find : function(fn, scope){
14478         for(var i = 0, len = this.items.length; i < len; i++){
14479             if(fn.call(scope || window, this.items[i], this.keys[i])){
14480                 return this.items[i];
14481             }
14482         }
14483         return null;
14484     },
14485    
14486 /**
14487  * Inserts an item at the specified index in the collection.
14488  * @param {Number} index The index to insert the item at.
14489  * @param {String} key The key to associate with the new item, or the item itself.
14490  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14491  * @return {Object} The item inserted.
14492  */
14493     insert : function(index, key, o){
14494         if(arguments.length == 2){
14495             o = arguments[1];
14496             key = this.getKey(o);
14497         }
14498         if(index >= this.length){
14499             return this.add(key, o);
14500         }
14501         this.length++;
14502         this.items.splice(index, 0, o);
14503         if(typeof key != "undefined" && key != null){
14504             this.map[key] = o;
14505         }
14506         this.keys.splice(index, 0, key);
14507         this.fireEvent("add", index, o, key);
14508         return o;
14509     },
14510    
14511 /**
14512  * Removed an item from the collection.
14513  * @param {Object} o The item to remove.
14514  * @return {Object} The item removed.
14515  */
14516     remove : function(o){
14517         return this.removeAt(this.indexOf(o));
14518     },
14519    
14520 /**
14521  * Remove an item from a specified index in the collection.
14522  * @param {Number} index The index within the collection of the item to remove.
14523  */
14524     removeAt : function(index){
14525         if(index < this.length && index >= 0){
14526             this.length--;
14527             var o = this.items[index];
14528             this.items.splice(index, 1);
14529             var key = this.keys[index];
14530             if(typeof key != "undefined"){
14531                 delete this.map[key];
14532             }
14533             this.keys.splice(index, 1);
14534             this.fireEvent("remove", o, key);
14535         }
14536     },
14537    
14538 /**
14539  * Removed an item associated with the passed key fom the collection.
14540  * @param {String} key The key of the item to remove.
14541  */
14542     removeKey : function(key){
14543         return this.removeAt(this.indexOfKey(key));
14544     },
14545    
14546 /**
14547  * Returns the number of items in the collection.
14548  * @return {Number} the number of items in the collection.
14549  */
14550     getCount : function(){
14551         return this.length; 
14552     },
14553    
14554 /**
14555  * Returns index within the collection of the passed Object.
14556  * @param {Object} o The item to find the index of.
14557  * @return {Number} index of the item.
14558  */
14559     indexOf : function(o){
14560         if(!this.items.indexOf){
14561             for(var i = 0, len = this.items.length; i < len; i++){
14562                 if(this.items[i] == o) {
14563                     return i;
14564                 }
14565             }
14566             return -1;
14567         }else{
14568             return this.items.indexOf(o);
14569         }
14570     },
14571    
14572 /**
14573  * Returns index within the collection of the passed key.
14574  * @param {String} key The key to find the index of.
14575  * @return {Number} index of the key.
14576  */
14577     indexOfKey : function(key){
14578         if(!this.keys.indexOf){
14579             for(var i = 0, len = this.keys.length; i < len; i++){
14580                 if(this.keys[i] == key) {
14581                     return i;
14582                 }
14583             }
14584             return -1;
14585         }else{
14586             return this.keys.indexOf(key);
14587         }
14588     },
14589    
14590 /**
14591  * Returns the item associated with the passed key OR index. Key has priority over index.
14592  * @param {String/Number} key The key or index of the item.
14593  * @return {Object} The item associated with the passed key.
14594  */
14595     item : function(key){
14596         if (key === 'length') {
14597             return null;
14598         }
14599         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14600         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14601     },
14602     
14603 /**
14604  * Returns the item at the specified index.
14605  * @param {Number} index The index of the item.
14606  * @return {Object}
14607  */
14608     itemAt : function(index){
14609         return this.items[index];
14610     },
14611     
14612 /**
14613  * Returns the item associated with the passed key.
14614  * @param {String/Number} key The key of the item.
14615  * @return {Object} The item associated with the passed key.
14616  */
14617     key : function(key){
14618         return this.map[key];
14619     },
14620    
14621 /**
14622  * Returns true if the collection contains the passed Object as an item.
14623  * @param {Object} o  The Object to look for in the collection.
14624  * @return {Boolean} True if the collection contains the Object as an item.
14625  */
14626     contains : function(o){
14627         return this.indexOf(o) != -1;
14628     },
14629    
14630 /**
14631  * Returns true if the collection contains the passed Object as a key.
14632  * @param {String} key The key to look for in the collection.
14633  * @return {Boolean} True if the collection contains the Object as a key.
14634  */
14635     containsKey : function(key){
14636         return typeof this.map[key] != "undefined";
14637     },
14638    
14639 /**
14640  * Removes all items from the collection.
14641  */
14642     clear : function(){
14643         this.length = 0;
14644         this.items = [];
14645         this.keys = [];
14646         this.map = {};
14647         this.fireEvent("clear");
14648     },
14649    
14650 /**
14651  * Returns the first item in the collection.
14652  * @return {Object} the first item in the collection..
14653  */
14654     first : function(){
14655         return this.items[0]; 
14656     },
14657    
14658 /**
14659  * Returns the last item in the collection.
14660  * @return {Object} the last item in the collection..
14661  */
14662     last : function(){
14663         return this.items[this.length-1];   
14664     },
14665     
14666     _sort : function(property, dir, fn){
14667         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14668         fn = fn || function(a, b){
14669             return a-b;
14670         };
14671         var c = [], k = this.keys, items = this.items;
14672         for(var i = 0, len = items.length; i < len; i++){
14673             c[c.length] = {key: k[i], value: items[i], index: i};
14674         }
14675         c.sort(function(a, b){
14676             var v = fn(a[property], b[property]) * dsc;
14677             if(v == 0){
14678                 v = (a.index < b.index ? -1 : 1);
14679             }
14680             return v;
14681         });
14682         for(var i = 0, len = c.length; i < len; i++){
14683             items[i] = c[i].value;
14684             k[i] = c[i].key;
14685         }
14686         this.fireEvent("sort", this);
14687     },
14688     
14689     /**
14690      * Sorts this collection with the passed comparison function
14691      * @param {String} direction (optional) "ASC" or "DESC"
14692      * @param {Function} fn (optional) comparison function
14693      */
14694     sort : function(dir, fn){
14695         this._sort("value", dir, fn);
14696     },
14697     
14698     /**
14699      * Sorts this collection by keys
14700      * @param {String} direction (optional) "ASC" or "DESC"
14701      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14702      */
14703     keySort : function(dir, fn){
14704         this._sort("key", dir, fn || function(a, b){
14705             return String(a).toUpperCase()-String(b).toUpperCase();
14706         });
14707     },
14708     
14709     /**
14710      * Returns a range of items in this collection
14711      * @param {Number} startIndex (optional) defaults to 0
14712      * @param {Number} endIndex (optional) default to the last item
14713      * @return {Array} An array of items
14714      */
14715     getRange : function(start, end){
14716         var items = this.items;
14717         if(items.length < 1){
14718             return [];
14719         }
14720         start = start || 0;
14721         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14722         var r = [];
14723         if(start <= end){
14724             for(var i = start; i <= end; i++) {
14725                     r[r.length] = items[i];
14726             }
14727         }else{
14728             for(var i = start; i >= end; i--) {
14729                     r[r.length] = items[i];
14730             }
14731         }
14732         return r;
14733     },
14734         
14735     /**
14736      * Filter the <i>objects</i> in this collection by a specific property. 
14737      * Returns a new collection that has been filtered.
14738      * @param {String} property A property on your objects
14739      * @param {String/RegExp} value Either string that the property values 
14740      * should start with or a RegExp to test against the property
14741      * @return {MixedCollection} The new filtered collection
14742      */
14743     filter : function(property, value){
14744         if(!value.exec){ // not a regex
14745             value = String(value);
14746             if(value.length == 0){
14747                 return this.clone();
14748             }
14749             value = new RegExp("^" + Roo.escapeRe(value), "i");
14750         }
14751         return this.filterBy(function(o){
14752             return o && value.test(o[property]);
14753         });
14754         },
14755     
14756     /**
14757      * Filter by a function. * Returns a new collection that has been filtered.
14758      * The passed function will be called with each 
14759      * object in the collection. If the function returns true, the value is included 
14760      * otherwise it is filtered.
14761      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14762      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14763      * @return {MixedCollection} The new filtered collection
14764      */
14765     filterBy : function(fn, scope){
14766         var r = new Roo.util.MixedCollection();
14767         r.getKey = this.getKey;
14768         var k = this.keys, it = this.items;
14769         for(var i = 0, len = it.length; i < len; i++){
14770             if(fn.call(scope||this, it[i], k[i])){
14771                                 r.add(k[i], it[i]);
14772                         }
14773         }
14774         return r;
14775     },
14776     
14777     /**
14778      * Creates a duplicate of this collection
14779      * @return {MixedCollection}
14780      */
14781     clone : function(){
14782         var r = new Roo.util.MixedCollection();
14783         var k = this.keys, it = this.items;
14784         for(var i = 0, len = it.length; i < len; i++){
14785             r.add(k[i], it[i]);
14786         }
14787         r.getKey = this.getKey;
14788         return r;
14789     }
14790 });
14791 /**
14792  * Returns the item associated with the passed key or index.
14793  * @method
14794  * @param {String/Number} key The key or index of the item.
14795  * @return {Object} The item associated with the passed key.
14796  */
14797 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14798  * Based on:
14799  * Ext JS Library 1.1.1
14800  * Copyright(c) 2006-2007, Ext JS, LLC.
14801  *
14802  * Originally Released Under LGPL - original licence link has changed is not relivant.
14803  *
14804  * Fork - LGPL
14805  * <script type="text/javascript">
14806  */
14807 /**
14808  * @class Roo.util.JSON
14809  * Modified version of Douglas Crockford"s json.js that doesn"t
14810  * mess with the Object prototype 
14811  * http://www.json.org/js.html
14812  * @static
14813  */
14814 Roo.util.JSON = new (function(){
14815     var useHasOwn = {}.hasOwnProperty ? true : false;
14816     
14817     // crashes Safari in some instances
14818     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14819     
14820     var pad = function(n) {
14821         return n < 10 ? "0" + n : n;
14822     };
14823     
14824     var m = {
14825         "\b": '\\b',
14826         "\t": '\\t',
14827         "\n": '\\n',
14828         "\f": '\\f',
14829         "\r": '\\r',
14830         '"' : '\\"',
14831         "\\": '\\\\'
14832     };
14833
14834     var encodeString = function(s){
14835         if (/["\\\x00-\x1f]/.test(s)) {
14836             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14837                 var c = m[b];
14838                 if(c){
14839                     return c;
14840                 }
14841                 c = b.charCodeAt();
14842                 return "\\u00" +
14843                     Math.floor(c / 16).toString(16) +
14844                     (c % 16).toString(16);
14845             }) + '"';
14846         }
14847         return '"' + s + '"';
14848     };
14849     
14850     var encodeArray = function(o){
14851         var a = ["["], b, i, l = o.length, v;
14852             for (i = 0; i < l; i += 1) {
14853                 v = o[i];
14854                 switch (typeof v) {
14855                     case "undefined":
14856                     case "function":
14857                     case "unknown":
14858                         break;
14859                     default:
14860                         if (b) {
14861                             a.push(',');
14862                         }
14863                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14864                         b = true;
14865                 }
14866             }
14867             a.push("]");
14868             return a.join("");
14869     };
14870     
14871     var encodeDate = function(o){
14872         return '"' + o.getFullYear() + "-" +
14873                 pad(o.getMonth() + 1) + "-" +
14874                 pad(o.getDate()) + "T" +
14875                 pad(o.getHours()) + ":" +
14876                 pad(o.getMinutes()) + ":" +
14877                 pad(o.getSeconds()) + '"';
14878     };
14879     
14880     /**
14881      * Encodes an Object, Array or other value
14882      * @param {Mixed} o The variable to encode
14883      * @return {String} The JSON string
14884      */
14885     this.encode = function(o)
14886     {
14887         // should this be extended to fully wrap stringify..
14888         
14889         if(typeof o == "undefined" || o === null){
14890             return "null";
14891         }else if(o instanceof Array){
14892             return encodeArray(o);
14893         }else if(o instanceof Date){
14894             return encodeDate(o);
14895         }else if(typeof o == "string"){
14896             return encodeString(o);
14897         }else if(typeof o == "number"){
14898             return isFinite(o) ? String(o) : "null";
14899         }else if(typeof o == "boolean"){
14900             return String(o);
14901         }else {
14902             var a = ["{"], b, i, v;
14903             for (i in o) {
14904                 if(!useHasOwn || o.hasOwnProperty(i)) {
14905                     v = o[i];
14906                     switch (typeof v) {
14907                     case "undefined":
14908                     case "function":
14909                     case "unknown":
14910                         break;
14911                     default:
14912                         if(b){
14913                             a.push(',');
14914                         }
14915                         a.push(this.encode(i), ":",
14916                                 v === null ? "null" : this.encode(v));
14917                         b = true;
14918                     }
14919                 }
14920             }
14921             a.push("}");
14922             return a.join("");
14923         }
14924     };
14925     
14926     /**
14927      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14928      * @param {String} json The JSON string
14929      * @return {Object} The resulting object
14930      */
14931     this.decode = function(json){
14932         
14933         return  /** eval:var:json */ eval("(" + json + ')');
14934     };
14935 })();
14936 /** 
14937  * Shorthand for {@link Roo.util.JSON#encode}
14938  * @member Roo encode 
14939  * @method */
14940 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14941 /** 
14942  * Shorthand for {@link Roo.util.JSON#decode}
14943  * @member Roo decode 
14944  * @method */
14945 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14946 /*
14947  * Based on:
14948  * Ext JS Library 1.1.1
14949  * Copyright(c) 2006-2007, Ext JS, LLC.
14950  *
14951  * Originally Released Under LGPL - original licence link has changed is not relivant.
14952  *
14953  * Fork - LGPL
14954  * <script type="text/javascript">
14955  */
14956  
14957 /**
14958  * @class Roo.util.Format
14959  * Reusable data formatting functions
14960  * @static
14961  */
14962 Roo.util.Format = function(){
14963     var trimRe = /^\s+|\s+$/g;
14964     return {
14965         /**
14966          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14967          * @param {String} value The string to truncate
14968          * @param {Number} length The maximum length to allow before truncating
14969          * @return {String} The converted text
14970          */
14971         ellipsis : function(value, len){
14972             if(value && value.length > len){
14973                 return value.substr(0, len-3)+"...";
14974             }
14975             return value;
14976         },
14977
14978         /**
14979          * Checks a reference and converts it to empty string if it is undefined
14980          * @param {Mixed} value Reference to check
14981          * @return {Mixed} Empty string if converted, otherwise the original value
14982          */
14983         undef : function(value){
14984             return typeof value != "undefined" ? value : "";
14985         },
14986
14987         /**
14988          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14989          * @param {String} value The string to encode
14990          * @return {String} The encoded text
14991          */
14992         htmlEncode : function(value){
14993             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14994         },
14995
14996         /**
14997          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14998          * @param {String} value The string to decode
14999          * @return {String} The decoded text
15000          */
15001         htmlDecode : function(value){
15002             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15003         },
15004
15005         /**
15006          * Trims any whitespace from either side of a string
15007          * @param {String} value The text to trim
15008          * @return {String} The trimmed text
15009          */
15010         trim : function(value){
15011             return String(value).replace(trimRe, "");
15012         },
15013
15014         /**
15015          * Returns a substring from within an original string
15016          * @param {String} value The original text
15017          * @param {Number} start The start index of the substring
15018          * @param {Number} length The length of the substring
15019          * @return {String} The substring
15020          */
15021         substr : function(value, start, length){
15022             return String(value).substr(start, length);
15023         },
15024
15025         /**
15026          * Converts a string to all lower case letters
15027          * @param {String} value The text to convert
15028          * @return {String} The converted text
15029          */
15030         lowercase : function(value){
15031             return String(value).toLowerCase();
15032         },
15033
15034         /**
15035          * Converts a string to all upper case letters
15036          * @param {String} value The text to convert
15037          * @return {String} The converted text
15038          */
15039         uppercase : function(value){
15040             return String(value).toUpperCase();
15041         },
15042
15043         /**
15044          * Converts the first character only of a string to upper case
15045          * @param {String} value The text to convert
15046          * @return {String} The converted text
15047          */
15048         capitalize : function(value){
15049             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15050         },
15051
15052         // private
15053         call : function(value, fn){
15054             if(arguments.length > 2){
15055                 var args = Array.prototype.slice.call(arguments, 2);
15056                 args.unshift(value);
15057                  
15058                 return /** eval:var:value */  eval(fn).apply(window, args);
15059             }else{
15060                 /** eval:var:value */
15061                 return /** eval:var:value */ eval(fn).call(window, value);
15062             }
15063         },
15064
15065        
15066         /**
15067          * safer version of Math.toFixed..??/
15068          * @param {Number/String} value The numeric value to format
15069          * @param {Number/String} value Decimal places 
15070          * @return {String} The formatted currency string
15071          */
15072         toFixed : function(v, n)
15073         {
15074             // why not use to fixed - precision is buggered???
15075             if (!n) {
15076                 return Math.round(v-0);
15077             }
15078             var fact = Math.pow(10,n+1);
15079             v = (Math.round((v-0)*fact))/fact;
15080             var z = (''+fact).substring(2);
15081             if (v == Math.floor(v)) {
15082                 return Math.floor(v) + '.' + z;
15083             }
15084             
15085             // now just padd decimals..
15086             var ps = String(v).split('.');
15087             var fd = (ps[1] + z);
15088             var r = fd.substring(0,n); 
15089             var rm = fd.substring(n); 
15090             if (rm < 5) {
15091                 return ps[0] + '.' + r;
15092             }
15093             r*=1; // turn it into a number;
15094             r++;
15095             if (String(r).length != n) {
15096                 ps[0]*=1;
15097                 ps[0]++;
15098                 r = String(r).substring(1); // chop the end off.
15099             }
15100             
15101             return ps[0] + '.' + r;
15102              
15103         },
15104         
15105         /**
15106          * Format a number as US currency
15107          * @param {Number/String} value The numeric value to format
15108          * @return {String} The formatted currency string
15109          */
15110         usMoney : function(v){
15111             return '$' + Roo.util.Format.number(v);
15112         },
15113         
15114         /**
15115          * Format a number
15116          * eventually this should probably emulate php's number_format
15117          * @param {Number/String} value The numeric value to format
15118          * @param {Number} decimals number of decimal places
15119          * @param {String} delimiter for thousands (default comma)
15120          * @return {String} The formatted currency string
15121          */
15122         number : function(v, decimals, thousandsDelimiter)
15123         {
15124             // multiply and round.
15125             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15126             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15127             
15128             var mul = Math.pow(10, decimals);
15129             var zero = String(mul).substring(1);
15130             v = (Math.round((v-0)*mul))/mul;
15131             
15132             // if it's '0' number.. then
15133             
15134             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15135             v = String(v);
15136             var ps = v.split('.');
15137             var whole = ps[0];
15138             
15139             var r = /(\d+)(\d{3})/;
15140             // add comma's
15141             
15142             if(thousandsDelimiter.length != 0) {
15143                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15144             } 
15145             
15146             var sub = ps[1] ?
15147                     // has decimals..
15148                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15149                     // does not have decimals
15150                     (decimals ? ('.' + zero) : '');
15151             
15152             
15153             return whole + sub ;
15154         },
15155         
15156         /**
15157          * Parse a value into a formatted date using the specified format pattern.
15158          * @param {Mixed} value The value to format
15159          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15160          * @return {String} The formatted date string
15161          */
15162         date : function(v, format){
15163             if(!v){
15164                 return "";
15165             }
15166             if(!(v instanceof Date)){
15167                 v = new Date(Date.parse(v));
15168             }
15169             return v.dateFormat(format || Roo.util.Format.defaults.date);
15170         },
15171
15172         /**
15173          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15174          * @param {String} format Any valid date format string
15175          * @return {Function} The date formatting function
15176          */
15177         dateRenderer : function(format){
15178             return function(v){
15179                 return Roo.util.Format.date(v, format);  
15180             };
15181         },
15182
15183         // private
15184         stripTagsRE : /<\/?[^>]+>/gi,
15185         
15186         /**
15187          * Strips all HTML tags
15188          * @param {Mixed} value The text from which to strip tags
15189          * @return {String} The stripped text
15190          */
15191         stripTags : function(v){
15192             return !v ? v : String(v).replace(this.stripTagsRE, "");
15193         },
15194         
15195         /**
15196          * Size in Mb,Gb etc.
15197          * @param {Number} value The number to be formated
15198          * @param {number} decimals how many decimal places
15199          * @return {String} the formated string
15200          */
15201         size : function(value, decimals)
15202         {
15203             var sizes = ['b', 'k', 'M', 'G', 'T'];
15204             if (value == 0) {
15205                 return 0;
15206             }
15207             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15208             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15209         }
15210         
15211         
15212         
15213     };
15214 }();
15215 Roo.util.Format.defaults = {
15216     date : 'd/M/Y'
15217 };/*
15218  * Based on:
15219  * Ext JS Library 1.1.1
15220  * Copyright(c) 2006-2007, Ext JS, LLC.
15221  *
15222  * Originally Released Under LGPL - original licence link has changed is not relivant.
15223  *
15224  * Fork - LGPL
15225  * <script type="text/javascript">
15226  */
15227
15228
15229  
15230
15231 /**
15232  * @class Roo.MasterTemplate
15233  * @extends Roo.Template
15234  * Provides a template that can have child templates. The syntax is:
15235 <pre><code>
15236 var t = new Roo.MasterTemplate(
15237         '&lt;select name="{name}"&gt;',
15238                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15239         '&lt;/select&gt;'
15240 );
15241 t.add('options', {value: 'foo', text: 'bar'});
15242 // or you can add multiple child elements in one shot
15243 t.addAll('options', [
15244     {value: 'foo', text: 'bar'},
15245     {value: 'foo2', text: 'bar2'},
15246     {value: 'foo3', text: 'bar3'}
15247 ]);
15248 // then append, applying the master template values
15249 t.append('my-form', {name: 'my-select'});
15250 </code></pre>
15251 * A name attribute for the child template is not required if you have only one child
15252 * template or you want to refer to them by index.
15253  */
15254 Roo.MasterTemplate = function(){
15255     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15256     this.originalHtml = this.html;
15257     var st = {};
15258     var m, re = this.subTemplateRe;
15259     re.lastIndex = 0;
15260     var subIndex = 0;
15261     while(m = re.exec(this.html)){
15262         var name = m[1], content = m[2];
15263         st[subIndex] = {
15264             name: name,
15265             index: subIndex,
15266             buffer: [],
15267             tpl : new Roo.Template(content)
15268         };
15269         if(name){
15270             st[name] = st[subIndex];
15271         }
15272         st[subIndex].tpl.compile();
15273         st[subIndex].tpl.call = this.call.createDelegate(this);
15274         subIndex++;
15275     }
15276     this.subCount = subIndex;
15277     this.subs = st;
15278 };
15279 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15280     /**
15281     * The regular expression used to match sub templates
15282     * @type RegExp
15283     * @property
15284     */
15285     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15286
15287     /**
15288      * Applies the passed values to a child template.
15289      * @param {String/Number} name (optional) The name or index of the child template
15290      * @param {Array/Object} values The values to be applied to the template
15291      * @return {MasterTemplate} this
15292      */
15293      add : function(name, values){
15294         if(arguments.length == 1){
15295             values = arguments[0];
15296             name = 0;
15297         }
15298         var s = this.subs[name];
15299         s.buffer[s.buffer.length] = s.tpl.apply(values);
15300         return this;
15301     },
15302
15303     /**
15304      * Applies all the passed values to a child template.
15305      * @param {String/Number} name (optional) The name or index of the child template
15306      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15307      * @param {Boolean} reset (optional) True to reset the template first
15308      * @return {MasterTemplate} this
15309      */
15310     fill : function(name, values, reset){
15311         var a = arguments;
15312         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15313             values = a[0];
15314             name = 0;
15315             reset = a[1];
15316         }
15317         if(reset){
15318             this.reset();
15319         }
15320         for(var i = 0, len = values.length; i < len; i++){
15321             this.add(name, values[i]);
15322         }
15323         return this;
15324     },
15325
15326     /**
15327      * Resets the template for reuse
15328      * @return {MasterTemplate} this
15329      */
15330      reset : function(){
15331         var s = this.subs;
15332         for(var i = 0; i < this.subCount; i++){
15333             s[i].buffer = [];
15334         }
15335         return this;
15336     },
15337
15338     applyTemplate : function(values){
15339         var s = this.subs;
15340         var replaceIndex = -1;
15341         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15342             return s[++replaceIndex].buffer.join("");
15343         });
15344         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15345     },
15346
15347     apply : function(){
15348         return this.applyTemplate.apply(this, arguments);
15349     },
15350
15351     compile : function(){return this;}
15352 });
15353
15354 /**
15355  * Alias for fill().
15356  * @method
15357  */
15358 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15359  /**
15360  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15361  * var tpl = Roo.MasterTemplate.from('element-id');
15362  * @param {String/HTMLElement} el
15363  * @param {Object} config
15364  * @static
15365  */
15366 Roo.MasterTemplate.from = function(el, config){
15367     el = Roo.getDom(el);
15368     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15369 };/*
15370  * Based on:
15371  * Ext JS Library 1.1.1
15372  * Copyright(c) 2006-2007, Ext JS, LLC.
15373  *
15374  * Originally Released Under LGPL - original licence link has changed is not relivant.
15375  *
15376  * Fork - LGPL
15377  * <script type="text/javascript">
15378  */
15379
15380  
15381 /**
15382  * @class Roo.util.CSS
15383  * Utility class for manipulating CSS rules
15384  * @static
15385
15386  */
15387 Roo.util.CSS = function(){
15388         var rules = null;
15389         var doc = document;
15390
15391     var camelRe = /(-[a-z])/gi;
15392     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15393
15394    return {
15395    /**
15396     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15397     * tag and appended to the HEAD of the document.
15398     * @param {String|Object} cssText The text containing the css rules
15399     * @param {String} id An id to add to the stylesheet for later removal
15400     * @return {StyleSheet}
15401     */
15402     createStyleSheet : function(cssText, id){
15403         var ss;
15404         var head = doc.getElementsByTagName("head")[0];
15405         var nrules = doc.createElement("style");
15406         nrules.setAttribute("type", "text/css");
15407         if(id){
15408             nrules.setAttribute("id", id);
15409         }
15410         if (typeof(cssText) != 'string') {
15411             // support object maps..
15412             // not sure if this a good idea.. 
15413             // perhaps it should be merged with the general css handling
15414             // and handle js style props.
15415             var cssTextNew = [];
15416             for(var n in cssText) {
15417                 var citems = [];
15418                 for(var k in cssText[n]) {
15419                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15420                 }
15421                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15422                 
15423             }
15424             cssText = cssTextNew.join("\n");
15425             
15426         }
15427        
15428        
15429        if(Roo.isIE){
15430            head.appendChild(nrules);
15431            ss = nrules.styleSheet;
15432            ss.cssText = cssText;
15433        }else{
15434            try{
15435                 nrules.appendChild(doc.createTextNode(cssText));
15436            }catch(e){
15437                nrules.cssText = cssText; 
15438            }
15439            head.appendChild(nrules);
15440            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15441        }
15442        this.cacheStyleSheet(ss);
15443        return ss;
15444    },
15445
15446    /**
15447     * Removes a style or link tag by id
15448     * @param {String} id The id of the tag
15449     */
15450    removeStyleSheet : function(id){
15451        var existing = doc.getElementById(id);
15452        if(existing){
15453            existing.parentNode.removeChild(existing);
15454        }
15455    },
15456
15457    /**
15458     * Dynamically swaps an existing stylesheet reference for a new one
15459     * @param {String} id The id of an existing link tag to remove
15460     * @param {String} url The href of the new stylesheet to include
15461     */
15462    swapStyleSheet : function(id, url){
15463        this.removeStyleSheet(id);
15464        var ss = doc.createElement("link");
15465        ss.setAttribute("rel", "stylesheet");
15466        ss.setAttribute("type", "text/css");
15467        ss.setAttribute("id", id);
15468        ss.setAttribute("href", url);
15469        doc.getElementsByTagName("head")[0].appendChild(ss);
15470    },
15471    
15472    /**
15473     * Refresh the rule cache if you have dynamically added stylesheets
15474     * @return {Object} An object (hash) of rules indexed by selector
15475     */
15476    refreshCache : function(){
15477        return this.getRules(true);
15478    },
15479
15480    // private
15481    cacheStyleSheet : function(stylesheet){
15482        if(!rules){
15483            rules = {};
15484        }
15485        try{// try catch for cross domain access issue
15486            var ssRules = stylesheet.cssRules || stylesheet.rules;
15487            for(var j = ssRules.length-1; j >= 0; --j){
15488                rules[ssRules[j].selectorText] = ssRules[j];
15489            }
15490        }catch(e){}
15491    },
15492    
15493    /**
15494     * Gets all css rules for the document
15495     * @param {Boolean} refreshCache true to refresh the internal cache
15496     * @return {Object} An object (hash) of rules indexed by selector
15497     */
15498    getRules : function(refreshCache){
15499                 if(rules == null || refreshCache){
15500                         rules = {};
15501                         var ds = doc.styleSheets;
15502                         for(var i =0, len = ds.length; i < len; i++){
15503                             try{
15504                         this.cacheStyleSheet(ds[i]);
15505                     }catch(e){} 
15506                 }
15507                 }
15508                 return rules;
15509         },
15510         
15511         /**
15512     * Gets an an individual CSS rule by selector(s)
15513     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15514     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15515     * @return {CSSRule} The CSS rule or null if one is not found
15516     */
15517    getRule : function(selector, refreshCache){
15518                 var rs = this.getRules(refreshCache);
15519                 if(!(selector instanceof Array)){
15520                     return rs[selector];
15521                 }
15522                 for(var i = 0; i < selector.length; i++){
15523                         if(rs[selector[i]]){
15524                                 return rs[selector[i]];
15525                         }
15526                 }
15527                 return null;
15528         },
15529         
15530         
15531         /**
15532     * Updates a rule property
15533     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15534     * @param {String} property The css property
15535     * @param {String} value The new value for the property
15536     * @return {Boolean} true If a rule was found and updated
15537     */
15538    updateRule : function(selector, property, value){
15539                 if(!(selector instanceof Array)){
15540                         var rule = this.getRule(selector);
15541                         if(rule){
15542                                 rule.style[property.replace(camelRe, camelFn)] = value;
15543                                 return true;
15544                         }
15545                 }else{
15546                         for(var i = 0; i < selector.length; i++){
15547                                 if(this.updateRule(selector[i], property, value)){
15548                                         return true;
15549                                 }
15550                         }
15551                 }
15552                 return false;
15553         }
15554    };   
15555 }();/*
15556  * Based on:
15557  * Ext JS Library 1.1.1
15558  * Copyright(c) 2006-2007, Ext JS, LLC.
15559  *
15560  * Originally Released Under LGPL - original licence link has changed is not relivant.
15561  *
15562  * Fork - LGPL
15563  * <script type="text/javascript">
15564  */
15565
15566  
15567
15568 /**
15569  * @class Roo.util.ClickRepeater
15570  * @extends Roo.util.Observable
15571  * 
15572  * A wrapper class which can be applied to any element. Fires a "click" event while the
15573  * mouse is pressed. The interval between firings may be specified in the config but
15574  * defaults to 10 milliseconds.
15575  * 
15576  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15577  * 
15578  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15579  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15580  * Similar to an autorepeat key delay.
15581  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15582  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15583  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15584  *           "interval" and "delay" are ignored. "immediate" is honored.
15585  * @cfg {Boolean} preventDefault True to prevent the default click event
15586  * @cfg {Boolean} stopDefault True to stop the default click event
15587  * 
15588  * @history
15589  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15590  *     2007-02-02 jvs Renamed to ClickRepeater
15591  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15592  *
15593  *  @constructor
15594  * @param {String/HTMLElement/Element} el The element to listen on
15595  * @param {Object} config
15596  **/
15597 Roo.util.ClickRepeater = function(el, config)
15598 {
15599     this.el = Roo.get(el);
15600     this.el.unselectable();
15601
15602     Roo.apply(this, config);
15603
15604     this.addEvents({
15605     /**
15606      * @event mousedown
15607      * Fires when the mouse button is depressed.
15608      * @param {Roo.util.ClickRepeater} this
15609      */
15610         "mousedown" : true,
15611     /**
15612      * @event click
15613      * Fires on a specified interval during the time the element is pressed.
15614      * @param {Roo.util.ClickRepeater} this
15615      */
15616         "click" : true,
15617     /**
15618      * @event mouseup
15619      * Fires when the mouse key is released.
15620      * @param {Roo.util.ClickRepeater} this
15621      */
15622         "mouseup" : true
15623     });
15624
15625     this.el.on("mousedown", this.handleMouseDown, this);
15626     if(this.preventDefault || this.stopDefault){
15627         this.el.on("click", function(e){
15628             if(this.preventDefault){
15629                 e.preventDefault();
15630             }
15631             if(this.stopDefault){
15632                 e.stopEvent();
15633             }
15634         }, this);
15635     }
15636
15637     // allow inline handler
15638     if(this.handler){
15639         this.on("click", this.handler,  this.scope || this);
15640     }
15641
15642     Roo.util.ClickRepeater.superclass.constructor.call(this);
15643 };
15644
15645 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15646     interval : 20,
15647     delay: 250,
15648     preventDefault : true,
15649     stopDefault : false,
15650     timer : 0,
15651
15652     // private
15653     handleMouseDown : function(){
15654         clearTimeout(this.timer);
15655         this.el.blur();
15656         if(this.pressClass){
15657             this.el.addClass(this.pressClass);
15658         }
15659         this.mousedownTime = new Date();
15660
15661         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15662         this.el.on("mouseout", this.handleMouseOut, this);
15663
15664         this.fireEvent("mousedown", this);
15665         this.fireEvent("click", this);
15666         
15667         this.timer = this.click.defer(this.delay || this.interval, this);
15668     },
15669
15670     // private
15671     click : function(){
15672         this.fireEvent("click", this);
15673         this.timer = this.click.defer(this.getInterval(), this);
15674     },
15675
15676     // private
15677     getInterval: function(){
15678         if(!this.accelerate){
15679             return this.interval;
15680         }
15681         var pressTime = this.mousedownTime.getElapsed();
15682         if(pressTime < 500){
15683             return 400;
15684         }else if(pressTime < 1700){
15685             return 320;
15686         }else if(pressTime < 2600){
15687             return 250;
15688         }else if(pressTime < 3500){
15689             return 180;
15690         }else if(pressTime < 4400){
15691             return 140;
15692         }else if(pressTime < 5300){
15693             return 80;
15694         }else if(pressTime < 6200){
15695             return 50;
15696         }else{
15697             return 10;
15698         }
15699     },
15700
15701     // private
15702     handleMouseOut : function(){
15703         clearTimeout(this.timer);
15704         if(this.pressClass){
15705             this.el.removeClass(this.pressClass);
15706         }
15707         this.el.on("mouseover", this.handleMouseReturn, this);
15708     },
15709
15710     // private
15711     handleMouseReturn : function(){
15712         this.el.un("mouseover", this.handleMouseReturn);
15713         if(this.pressClass){
15714             this.el.addClass(this.pressClass);
15715         }
15716         this.click();
15717     },
15718
15719     // private
15720     handleMouseUp : function(){
15721         clearTimeout(this.timer);
15722         this.el.un("mouseover", this.handleMouseReturn);
15723         this.el.un("mouseout", this.handleMouseOut);
15724         Roo.get(document).un("mouseup", this.handleMouseUp);
15725         this.el.removeClass(this.pressClass);
15726         this.fireEvent("mouseup", this);
15727     }
15728 });/**
15729  * @class Roo.util.Clipboard
15730  * @static
15731  * 
15732  * Clipboard UTILS
15733  * 
15734  **/
15735 Roo.util.Clipboard = {
15736     /**
15737      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15738      * @param {String} text to copy to clipboard
15739      */
15740     write : function(text) {
15741         // navigator clipboard api needs a secure context (https)
15742         if (navigator.clipboard && window.isSecureContext) {
15743             // navigator clipboard api method'
15744             navigator.clipboard.writeText(text);
15745             return ;
15746         } 
15747         // text area method
15748         var ta = document.createElement("textarea");
15749         ta.value = text;
15750         // make the textarea out of viewport
15751         ta.style.position = "fixed";
15752         ta.style.left = "-999999px";
15753         ta.style.top = "-999999px";
15754         document.body.appendChild(ta);
15755         ta.focus();
15756         ta.select();
15757         document.execCommand('copy');
15758         (function() {
15759             ta.remove();
15760         }).defer(100);
15761         
15762     }
15763         
15764 }
15765     /*
15766  * Based on:
15767  * Ext JS Library 1.1.1
15768  * Copyright(c) 2006-2007, Ext JS, LLC.
15769  *
15770  * Originally Released Under LGPL - original licence link has changed is not relivant.
15771  *
15772  * Fork - LGPL
15773  * <script type="text/javascript">
15774  */
15775
15776  
15777 /**
15778  * @class Roo.KeyNav
15779  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15780  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15781  * way to implement custom navigation schemes for any UI component.</p>
15782  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15783  * pageUp, pageDown, del, home, end.  Usage:</p>
15784  <pre><code>
15785 var nav = new Roo.KeyNav("my-element", {
15786     "left" : function(e){
15787         this.moveLeft(e.ctrlKey);
15788     },
15789     "right" : function(e){
15790         this.moveRight(e.ctrlKey);
15791     },
15792     "enter" : function(e){
15793         this.save();
15794     },
15795     scope : this
15796 });
15797 </code></pre>
15798  * @constructor
15799  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15800  * @param {Object} config The config
15801  */
15802 Roo.KeyNav = function(el, config){
15803     this.el = Roo.get(el);
15804     Roo.apply(this, config);
15805     if(!this.disabled){
15806         this.disabled = true;
15807         this.enable();
15808     }
15809 };
15810
15811 Roo.KeyNav.prototype = {
15812     /**
15813      * @cfg {Boolean} disabled
15814      * True to disable this KeyNav instance (defaults to false)
15815      */
15816     disabled : false,
15817     /**
15818      * @cfg {String} defaultEventAction
15819      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15820      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15821      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15822      */
15823     defaultEventAction: "stopEvent",
15824     /**
15825      * @cfg {Boolean} forceKeyDown
15826      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15827      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15828      * handle keydown instead of keypress.
15829      */
15830     forceKeyDown : false,
15831
15832     // private
15833     prepareEvent : function(e){
15834         var k = e.getKey();
15835         var h = this.keyToHandler[k];
15836         //if(h && this[h]){
15837         //    e.stopPropagation();
15838         //}
15839         if(Roo.isSafari && h && k >= 37 && k <= 40){
15840             e.stopEvent();
15841         }
15842     },
15843
15844     // private
15845     relay : function(e){
15846         var k = e.getKey();
15847         var h = this.keyToHandler[k];
15848         if(h && this[h]){
15849             if(this.doRelay(e, this[h], h) !== true){
15850                 e[this.defaultEventAction]();
15851             }
15852         }
15853     },
15854
15855     // private
15856     doRelay : function(e, h, hname){
15857         return h.call(this.scope || this, e);
15858     },
15859
15860     // possible handlers
15861     enter : false,
15862     left : false,
15863     right : false,
15864     up : false,
15865     down : false,
15866     tab : false,
15867     esc : false,
15868     pageUp : false,
15869     pageDown : false,
15870     del : false,
15871     home : false,
15872     end : false,
15873
15874     // quick lookup hash
15875     keyToHandler : {
15876         37 : "left",
15877         39 : "right",
15878         38 : "up",
15879         40 : "down",
15880         33 : "pageUp",
15881         34 : "pageDown",
15882         46 : "del",
15883         36 : "home",
15884         35 : "end",
15885         13 : "enter",
15886         27 : "esc",
15887         9  : "tab"
15888     },
15889
15890         /**
15891          * Enable this KeyNav
15892          */
15893         enable: function(){
15894                 if(this.disabled){
15895             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15896             // the EventObject will normalize Safari automatically
15897             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15898                 this.el.on("keydown", this.relay,  this);
15899             }else{
15900                 this.el.on("keydown", this.prepareEvent,  this);
15901                 this.el.on("keypress", this.relay,  this);
15902             }
15903                     this.disabled = false;
15904                 }
15905         },
15906
15907         /**
15908          * Disable this KeyNav
15909          */
15910         disable: function(){
15911                 if(!this.disabled){
15912                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15913                 this.el.un("keydown", this.relay);
15914             }else{
15915                 this.el.un("keydown", this.prepareEvent);
15916                 this.el.un("keypress", this.relay);
15917             }
15918                     this.disabled = true;
15919                 }
15920         }
15921 };/*
15922  * Based on:
15923  * Ext JS Library 1.1.1
15924  * Copyright(c) 2006-2007, Ext JS, LLC.
15925  *
15926  * Originally Released Under LGPL - original licence link has changed is not relivant.
15927  *
15928  * Fork - LGPL
15929  * <script type="text/javascript">
15930  */
15931
15932  
15933 /**
15934  * @class Roo.KeyMap
15935  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15936  * The constructor accepts the same config object as defined by {@link #addBinding}.
15937  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15938  * combination it will call the function with this signature (if the match is a multi-key
15939  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15940  * A KeyMap can also handle a string representation of keys.<br />
15941  * Usage:
15942  <pre><code>
15943 // map one key by key code
15944 var map = new Roo.KeyMap("my-element", {
15945     key: 13, // or Roo.EventObject.ENTER
15946     fn: myHandler,
15947     scope: myObject
15948 });
15949
15950 // map multiple keys to one action by string
15951 var map = new Roo.KeyMap("my-element", {
15952     key: "a\r\n\t",
15953     fn: myHandler,
15954     scope: myObject
15955 });
15956
15957 // map multiple keys to multiple actions by strings and array of codes
15958 var map = new Roo.KeyMap("my-element", [
15959     {
15960         key: [10,13],
15961         fn: function(){ alert("Return was pressed"); }
15962     }, {
15963         key: "abc",
15964         fn: function(){ alert('a, b or c was pressed'); }
15965     }, {
15966         key: "\t",
15967         ctrl:true,
15968         shift:true,
15969         fn: function(){ alert('Control + shift + tab was pressed.'); }
15970     }
15971 ]);
15972 </code></pre>
15973  * <b>Note: A KeyMap starts enabled</b>
15974  * @constructor
15975  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15976  * @param {Object} config The config (see {@link #addBinding})
15977  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15978  */
15979 Roo.KeyMap = function(el, config, eventName){
15980     this.el  = Roo.get(el);
15981     this.eventName = eventName || "keydown";
15982     this.bindings = [];
15983     if(config){
15984         this.addBinding(config);
15985     }
15986     this.enable();
15987 };
15988
15989 Roo.KeyMap.prototype = {
15990     /**
15991      * True to stop the event from bubbling and prevent the default browser action if the
15992      * key was handled by the KeyMap (defaults to false)
15993      * @type Boolean
15994      */
15995     stopEvent : false,
15996
15997     /**
15998      * Add a new binding to this KeyMap. The following config object properties are supported:
15999      * <pre>
16000 Property    Type             Description
16001 ----------  ---------------  ----------------------------------------------------------------------
16002 key         String/Array     A single keycode or an array of keycodes to handle
16003 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16004 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16005 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16006 fn          Function         The function to call when KeyMap finds the expected key combination
16007 scope       Object           The scope of the callback function
16008 </pre>
16009      *
16010      * Usage:
16011      * <pre><code>
16012 // Create a KeyMap
16013 var map = new Roo.KeyMap(document, {
16014     key: Roo.EventObject.ENTER,
16015     fn: handleKey,
16016     scope: this
16017 });
16018
16019 //Add a new binding to the existing KeyMap later
16020 map.addBinding({
16021     key: 'abc',
16022     shift: true,
16023     fn: handleKey,
16024     scope: this
16025 });
16026 </code></pre>
16027      * @param {Object/Array} config A single KeyMap config or an array of configs
16028      */
16029         addBinding : function(config){
16030         if(config instanceof Array){
16031             for(var i = 0, len = config.length; i < len; i++){
16032                 this.addBinding(config[i]);
16033             }
16034             return;
16035         }
16036         var keyCode = config.key,
16037             shift = config.shift, 
16038             ctrl = config.ctrl, 
16039             alt = config.alt,
16040             fn = config.fn,
16041             scope = config.scope;
16042         if(typeof keyCode == "string"){
16043             var ks = [];
16044             var keyString = keyCode.toUpperCase();
16045             for(var j = 0, len = keyString.length; j < len; j++){
16046                 ks.push(keyString.charCodeAt(j));
16047             }
16048             keyCode = ks;
16049         }
16050         var keyArray = keyCode instanceof Array;
16051         var handler = function(e){
16052             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16053                 var k = e.getKey();
16054                 if(keyArray){
16055                     for(var i = 0, len = keyCode.length; i < len; i++){
16056                         if(keyCode[i] == k){
16057                           if(this.stopEvent){
16058                               e.stopEvent();
16059                           }
16060                           fn.call(scope || window, k, e);
16061                           return;
16062                         }
16063                     }
16064                 }else{
16065                     if(k == keyCode){
16066                         if(this.stopEvent){
16067                            e.stopEvent();
16068                         }
16069                         fn.call(scope || window, k, e);
16070                     }
16071                 }
16072             }
16073         };
16074         this.bindings.push(handler);  
16075         },
16076
16077     /**
16078      * Shorthand for adding a single key listener
16079      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16080      * following options:
16081      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16082      * @param {Function} fn The function to call
16083      * @param {Object} scope (optional) The scope of the function
16084      */
16085     on : function(key, fn, scope){
16086         var keyCode, shift, ctrl, alt;
16087         if(typeof key == "object" && !(key instanceof Array)){
16088             keyCode = key.key;
16089             shift = key.shift;
16090             ctrl = key.ctrl;
16091             alt = key.alt;
16092         }else{
16093             keyCode = key;
16094         }
16095         this.addBinding({
16096             key: keyCode,
16097             shift: shift,
16098             ctrl: ctrl,
16099             alt: alt,
16100             fn: fn,
16101             scope: scope
16102         })
16103     },
16104
16105     // private
16106     handleKeyDown : function(e){
16107             if(this.enabled){ //just in case
16108             var b = this.bindings;
16109             for(var i = 0, len = b.length; i < len; i++){
16110                 b[i].call(this, e);
16111             }
16112             }
16113         },
16114         
16115         /**
16116          * Returns true if this KeyMap is enabled
16117          * @return {Boolean} 
16118          */
16119         isEnabled : function(){
16120             return this.enabled;  
16121         },
16122         
16123         /**
16124          * Enables this KeyMap
16125          */
16126         enable: function(){
16127                 if(!this.enabled){
16128                     this.el.on(this.eventName, this.handleKeyDown, this);
16129                     this.enabled = true;
16130                 }
16131         },
16132
16133         /**
16134          * Disable this KeyMap
16135          */
16136         disable: function(){
16137                 if(this.enabled){
16138                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16139                     this.enabled = false;
16140                 }
16141         }
16142 };/*
16143  * Based on:
16144  * Ext JS Library 1.1.1
16145  * Copyright(c) 2006-2007, Ext JS, LLC.
16146  *
16147  * Originally Released Under LGPL - original licence link has changed is not relivant.
16148  *
16149  * Fork - LGPL
16150  * <script type="text/javascript">
16151  */
16152
16153  
16154 /**
16155  * @class Roo.util.TextMetrics
16156  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16157  * wide, in pixels, a given block of text will be.
16158  * @static
16159  */
16160 Roo.util.TextMetrics = function(){
16161     var shared;
16162     return {
16163         /**
16164          * Measures the size of the specified text
16165          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16166          * that can affect the size of the rendered text
16167          * @param {String} text The text to measure
16168          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16169          * in order to accurately measure the text height
16170          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16171          */
16172         measure : function(el, text, fixedWidth){
16173             if(!shared){
16174                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16175             }
16176             shared.bind(el);
16177             shared.setFixedWidth(fixedWidth || 'auto');
16178             return shared.getSize(text);
16179         },
16180
16181         /**
16182          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16183          * the overhead of multiple calls to initialize the style properties on each measurement.
16184          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16185          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16186          * in order to accurately measure the text height
16187          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16188          */
16189         createInstance : function(el, fixedWidth){
16190             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16191         }
16192     };
16193 }();
16194
16195 /**
16196  * @class Roo.util.TextMetrics.Instance
16197  * Instance of  TextMetrics Calcuation
16198  * @constructor
16199  * Create a new TextMetrics Instance
16200  * @param {Object} bindto
16201  * @param {Boolean} fixedWidth
16202  */
16203
16204 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16205 {
16206     var ml = new Roo.Element(document.createElement('div'));
16207     document.body.appendChild(ml.dom);
16208     ml.position('absolute');
16209     ml.setLeftTop(-1000, -1000);
16210     ml.hide();
16211
16212     if(fixedWidth){
16213         ml.setWidth(fixedWidth);
16214     }
16215      
16216     var instance = {
16217         /**
16218          * Returns the size of the specified text based on the internal element's style and width properties
16219          * @param {String} text The text to measure
16220          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16221          */
16222         getSize : function(text){
16223             ml.update(text);
16224             var s = ml.getSize();
16225             ml.update('');
16226             return s;
16227         },
16228
16229         /**
16230          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16231          * that can affect the size of the rendered text
16232          * @param {String/HTMLElement} el The element, dom node or id
16233          */
16234         bind : function(el){
16235             ml.setStyle(
16236                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16237             );
16238         },
16239
16240         /**
16241          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16242          * to set a fixed width in order to accurately measure the text height.
16243          * @param {Number} width The width to set on the element
16244          */
16245         setFixedWidth : function(width){
16246             ml.setWidth(width);
16247         },
16248
16249         /**
16250          * Returns the measured width of the specified text
16251          * @param {String} text The text to measure
16252          * @return {Number} width The width in pixels
16253          */
16254         getWidth : function(text){
16255             ml.dom.style.width = 'auto';
16256             return this.getSize(text).width;
16257         },
16258
16259         /**
16260          * Returns the measured height of the specified text.  For multiline text, be sure to call
16261          * {@link #setFixedWidth} if necessary.
16262          * @param {String} text The text to measure
16263          * @return {Number} height The height in pixels
16264          */
16265         getHeight : function(text){
16266             return this.getSize(text).height;
16267         }
16268     };
16269
16270     instance.bind(bindTo);
16271
16272     return instance;
16273 };
16274
16275 // backwards compat
16276 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16277  * Based on:
16278  * Ext JS Library 1.1.1
16279  * Copyright(c) 2006-2007, Ext JS, LLC.
16280  *
16281  * Originally Released Under LGPL - original licence link has changed is not relivant.
16282  *
16283  * Fork - LGPL
16284  * <script type="text/javascript">
16285  */
16286
16287 /**
16288  * @class Roo.state.Provider
16289  * Abstract base class for state provider implementations. This class provides methods
16290  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16291  * Provider interface.
16292  */
16293 Roo.state.Provider = function(){
16294     /**
16295      * @event statechange
16296      * Fires when a state change occurs.
16297      * @param {Provider} this This state provider
16298      * @param {String} key The state key which was changed
16299      * @param {String} value The encoded value for the state
16300      */
16301     this.addEvents({
16302         "statechange": true
16303     });
16304     this.state = {};
16305     Roo.state.Provider.superclass.constructor.call(this);
16306 };
16307 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16308     /**
16309      * Returns the current value for a key
16310      * @param {String} name The key name
16311      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16312      * @return {Mixed} The state data
16313      */
16314     get : function(name, defaultValue){
16315         return typeof this.state[name] == "undefined" ?
16316             defaultValue : this.state[name];
16317     },
16318     
16319     /**
16320      * Clears a value from the state
16321      * @param {String} name The key name
16322      */
16323     clear : function(name){
16324         delete this.state[name];
16325         this.fireEvent("statechange", this, name, null);
16326     },
16327     
16328     /**
16329      * Sets the value for a key
16330      * @param {String} name The key name
16331      * @param {Mixed} value The value to set
16332      */
16333     set : function(name, value){
16334         this.state[name] = value;
16335         this.fireEvent("statechange", this, name, value);
16336     },
16337     
16338     /**
16339      * Decodes a string previously encoded with {@link #encodeValue}.
16340      * @param {String} value The value to decode
16341      * @return {Mixed} The decoded value
16342      */
16343     decodeValue : function(cookie){
16344         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16345         var matches = re.exec(unescape(cookie));
16346         if(!matches || !matches[1]) {
16347             return; // non state cookie
16348         }
16349         var type = matches[1];
16350         var v = matches[2];
16351         switch(type){
16352             case "n":
16353                 return parseFloat(v);
16354             case "d":
16355                 return new Date(Date.parse(v));
16356             case "b":
16357                 return (v == "1");
16358             case "a":
16359                 var all = [];
16360                 var values = v.split("^");
16361                 for(var i = 0, len = values.length; i < len; i++){
16362                     all.push(this.decodeValue(values[i]));
16363                 }
16364                 return all;
16365            case "o":
16366                 var all = {};
16367                 var values = v.split("^");
16368                 for(var i = 0, len = values.length; i < len; i++){
16369                     var kv = values[i].split("=");
16370                     all[kv[0]] = this.decodeValue(kv[1]);
16371                 }
16372                 return all;
16373            default:
16374                 return v;
16375         }
16376     },
16377     
16378     /**
16379      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16380      * @param {Mixed} value The value to encode
16381      * @return {String} The encoded value
16382      */
16383     encodeValue : function(v){
16384         var enc;
16385         if(typeof v == "number"){
16386             enc = "n:" + v;
16387         }else if(typeof v == "boolean"){
16388             enc = "b:" + (v ? "1" : "0");
16389         }else if(v instanceof Date){
16390             enc = "d:" + v.toGMTString();
16391         }else if(v instanceof Array){
16392             var flat = "";
16393             for(var i = 0, len = v.length; i < len; i++){
16394                 flat += this.encodeValue(v[i]);
16395                 if(i != len-1) {
16396                     flat += "^";
16397                 }
16398             }
16399             enc = "a:" + flat;
16400         }else if(typeof v == "object"){
16401             var flat = "";
16402             for(var key in v){
16403                 if(typeof v[key] != "function"){
16404                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16405                 }
16406             }
16407             enc = "o:" + flat.substring(0, flat.length-1);
16408         }else{
16409             enc = "s:" + v;
16410         }
16411         return escape(enc);        
16412     }
16413 });
16414
16415 /*
16416  * Based on:
16417  * Ext JS Library 1.1.1
16418  * Copyright(c) 2006-2007, Ext JS, LLC.
16419  *
16420  * Originally Released Under LGPL - original licence link has changed is not relivant.
16421  *
16422  * Fork - LGPL
16423  * <script type="text/javascript">
16424  */
16425 /**
16426  * @class Roo.state.Manager
16427  * This is the global state manager. By default all components that are "state aware" check this class
16428  * for state information if you don't pass them a custom state provider. In order for this class
16429  * to be useful, it must be initialized with a provider when your application initializes.
16430  <pre><code>
16431 // in your initialization function
16432 init : function(){
16433    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16434    ...
16435    // supposed you have a {@link Roo.BorderLayout}
16436    var layout = new Roo.BorderLayout(...);
16437    layout.restoreState();
16438    // or a {Roo.BasicDialog}
16439    var dialog = new Roo.BasicDialog(...);
16440    dialog.restoreState();
16441  </code></pre>
16442  * @static
16443  */
16444 Roo.state.Manager = function(){
16445     var provider = new Roo.state.Provider();
16446     
16447     return {
16448         /**
16449          * Configures the default state provider for your application
16450          * @param {Provider} stateProvider The state provider to set
16451          */
16452         setProvider : function(stateProvider){
16453             provider = stateProvider;
16454         },
16455         
16456         /**
16457          * Returns the current value for a key
16458          * @param {String} name The key name
16459          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16460          * @return {Mixed} The state data
16461          */
16462         get : function(key, defaultValue){
16463             return provider.get(key, defaultValue);
16464         },
16465         
16466         /**
16467          * Sets the value for a key
16468          * @param {String} name The key name
16469          * @param {Mixed} value The state data
16470          */
16471          set : function(key, value){
16472             provider.set(key, value);
16473         },
16474         
16475         /**
16476          * Clears a value from the state
16477          * @param {String} name The key name
16478          */
16479         clear : function(key){
16480             provider.clear(key);
16481         },
16482         
16483         /**
16484          * Gets the currently configured state provider
16485          * @return {Provider} The state provider
16486          */
16487         getProvider : function(){
16488             return provider;
16489         }
16490     };
16491 }();
16492 /*
16493  * Based on:
16494  * Ext JS Library 1.1.1
16495  * Copyright(c) 2006-2007, Ext JS, LLC.
16496  *
16497  * Originally Released Under LGPL - original licence link has changed is not relivant.
16498  *
16499  * Fork - LGPL
16500  * <script type="text/javascript">
16501  */
16502 /**
16503  * @class Roo.state.CookieProvider
16504  * @extends Roo.state.Provider
16505  * The default Provider implementation which saves state via cookies.
16506  * <br />Usage:
16507  <pre><code>
16508    var cp = new Roo.state.CookieProvider({
16509        path: "/cgi-bin/",
16510        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16511        domain: "roojs.com"
16512    })
16513    Roo.state.Manager.setProvider(cp);
16514  </code></pre>
16515  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16516  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16517  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16518  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16519  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16520  * domain the page is running on including the 'www' like 'www.roojs.com')
16521  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16522  * @constructor
16523  * Create a new CookieProvider
16524  * @param {Object} config The configuration object
16525  */
16526 Roo.state.CookieProvider = function(config){
16527     Roo.state.CookieProvider.superclass.constructor.call(this);
16528     this.path = "/";
16529     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16530     this.domain = null;
16531     this.secure = false;
16532     Roo.apply(this, config);
16533     this.state = this.readCookies();
16534 };
16535
16536 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16537     // private
16538     set : function(name, value){
16539         if(typeof value == "undefined" || value === null){
16540             this.clear(name);
16541             return;
16542         }
16543         this.setCookie(name, value);
16544         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16545     },
16546
16547     // private
16548     clear : function(name){
16549         this.clearCookie(name);
16550         Roo.state.CookieProvider.superclass.clear.call(this, name);
16551     },
16552
16553     // private
16554     readCookies : function(){
16555         var cookies = {};
16556         var c = document.cookie + ";";
16557         var re = /\s?(.*?)=(.*?);/g;
16558         var matches;
16559         while((matches = re.exec(c)) != null){
16560             var name = matches[1];
16561             var value = matches[2];
16562             if(name && name.substring(0,3) == "ys-"){
16563                 cookies[name.substr(3)] = this.decodeValue(value);
16564             }
16565         }
16566         return cookies;
16567     },
16568
16569     // private
16570     setCookie : function(name, value){
16571         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16572            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16573            ((this.path == null) ? "" : ("; path=" + this.path)) +
16574            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16575            ((this.secure == true) ? "; secure" : "");
16576     },
16577
16578     // private
16579     clearCookie : function(name){
16580         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16581            ((this.path == null) ? "" : ("; path=" + this.path)) +
16582            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16583            ((this.secure == true) ? "; secure" : "");
16584     }
16585 });/*
16586  * Based on:
16587  * Ext JS Library 1.1.1
16588  * Copyright(c) 2006-2007, Ext JS, LLC.
16589  *
16590  * Originally Released Under LGPL - original licence link has changed is not relivant.
16591  *
16592  * Fork - LGPL
16593  * <script type="text/javascript">
16594  */
16595  
16596
16597 /**
16598  * @class Roo.ComponentMgr
16599  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16600  * @static
16601  */
16602 Roo.ComponentMgr = function(){
16603     var all = new Roo.util.MixedCollection();
16604
16605     return {
16606         /**
16607          * Registers a component.
16608          * @param {Roo.Component} c The component
16609          */
16610         register : function(c){
16611             all.add(c);
16612         },
16613
16614         /**
16615          * Unregisters a component.
16616          * @param {Roo.Component} c The component
16617          */
16618         unregister : function(c){
16619             all.remove(c);
16620         },
16621
16622         /**
16623          * Returns a component by id
16624          * @param {String} id The component id
16625          */
16626         get : function(id){
16627             return all.get(id);
16628         },
16629
16630         /**
16631          * Registers a function that will be called when a specified component is added to ComponentMgr
16632          * @param {String} id The component id
16633          * @param {Funtction} fn The callback function
16634          * @param {Object} scope The scope of the callback
16635          */
16636         onAvailable : function(id, fn, scope){
16637             all.on("add", function(index, o){
16638                 if(o.id == id){
16639                     fn.call(scope || o, o);
16640                     all.un("add", fn, scope);
16641                 }
16642             });
16643         }
16644     };
16645 }();/*
16646  * Based on:
16647  * Ext JS Library 1.1.1
16648  * Copyright(c) 2006-2007, Ext JS, LLC.
16649  *
16650  * Originally Released Under LGPL - original licence link has changed is not relivant.
16651  *
16652  * Fork - LGPL
16653  * <script type="text/javascript">
16654  */
16655  
16656 /**
16657  * @class Roo.Component
16658  * @extends Roo.util.Observable
16659  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16660  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16661  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16662  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16663  * All visual components (widgets) that require rendering into a layout should subclass Component.
16664  * @constructor
16665  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16666  * 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
16667  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16668  */
16669 Roo.Component = function(config){
16670     config = config || {};
16671     if(config.tagName || config.dom || typeof config == "string"){ // element object
16672         config = {el: config, id: config.id || config};
16673     }
16674     this.initialConfig = config;
16675
16676     Roo.apply(this, config);
16677     this.addEvents({
16678         /**
16679          * @event disable
16680          * Fires after the component is disabled.
16681              * @param {Roo.Component} this
16682              */
16683         disable : true,
16684         /**
16685          * @event enable
16686          * Fires after the component is enabled.
16687              * @param {Roo.Component} this
16688              */
16689         enable : true,
16690         /**
16691          * @event beforeshow
16692          * Fires before the component is shown.  Return false to stop the show.
16693              * @param {Roo.Component} this
16694              */
16695         beforeshow : true,
16696         /**
16697          * @event show
16698          * Fires after the component is shown.
16699              * @param {Roo.Component} this
16700              */
16701         show : true,
16702         /**
16703          * @event beforehide
16704          * Fires before the component is hidden. Return false to stop the hide.
16705              * @param {Roo.Component} this
16706              */
16707         beforehide : true,
16708         /**
16709          * @event hide
16710          * Fires after the component is hidden.
16711              * @param {Roo.Component} this
16712              */
16713         hide : true,
16714         /**
16715          * @event beforerender
16716          * Fires before the component is rendered. Return false to stop the render.
16717              * @param {Roo.Component} this
16718              */
16719         beforerender : true,
16720         /**
16721          * @event render
16722          * Fires after the component is rendered.
16723              * @param {Roo.Component} this
16724              */
16725         render : true,
16726         /**
16727          * @event beforedestroy
16728          * Fires before the component is destroyed. Return false to stop the destroy.
16729              * @param {Roo.Component} this
16730              */
16731         beforedestroy : true,
16732         /**
16733          * @event destroy
16734          * Fires after the component is destroyed.
16735              * @param {Roo.Component} this
16736              */
16737         destroy : true
16738     });
16739     if(!this.id){
16740         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16741     }
16742     Roo.ComponentMgr.register(this);
16743     Roo.Component.superclass.constructor.call(this);
16744     this.initComponent();
16745     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16746         this.render(this.renderTo);
16747         delete this.renderTo;
16748     }
16749 };
16750
16751 /** @private */
16752 Roo.Component.AUTO_ID = 1000;
16753
16754 Roo.extend(Roo.Component, Roo.util.Observable, {
16755     /**
16756      * @scope Roo.Component.prototype
16757      * @type {Boolean}
16758      * true if this component is hidden. Read-only.
16759      */
16760     hidden : false,
16761     /**
16762      * @type {Boolean}
16763      * true if this component is disabled. Read-only.
16764      */
16765     disabled : false,
16766     /**
16767      * @type {Boolean}
16768      * true if this component has been rendered. Read-only.
16769      */
16770     rendered : false,
16771     
16772     /** @cfg {String} disableClass
16773      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16774      */
16775     disabledClass : "x-item-disabled",
16776         /** @cfg {Boolean} allowDomMove
16777          * Whether the component can move the Dom node when rendering (defaults to true).
16778          */
16779     allowDomMove : true,
16780     /** @cfg {String} hideMode (display|visibility)
16781      * How this component should hidden. Supported values are
16782      * "visibility" (css visibility), "offsets" (negative offset position) and
16783      * "display" (css display) - defaults to "display".
16784      */
16785     hideMode: 'display',
16786
16787     /** @private */
16788     ctype : "Roo.Component",
16789
16790     /**
16791      * @cfg {String} actionMode 
16792      * which property holds the element that used for  hide() / show() / disable() / enable()
16793      * default is 'el' for forms you probably want to set this to fieldEl 
16794      */
16795     actionMode : "el",
16796
16797     /** @private */
16798     getActionEl : function(){
16799         return this[this.actionMode];
16800     },
16801
16802     initComponent : Roo.emptyFn,
16803     /**
16804      * If this is a lazy rendering component, render it to its container element.
16805      * @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.
16806      */
16807     render : function(container, position){
16808         
16809         if(this.rendered){
16810             return this;
16811         }
16812         
16813         if(this.fireEvent("beforerender", this) === false){
16814             return false;
16815         }
16816         
16817         if(!container && this.el){
16818             this.el = Roo.get(this.el);
16819             container = this.el.dom.parentNode;
16820             this.allowDomMove = false;
16821         }
16822         this.container = Roo.get(container);
16823         this.rendered = true;
16824         if(position !== undefined){
16825             if(typeof position == 'number'){
16826                 position = this.container.dom.childNodes[position];
16827             }else{
16828                 position = Roo.getDom(position);
16829             }
16830         }
16831         this.onRender(this.container, position || null);
16832         if(this.cls){
16833             this.el.addClass(this.cls);
16834             delete this.cls;
16835         }
16836         if(this.style){
16837             this.el.applyStyles(this.style);
16838             delete this.style;
16839         }
16840         this.fireEvent("render", this);
16841         this.afterRender(this.container);
16842         if(this.hidden){
16843             this.hide();
16844         }
16845         if(this.disabled){
16846             this.disable();
16847         }
16848
16849         return this;
16850         
16851     },
16852
16853     /** @private */
16854     // default function is not really useful
16855     onRender : function(ct, position){
16856         if(this.el){
16857             this.el = Roo.get(this.el);
16858             if(this.allowDomMove !== false){
16859                 ct.dom.insertBefore(this.el.dom, position);
16860             }
16861         }
16862     },
16863
16864     /** @private */
16865     getAutoCreate : function(){
16866         var cfg = typeof this.autoCreate == "object" ?
16867                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16868         if(this.id && !cfg.id){
16869             cfg.id = this.id;
16870         }
16871         return cfg;
16872     },
16873
16874     /** @private */
16875     afterRender : Roo.emptyFn,
16876
16877     /**
16878      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16879      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16880      */
16881     destroy : function(){
16882         if(this.fireEvent("beforedestroy", this) !== false){
16883             this.purgeListeners();
16884             this.beforeDestroy();
16885             if(this.rendered){
16886                 this.el.removeAllListeners();
16887                 this.el.remove();
16888                 if(this.actionMode == "container"){
16889                     this.container.remove();
16890                 }
16891             }
16892             this.onDestroy();
16893             Roo.ComponentMgr.unregister(this);
16894             this.fireEvent("destroy", this);
16895         }
16896     },
16897
16898         /** @private */
16899     beforeDestroy : function(){
16900
16901     },
16902
16903         /** @private */
16904         onDestroy : function(){
16905
16906     },
16907
16908     /**
16909      * Returns the underlying {@link Roo.Element}.
16910      * @return {Roo.Element} The element
16911      */
16912     getEl : function(){
16913         return this.el;
16914     },
16915
16916     /**
16917      * Returns the id of this component.
16918      * @return {String}
16919      */
16920     getId : function(){
16921         return this.id;
16922     },
16923
16924     /**
16925      * Try to focus this component.
16926      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16927      * @return {Roo.Component} this
16928      */
16929     focus : function(selectText){
16930         if(this.rendered){
16931             this.el.focus();
16932             if(selectText === true){
16933                 this.el.dom.select();
16934             }
16935         }
16936         return this;
16937     },
16938
16939     /** @private */
16940     blur : function(){
16941         if(this.rendered){
16942             this.el.blur();
16943         }
16944         return this;
16945     },
16946
16947     /**
16948      * Disable this component.
16949      * @return {Roo.Component} this
16950      */
16951     disable : function(){
16952         if(this.rendered){
16953             this.onDisable();
16954         }
16955         this.disabled = true;
16956         this.fireEvent("disable", this);
16957         return this;
16958     },
16959
16960         // private
16961     onDisable : function(){
16962         this.getActionEl().addClass(this.disabledClass);
16963         this.el.dom.disabled = true;
16964     },
16965
16966     /**
16967      * Enable this component.
16968      * @return {Roo.Component} this
16969      */
16970     enable : function(){
16971         if(this.rendered){
16972             this.onEnable();
16973         }
16974         this.disabled = false;
16975         this.fireEvent("enable", this);
16976         return this;
16977     },
16978
16979         // private
16980     onEnable : function(){
16981         this.getActionEl().removeClass(this.disabledClass);
16982         this.el.dom.disabled = false;
16983     },
16984
16985     /**
16986      * Convenience function for setting disabled/enabled by boolean.
16987      * @param {Boolean} disabled
16988      */
16989     setDisabled : function(disabled){
16990         this[disabled ? "disable" : "enable"]();
16991     },
16992
16993     /**
16994      * Show this component.
16995      * @return {Roo.Component} this
16996      */
16997     show: function(){
16998         if(this.fireEvent("beforeshow", this) !== false){
16999             this.hidden = false;
17000             if(this.rendered){
17001                 this.onShow();
17002             }
17003             this.fireEvent("show", this);
17004         }
17005         return this;
17006     },
17007
17008     // private
17009     onShow : function(){
17010         var ae = this.getActionEl();
17011         if(this.hideMode == 'visibility'){
17012             ae.dom.style.visibility = "visible";
17013         }else if(this.hideMode == 'offsets'){
17014             ae.removeClass('x-hidden');
17015         }else{
17016             ae.dom.style.display = "";
17017         }
17018     },
17019
17020     /**
17021      * Hide this component.
17022      * @return {Roo.Component} this
17023      */
17024     hide: function(){
17025         if(this.fireEvent("beforehide", this) !== false){
17026             this.hidden = true;
17027             if(this.rendered){
17028                 this.onHide();
17029             }
17030             this.fireEvent("hide", this);
17031         }
17032         return this;
17033     },
17034
17035     // private
17036     onHide : function(){
17037         var ae = this.getActionEl();
17038         if(this.hideMode == 'visibility'){
17039             ae.dom.style.visibility = "hidden";
17040         }else if(this.hideMode == 'offsets'){
17041             ae.addClass('x-hidden');
17042         }else{
17043             ae.dom.style.display = "none";
17044         }
17045     },
17046
17047     /**
17048      * Convenience function to hide or show this component by boolean.
17049      * @param {Boolean} visible True to show, false to hide
17050      * @return {Roo.Component} this
17051      */
17052     setVisible: function(visible){
17053         if(visible) {
17054             this.show();
17055         }else{
17056             this.hide();
17057         }
17058         return this;
17059     },
17060
17061     /**
17062      * Returns true if this component is visible.
17063      */
17064     isVisible : function(){
17065         return this.getActionEl().isVisible();
17066     },
17067
17068     cloneConfig : function(overrides){
17069         overrides = overrides || {};
17070         var id = overrides.id || Roo.id();
17071         var cfg = Roo.applyIf(overrides, this.initialConfig);
17072         cfg.id = id; // prevent dup id
17073         return new this.constructor(cfg);
17074     }
17075 });/*
17076  * Based on:
17077  * Ext JS Library 1.1.1
17078  * Copyright(c) 2006-2007, Ext JS, LLC.
17079  *
17080  * Originally Released Under LGPL - original licence link has changed is not relivant.
17081  *
17082  * Fork - LGPL
17083  * <script type="text/javascript">
17084  */
17085
17086 /**
17087  * @class Roo.BoxComponent
17088  * @extends Roo.Component
17089  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17090  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17091  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17092  * layout containers.
17093  * @constructor
17094  * @param {Roo.Element/String/Object} config The configuration options.
17095  */
17096 Roo.BoxComponent = function(config){
17097     Roo.Component.call(this, config);
17098     this.addEvents({
17099         /**
17100          * @event resize
17101          * Fires after the component is resized.
17102              * @param {Roo.Component} this
17103              * @param {Number} adjWidth The box-adjusted width that was set
17104              * @param {Number} adjHeight The box-adjusted height that was set
17105              * @param {Number} rawWidth The width that was originally specified
17106              * @param {Number} rawHeight The height that was originally specified
17107              */
17108         resize : true,
17109         /**
17110          * @event move
17111          * Fires after the component is moved.
17112              * @param {Roo.Component} this
17113              * @param {Number} x The new x position
17114              * @param {Number} y The new y position
17115              */
17116         move : true
17117     });
17118 };
17119
17120 Roo.extend(Roo.BoxComponent, Roo.Component, {
17121     // private, set in afterRender to signify that the component has been rendered
17122     boxReady : false,
17123     // private, used to defer height settings to subclasses
17124     deferHeight: false,
17125     /** @cfg {Number} width
17126      * width (optional) size of component
17127      */
17128      /** @cfg {Number} height
17129      * height (optional) size of component
17130      */
17131      
17132     /**
17133      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17134      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17135      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17136      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17137      * @return {Roo.BoxComponent} this
17138      */
17139     setSize : function(w, h){
17140         // support for standard size objects
17141         if(typeof w == 'object'){
17142             h = w.height;
17143             w = w.width;
17144         }
17145         // not rendered
17146         if(!this.boxReady){
17147             this.width = w;
17148             this.height = h;
17149             return this;
17150         }
17151
17152         // prevent recalcs when not needed
17153         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17154             return this;
17155         }
17156         this.lastSize = {width: w, height: h};
17157
17158         var adj = this.adjustSize(w, h);
17159         var aw = adj.width, ah = adj.height;
17160         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17161             var rz = this.getResizeEl();
17162             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17163                 rz.setSize(aw, ah);
17164             }else if(!this.deferHeight && ah !== undefined){
17165                 rz.setHeight(ah);
17166             }else if(aw !== undefined){
17167                 rz.setWidth(aw);
17168             }
17169             this.onResize(aw, ah, w, h);
17170             this.fireEvent('resize', this, aw, ah, w, h);
17171         }
17172         return this;
17173     },
17174
17175     /**
17176      * Gets the current size of the component's underlying element.
17177      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17178      */
17179     getSize : function(){
17180         return this.el.getSize();
17181     },
17182
17183     /**
17184      * Gets the current XY position of the component's underlying element.
17185      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17186      * @return {Array} The XY position of the element (e.g., [100, 200])
17187      */
17188     getPosition : function(local){
17189         if(local === true){
17190             return [this.el.getLeft(true), this.el.getTop(true)];
17191         }
17192         return this.xy || this.el.getXY();
17193     },
17194
17195     /**
17196      * Gets the current box measurements of the component's underlying element.
17197      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17198      * @returns {Object} box An object in the format {x, y, width, height}
17199      */
17200     getBox : function(local){
17201         var s = this.el.getSize();
17202         if(local){
17203             s.x = this.el.getLeft(true);
17204             s.y = this.el.getTop(true);
17205         }else{
17206             var xy = this.xy || this.el.getXY();
17207             s.x = xy[0];
17208             s.y = xy[1];
17209         }
17210         return s;
17211     },
17212
17213     /**
17214      * Sets the current box measurements of the component's underlying element.
17215      * @param {Object} box An object in the format {x, y, width, height}
17216      * @returns {Roo.BoxComponent} this
17217      */
17218     updateBox : function(box){
17219         this.setSize(box.width, box.height);
17220         this.setPagePosition(box.x, box.y);
17221         return this;
17222     },
17223
17224     // protected
17225     getResizeEl : function(){
17226         return this.resizeEl || this.el;
17227     },
17228
17229     // protected
17230     getPositionEl : function(){
17231         return this.positionEl || this.el;
17232     },
17233
17234     /**
17235      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17236      * This method fires the move event.
17237      * @param {Number} left The new left
17238      * @param {Number} top The new top
17239      * @returns {Roo.BoxComponent} this
17240      */
17241     setPosition : function(x, y){
17242         this.x = x;
17243         this.y = y;
17244         if(!this.boxReady){
17245             return this;
17246         }
17247         var adj = this.adjustPosition(x, y);
17248         var ax = adj.x, ay = adj.y;
17249
17250         var el = this.getPositionEl();
17251         if(ax !== undefined || ay !== undefined){
17252             if(ax !== undefined && ay !== undefined){
17253                 el.setLeftTop(ax, ay);
17254             }else if(ax !== undefined){
17255                 el.setLeft(ax);
17256             }else if(ay !== undefined){
17257                 el.setTop(ay);
17258             }
17259             this.onPosition(ax, ay);
17260             this.fireEvent('move', this, ax, ay);
17261         }
17262         return this;
17263     },
17264
17265     /**
17266      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17267      * This method fires the move event.
17268      * @param {Number} x The new x position
17269      * @param {Number} y The new y position
17270      * @returns {Roo.BoxComponent} this
17271      */
17272     setPagePosition : function(x, y){
17273         this.pageX = x;
17274         this.pageY = y;
17275         if(!this.boxReady){
17276             return;
17277         }
17278         if(x === undefined || y === undefined){ // cannot translate undefined points
17279             return;
17280         }
17281         var p = this.el.translatePoints(x, y);
17282         this.setPosition(p.left, p.top);
17283         return this;
17284     },
17285
17286     // private
17287     onRender : function(ct, position){
17288         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17289         if(this.resizeEl){
17290             this.resizeEl = Roo.get(this.resizeEl);
17291         }
17292         if(this.positionEl){
17293             this.positionEl = Roo.get(this.positionEl);
17294         }
17295     },
17296
17297     // private
17298     afterRender : function(){
17299         Roo.BoxComponent.superclass.afterRender.call(this);
17300         this.boxReady = true;
17301         this.setSize(this.width, this.height);
17302         if(this.x || this.y){
17303             this.setPosition(this.x, this.y);
17304         }
17305         if(this.pageX || this.pageY){
17306             this.setPagePosition(this.pageX, this.pageY);
17307         }
17308     },
17309
17310     /**
17311      * Force the component's size to recalculate based on the underlying element's current height and width.
17312      * @returns {Roo.BoxComponent} this
17313      */
17314     syncSize : function(){
17315         delete this.lastSize;
17316         this.setSize(this.el.getWidth(), this.el.getHeight());
17317         return this;
17318     },
17319
17320     /**
17321      * Called after the component is resized, this method is empty by default but can be implemented by any
17322      * subclass that needs to perform custom logic after a resize occurs.
17323      * @param {Number} adjWidth The box-adjusted width that was set
17324      * @param {Number} adjHeight The box-adjusted height that was set
17325      * @param {Number} rawWidth The width that was originally specified
17326      * @param {Number} rawHeight The height that was originally specified
17327      */
17328     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17329
17330     },
17331
17332     /**
17333      * Called after the component is moved, this method is empty by default but can be implemented by any
17334      * subclass that needs to perform custom logic after a move occurs.
17335      * @param {Number} x The new x position
17336      * @param {Number} y The new y position
17337      */
17338     onPosition : function(x, y){
17339
17340     },
17341
17342     // private
17343     adjustSize : function(w, h){
17344         if(this.autoWidth){
17345             w = 'auto';
17346         }
17347         if(this.autoHeight){
17348             h = 'auto';
17349         }
17350         return {width : w, height: h};
17351     },
17352
17353     // private
17354     adjustPosition : function(x, y){
17355         return {x : x, y: y};
17356     }
17357 });/*
17358  * Based on:
17359  * Ext JS Library 1.1.1
17360  * Copyright(c) 2006-2007, Ext JS, LLC.
17361  *
17362  * Originally Released Under LGPL - original licence link has changed is not relivant.
17363  *
17364  * Fork - LGPL
17365  * <script type="text/javascript">
17366  */
17367  (function(){ 
17368 /**
17369  * @class Roo.Layer
17370  * @extends Roo.Element
17371  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17372  * automatic maintaining of shadow/shim positions.
17373  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17374  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17375  * you can pass a string with a CSS class name. False turns off the shadow.
17376  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17377  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17378  * @cfg {String} cls CSS class to add to the element
17379  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17380  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17381  * @constructor
17382  * @param {Object} config An object with config options.
17383  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17384  */
17385
17386 Roo.Layer = function(config, existingEl){
17387     config = config || {};
17388     var dh = Roo.DomHelper;
17389     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17390     if(existingEl){
17391         this.dom = Roo.getDom(existingEl);
17392     }
17393     if(!this.dom){
17394         var o = config.dh || {tag: "div", cls: "x-layer"};
17395         this.dom = dh.append(pel, o);
17396     }
17397     if(config.cls){
17398         this.addClass(config.cls);
17399     }
17400     this.constrain = config.constrain !== false;
17401     this.visibilityMode = Roo.Element.VISIBILITY;
17402     if(config.id){
17403         this.id = this.dom.id = config.id;
17404     }else{
17405         this.id = Roo.id(this.dom);
17406     }
17407     this.zindex = config.zindex || this.getZIndex();
17408     this.position("absolute", this.zindex);
17409     if(config.shadow){
17410         this.shadowOffset = config.shadowOffset || 4;
17411         this.shadow = new Roo.Shadow({
17412             offset : this.shadowOffset,
17413             mode : config.shadow
17414         });
17415     }else{
17416         this.shadowOffset = 0;
17417     }
17418     this.useShim = config.shim !== false && Roo.useShims;
17419     this.useDisplay = config.useDisplay;
17420     this.hide();
17421 };
17422
17423 var supr = Roo.Element.prototype;
17424
17425 // shims are shared among layer to keep from having 100 iframes
17426 var shims = [];
17427
17428 Roo.extend(Roo.Layer, Roo.Element, {
17429
17430     getZIndex : function(){
17431         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17432     },
17433
17434     getShim : function(){
17435         if(!this.useShim){
17436             return null;
17437         }
17438         if(this.shim){
17439             return this.shim;
17440         }
17441         var shim = shims.shift();
17442         if(!shim){
17443             shim = this.createShim();
17444             shim.enableDisplayMode('block');
17445             shim.dom.style.display = 'none';
17446             shim.dom.style.visibility = 'visible';
17447         }
17448         var pn = this.dom.parentNode;
17449         if(shim.dom.parentNode != pn){
17450             pn.insertBefore(shim.dom, this.dom);
17451         }
17452         shim.setStyle('z-index', this.getZIndex()-2);
17453         this.shim = shim;
17454         return shim;
17455     },
17456
17457     hideShim : function(){
17458         if(this.shim){
17459             this.shim.setDisplayed(false);
17460             shims.push(this.shim);
17461             delete this.shim;
17462         }
17463     },
17464
17465     disableShadow : function(){
17466         if(this.shadow){
17467             this.shadowDisabled = true;
17468             this.shadow.hide();
17469             this.lastShadowOffset = this.shadowOffset;
17470             this.shadowOffset = 0;
17471         }
17472     },
17473
17474     enableShadow : function(show){
17475         if(this.shadow){
17476             this.shadowDisabled = false;
17477             this.shadowOffset = this.lastShadowOffset;
17478             delete this.lastShadowOffset;
17479             if(show){
17480                 this.sync(true);
17481             }
17482         }
17483     },
17484
17485     // private
17486     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17487     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17488     sync : function(doShow){
17489         var sw = this.shadow;
17490         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17491             var sh = this.getShim();
17492
17493             var w = this.getWidth(),
17494                 h = this.getHeight();
17495
17496             var l = this.getLeft(true),
17497                 t = this.getTop(true);
17498
17499             if(sw && !this.shadowDisabled){
17500                 if(doShow && !sw.isVisible()){
17501                     sw.show(this);
17502                 }else{
17503                     sw.realign(l, t, w, h);
17504                 }
17505                 if(sh){
17506                     if(doShow){
17507                        sh.show();
17508                     }
17509                     // fit the shim behind the shadow, so it is shimmed too
17510                     var a = sw.adjusts, s = sh.dom.style;
17511                     s.left = (Math.min(l, l+a.l))+"px";
17512                     s.top = (Math.min(t, t+a.t))+"px";
17513                     s.width = (w+a.w)+"px";
17514                     s.height = (h+a.h)+"px";
17515                 }
17516             }else if(sh){
17517                 if(doShow){
17518                    sh.show();
17519                 }
17520                 sh.setSize(w, h);
17521                 sh.setLeftTop(l, t);
17522             }
17523             
17524         }
17525     },
17526
17527     // private
17528     destroy : function(){
17529         this.hideShim();
17530         if(this.shadow){
17531             this.shadow.hide();
17532         }
17533         this.removeAllListeners();
17534         var pn = this.dom.parentNode;
17535         if(pn){
17536             pn.removeChild(this.dom);
17537         }
17538         Roo.Element.uncache(this.id);
17539     },
17540
17541     remove : function(){
17542         this.destroy();
17543     },
17544
17545     // private
17546     beginUpdate : function(){
17547         this.updating = true;
17548     },
17549
17550     // private
17551     endUpdate : function(){
17552         this.updating = false;
17553         this.sync(true);
17554     },
17555
17556     // private
17557     hideUnders : function(negOffset){
17558         if(this.shadow){
17559             this.shadow.hide();
17560         }
17561         this.hideShim();
17562     },
17563
17564     // private
17565     constrainXY : function(){
17566         if(this.constrain){
17567             var vw = Roo.lib.Dom.getViewWidth(),
17568                 vh = Roo.lib.Dom.getViewHeight();
17569             var s = Roo.get(document).getScroll();
17570
17571             var xy = this.getXY();
17572             var x = xy[0], y = xy[1];   
17573             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17574             // only move it if it needs it
17575             var moved = false;
17576             // first validate right/bottom
17577             if((x + w) > vw+s.left){
17578                 x = vw - w - this.shadowOffset;
17579                 moved = true;
17580             }
17581             if((y + h) > vh+s.top){
17582                 y = vh - h - this.shadowOffset;
17583                 moved = true;
17584             }
17585             // then make sure top/left isn't negative
17586             if(x < s.left){
17587                 x = s.left;
17588                 moved = true;
17589             }
17590             if(y < s.top){
17591                 y = s.top;
17592                 moved = true;
17593             }
17594             if(moved){
17595                 if(this.avoidY){
17596                     var ay = this.avoidY;
17597                     if(y <= ay && (y+h) >= ay){
17598                         y = ay-h-5;   
17599                     }
17600                 }
17601                 xy = [x, y];
17602                 this.storeXY(xy);
17603                 supr.setXY.call(this, xy);
17604                 this.sync();
17605             }
17606         }
17607     },
17608
17609     isVisible : function(){
17610         return this.visible;    
17611     },
17612
17613     // private
17614     showAction : function(){
17615         this.visible = true; // track visibility to prevent getStyle calls
17616         if(this.useDisplay === true){
17617             this.setDisplayed("");
17618         }else if(this.lastXY){
17619             supr.setXY.call(this, this.lastXY);
17620         }else if(this.lastLT){
17621             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17622         }
17623     },
17624
17625     // private
17626     hideAction : function(){
17627         this.visible = false;
17628         if(this.useDisplay === true){
17629             this.setDisplayed(false);
17630         }else{
17631             this.setLeftTop(-10000,-10000);
17632         }
17633     },
17634
17635     // overridden Element method
17636     setVisible : function(v, a, d, c, e){
17637         if(v){
17638             this.showAction();
17639         }
17640         if(a && v){
17641             var cb = function(){
17642                 this.sync(true);
17643                 if(c){
17644                     c();
17645                 }
17646             }.createDelegate(this);
17647             supr.setVisible.call(this, true, true, d, cb, e);
17648         }else{
17649             if(!v){
17650                 this.hideUnders(true);
17651             }
17652             var cb = c;
17653             if(a){
17654                 cb = function(){
17655                     this.hideAction();
17656                     if(c){
17657                         c();
17658                     }
17659                 }.createDelegate(this);
17660             }
17661             supr.setVisible.call(this, v, a, d, cb, e);
17662             if(v){
17663                 this.sync(true);
17664             }else if(!a){
17665                 this.hideAction();
17666             }
17667         }
17668     },
17669
17670     storeXY : function(xy){
17671         delete this.lastLT;
17672         this.lastXY = xy;
17673     },
17674
17675     storeLeftTop : function(left, top){
17676         delete this.lastXY;
17677         this.lastLT = [left, top];
17678     },
17679
17680     // private
17681     beforeFx : function(){
17682         this.beforeAction();
17683         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17684     },
17685
17686     // private
17687     afterFx : function(){
17688         Roo.Layer.superclass.afterFx.apply(this, arguments);
17689         this.sync(this.isVisible());
17690     },
17691
17692     // private
17693     beforeAction : function(){
17694         if(!this.updating && this.shadow){
17695             this.shadow.hide();
17696         }
17697     },
17698
17699     // overridden Element method
17700     setLeft : function(left){
17701         this.storeLeftTop(left, this.getTop(true));
17702         supr.setLeft.apply(this, arguments);
17703         this.sync();
17704     },
17705
17706     setTop : function(top){
17707         this.storeLeftTop(this.getLeft(true), top);
17708         supr.setTop.apply(this, arguments);
17709         this.sync();
17710     },
17711
17712     setLeftTop : function(left, top){
17713         this.storeLeftTop(left, top);
17714         supr.setLeftTop.apply(this, arguments);
17715         this.sync();
17716     },
17717
17718     setXY : function(xy, a, d, c, e){
17719         this.fixDisplay();
17720         this.beforeAction();
17721         this.storeXY(xy);
17722         var cb = this.createCB(c);
17723         supr.setXY.call(this, xy, a, d, cb, e);
17724         if(!a){
17725             cb();
17726         }
17727     },
17728
17729     // private
17730     createCB : function(c){
17731         var el = this;
17732         return function(){
17733             el.constrainXY();
17734             el.sync(true);
17735             if(c){
17736                 c();
17737             }
17738         };
17739     },
17740
17741     // overridden Element method
17742     setX : function(x, a, d, c, e){
17743         this.setXY([x, this.getY()], a, d, c, e);
17744     },
17745
17746     // overridden Element method
17747     setY : function(y, a, d, c, e){
17748         this.setXY([this.getX(), y], a, d, c, e);
17749     },
17750
17751     // overridden Element method
17752     setSize : function(w, h, a, d, c, e){
17753         this.beforeAction();
17754         var cb = this.createCB(c);
17755         supr.setSize.call(this, w, h, a, d, cb, e);
17756         if(!a){
17757             cb();
17758         }
17759     },
17760
17761     // overridden Element method
17762     setWidth : function(w, a, d, c, e){
17763         this.beforeAction();
17764         var cb = this.createCB(c);
17765         supr.setWidth.call(this, w, a, d, cb, e);
17766         if(!a){
17767             cb();
17768         }
17769     },
17770
17771     // overridden Element method
17772     setHeight : function(h, a, d, c, e){
17773         this.beforeAction();
17774         var cb = this.createCB(c);
17775         supr.setHeight.call(this, h, a, d, cb, e);
17776         if(!a){
17777             cb();
17778         }
17779     },
17780
17781     // overridden Element method
17782     setBounds : function(x, y, w, h, a, d, c, e){
17783         this.beforeAction();
17784         var cb = this.createCB(c);
17785         if(!a){
17786             this.storeXY([x, y]);
17787             supr.setXY.call(this, [x, y]);
17788             supr.setSize.call(this, w, h, a, d, cb, e);
17789             cb();
17790         }else{
17791             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17792         }
17793         return this;
17794     },
17795     
17796     /**
17797      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17798      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17799      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17800      * @param {Number} zindex The new z-index to set
17801      * @return {this} The Layer
17802      */
17803     setZIndex : function(zindex){
17804         this.zindex = zindex;
17805         this.setStyle("z-index", zindex + 2);
17806         if(this.shadow){
17807             this.shadow.setZIndex(zindex + 1);
17808         }
17809         if(this.shim){
17810             this.shim.setStyle("z-index", zindex);
17811         }
17812     }
17813 });
17814 })();/*
17815  * Original code for Roojs - LGPL
17816  * <script type="text/javascript">
17817  */
17818  
17819 /**
17820  * @class Roo.XComponent
17821  * A delayed Element creator...
17822  * Or a way to group chunks of interface together.
17823  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17824  *  used in conjunction with XComponent.build() it will create an instance of each element,
17825  *  then call addxtype() to build the User interface.
17826  * 
17827  * Mypart.xyx = new Roo.XComponent({
17828
17829     parent : 'Mypart.xyz', // empty == document.element.!!
17830     order : '001',
17831     name : 'xxxx'
17832     region : 'xxxx'
17833     disabled : function() {} 
17834      
17835     tree : function() { // return an tree of xtype declared components
17836         var MODULE = this;
17837         return 
17838         {
17839             xtype : 'NestedLayoutPanel',
17840             // technicall
17841         }
17842      ]
17843  *})
17844  *
17845  *
17846  * It can be used to build a big heiracy, with parent etc.
17847  * or you can just use this to render a single compoent to a dom element
17848  * MYPART.render(Roo.Element | String(id) | dom_element )
17849  *
17850  *
17851  * Usage patterns.
17852  *
17853  * Classic Roo
17854  *
17855  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17856  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17857  *
17858  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17859  *
17860  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17861  * - if mulitple topModules exist, the last one is defined as the top module.
17862  *
17863  * Embeded Roo
17864  * 
17865  * When the top level or multiple modules are to embedded into a existing HTML page,
17866  * the parent element can container '#id' of the element where the module will be drawn.
17867  *
17868  * Bootstrap Roo
17869  *
17870  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17871  * it relies more on a include mechanism, where sub modules are included into an outer page.
17872  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17873  * 
17874  * Bootstrap Roo Included elements
17875  *
17876  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17877  * hence confusing the component builder as it thinks there are multiple top level elements. 
17878  *
17879  * String Over-ride & Translations
17880  *
17881  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17882  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17883  * are needed. @see Roo.XComponent.overlayString  
17884  * 
17885  * 
17886  * 
17887  * @extends Roo.util.Observable
17888  * @constructor
17889  * @param cfg {Object} configuration of component
17890  * 
17891  */
17892 Roo.XComponent = function(cfg) {
17893     Roo.apply(this, cfg);
17894     this.addEvents({ 
17895         /**
17896              * @event built
17897              * Fires when this the componnt is built
17898              * @param {Roo.XComponent} c the component
17899              */
17900         'built' : true
17901         
17902     });
17903     this.region = this.region || 'center'; // default..
17904     Roo.XComponent.register(this);
17905     this.modules = false;
17906     this.el = false; // where the layout goes..
17907     
17908     
17909 }
17910 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17911     /**
17912      * @property el
17913      * The created element (with Roo.factory())
17914      * @type {Roo.Layout}
17915      */
17916     el  : false,
17917     
17918     /**
17919      * @property el
17920      * for BC  - use el in new code
17921      * @type {Roo.Layout}
17922      */
17923     panel : false,
17924     
17925     /**
17926      * @property layout
17927      * for BC  - use el in new code
17928      * @type {Roo.Layout}
17929      */
17930     layout : false,
17931     
17932      /**
17933      * @cfg {Function|boolean} disabled
17934      * If this module is disabled by some rule, return true from the funtion
17935      */
17936     disabled : false,
17937     
17938     /**
17939      * @cfg {String} parent 
17940      * Name of parent element which it get xtype added to..
17941      */
17942     parent: false,
17943     
17944     /**
17945      * @cfg {String} order
17946      * Used to set the order in which elements are created (usefull for multiple tabs)
17947      */
17948     
17949     order : false,
17950     /**
17951      * @cfg {String} name
17952      * String to display while loading.
17953      */
17954     name : false,
17955     /**
17956      * @cfg {String} region
17957      * Region to render component to (defaults to center)
17958      */
17959     region : 'center',
17960     
17961     /**
17962      * @cfg {Array} items
17963      * A single item array - the first element is the root of the tree..
17964      * It's done this way to stay compatible with the Xtype system...
17965      */
17966     items : false,
17967     
17968     /**
17969      * @property _tree
17970      * The method that retuns the tree of parts that make up this compoennt 
17971      * @type {function}
17972      */
17973     _tree  : false,
17974     
17975      /**
17976      * render
17977      * render element to dom or tree
17978      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17979      */
17980     
17981     render : function(el)
17982     {
17983         
17984         el = el || false;
17985         var hp = this.parent ? 1 : 0;
17986         Roo.debug &&  Roo.log(this);
17987         
17988         var tree = this._tree ? this._tree() : this.tree();
17989
17990         
17991         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17992             // if parent is a '#.....' string, then let's use that..
17993             var ename = this.parent.substr(1);
17994             this.parent = false;
17995             Roo.debug && Roo.log(ename);
17996             switch (ename) {
17997                 case 'bootstrap-body':
17998                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17999                         // this is the BorderLayout standard?
18000                        this.parent = { el : true };
18001                        break;
18002                     }
18003                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18004                         // need to insert stuff...
18005                         this.parent =  {
18006                              el : new Roo.bootstrap.layout.Border({
18007                                  el : document.body, 
18008                      
18009                                  center: {
18010                                     titlebar: false,
18011                                     autoScroll:false,
18012                                     closeOnTab: true,
18013                                     tabPosition: 'top',
18014                                       //resizeTabs: true,
18015                                     alwaysShowTabs: true,
18016                                     hideTabs: false
18017                                      //minTabWidth: 140
18018                                  }
18019                              })
18020                         
18021                          };
18022                          break;
18023                     }
18024                          
18025                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18026                         this.parent = { el :  new  Roo.bootstrap.Body() };
18027                         Roo.debug && Roo.log("setting el to doc body");
18028                          
18029                     } else {
18030                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18031                     }
18032                     break;
18033                 case 'bootstrap':
18034                     this.parent = { el : true};
18035                     // fall through
18036                 default:
18037                     el = Roo.get(ename);
18038                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18039                         this.parent = { el : true};
18040                     }
18041                     
18042                     break;
18043             }
18044                 
18045             
18046             if (!el && !this.parent) {
18047                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18048                 return;
18049             }
18050         }
18051         
18052         Roo.debug && Roo.log("EL:");
18053         Roo.debug && Roo.log(el);
18054         Roo.debug && Roo.log("this.parent.el:");
18055         Roo.debug && Roo.log(this.parent.el);
18056         
18057
18058         // altertive root elements ??? - we need a better way to indicate these.
18059         var is_alt = Roo.XComponent.is_alt ||
18060                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18061                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18062                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18063         
18064         
18065         
18066         if (!this.parent && is_alt) {
18067             //el = Roo.get(document.body);
18068             this.parent = { el : true };
18069         }
18070             
18071             
18072         
18073         if (!this.parent) {
18074             
18075             Roo.debug && Roo.log("no parent - creating one");
18076             
18077             el = el ? Roo.get(el) : false;      
18078             
18079             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18080                 
18081                 this.parent =  {
18082                     el : new Roo.bootstrap.layout.Border({
18083                         el: el || document.body,
18084                     
18085                         center: {
18086                             titlebar: false,
18087                             autoScroll:false,
18088                             closeOnTab: true,
18089                             tabPosition: 'top',
18090                              //resizeTabs: true,
18091                             alwaysShowTabs: false,
18092                             hideTabs: true,
18093                             minTabWidth: 140,
18094                             overflow: 'visible'
18095                          }
18096                      })
18097                 };
18098             } else {
18099             
18100                 // it's a top level one..
18101                 this.parent =  {
18102                     el : new Roo.BorderLayout(el || document.body, {
18103                         center: {
18104                             titlebar: false,
18105                             autoScroll:false,
18106                             closeOnTab: true,
18107                             tabPosition: 'top',
18108                              //resizeTabs: true,
18109                             alwaysShowTabs: el && hp? false :  true,
18110                             hideTabs: el || !hp ? true :  false,
18111                             minTabWidth: 140
18112                          }
18113                     })
18114                 };
18115             }
18116         }
18117         
18118         if (!this.parent.el) {
18119                 // probably an old style ctor, which has been disabled.
18120                 return;
18121
18122         }
18123                 // The 'tree' method is  '_tree now' 
18124             
18125         tree.region = tree.region || this.region;
18126         var is_body = false;
18127         if (this.parent.el === true) {
18128             // bootstrap... - body..
18129             if (el) {
18130                 tree.el = el;
18131             }
18132             this.parent.el = Roo.factory(tree);
18133             is_body = true;
18134         }
18135         
18136         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18137         this.fireEvent('built', this);
18138         
18139         this.panel = this.el;
18140         this.layout = this.panel.layout;
18141         this.parentLayout = this.parent.layout  || false;  
18142          
18143     }
18144     
18145 });
18146
18147 Roo.apply(Roo.XComponent, {
18148     /**
18149      * @property  hideProgress
18150      * true to disable the building progress bar.. usefull on single page renders.
18151      * @type Boolean
18152      */
18153     hideProgress : false,
18154     /**
18155      * @property  buildCompleted
18156      * True when the builder has completed building the interface.
18157      * @type Boolean
18158      */
18159     buildCompleted : false,
18160      
18161     /**
18162      * @property  topModule
18163      * the upper most module - uses document.element as it's constructor.
18164      * @type Object
18165      */
18166      
18167     topModule  : false,
18168       
18169     /**
18170      * @property  modules
18171      * array of modules to be created by registration system.
18172      * @type {Array} of Roo.XComponent
18173      */
18174     
18175     modules : [],
18176     /**
18177      * @property  elmodules
18178      * array of modules to be created by which use #ID 
18179      * @type {Array} of Roo.XComponent
18180      */
18181      
18182     elmodules : [],
18183
18184      /**
18185      * @property  is_alt
18186      * Is an alternative Root - normally used by bootstrap or other systems,
18187      *    where the top element in the tree can wrap 'body' 
18188      * @type {boolean}  (default false)
18189      */
18190      
18191     is_alt : false,
18192     /**
18193      * @property  build_from_html
18194      * Build elements from html - used by bootstrap HTML stuff 
18195      *    - this is cleared after build is completed
18196      * @type {boolean}    (default false)
18197      */
18198      
18199     build_from_html : false,
18200     /**
18201      * Register components to be built later.
18202      *
18203      * This solves the following issues
18204      * - Building is not done on page load, but after an authentication process has occured.
18205      * - Interface elements are registered on page load
18206      * - Parent Interface elements may not be loaded before child, so this handles that..
18207      * 
18208      *
18209      * example:
18210      * 
18211      * MyApp.register({
18212           order : '000001',
18213           module : 'Pman.Tab.projectMgr',
18214           region : 'center',
18215           parent : 'Pman.layout',
18216           disabled : false,  // or use a function..
18217         })
18218      
18219      * * @param {Object} details about module
18220      */
18221     register : function(obj) {
18222                 
18223         Roo.XComponent.event.fireEvent('register', obj);
18224         switch(typeof(obj.disabled) ) {
18225                 
18226             case 'undefined':
18227                 break;
18228             
18229             case 'function':
18230                 if ( obj.disabled() ) {
18231                         return;
18232                 }
18233                 break;
18234             
18235             default:
18236                 if (obj.disabled || obj.region == '#disabled') {
18237                         return;
18238                 }
18239                 break;
18240         }
18241                 
18242         this.modules.push(obj);
18243          
18244     },
18245     /**
18246      * convert a string to an object..
18247      * eg. 'AAA.BBB' -> finds AAA.BBB
18248
18249      */
18250     
18251     toObject : function(str)
18252     {
18253         if (!str || typeof(str) == 'object') {
18254             return str;
18255         }
18256         if (str.substring(0,1) == '#') {
18257             return str;
18258         }
18259
18260         var ar = str.split('.');
18261         var rt, o;
18262         rt = ar.shift();
18263             /** eval:var:o */
18264         try {
18265             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18266         } catch (e) {
18267             throw "Module not found : " + str;
18268         }
18269         
18270         if (o === false) {
18271             throw "Module not found : " + str;
18272         }
18273         Roo.each(ar, function(e) {
18274             if (typeof(o[e]) == 'undefined') {
18275                 throw "Module not found : " + str;
18276             }
18277             o = o[e];
18278         });
18279         
18280         return o;
18281         
18282     },
18283     
18284     
18285     /**
18286      * move modules into their correct place in the tree..
18287      * 
18288      */
18289     preBuild : function ()
18290     {
18291         var _t = this;
18292         Roo.each(this.modules , function (obj)
18293         {
18294             Roo.XComponent.event.fireEvent('beforebuild', obj);
18295             
18296             var opar = obj.parent;
18297             try { 
18298                 obj.parent = this.toObject(opar);
18299             } catch(e) {
18300                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18301                 return;
18302             }
18303             
18304             if (!obj.parent) {
18305                 Roo.debug && Roo.log("GOT top level module");
18306                 Roo.debug && Roo.log(obj);
18307                 obj.modules = new Roo.util.MixedCollection(false, 
18308                     function(o) { return o.order + '' }
18309                 );
18310                 this.topModule = obj;
18311                 return;
18312             }
18313                         // parent is a string (usually a dom element name..)
18314             if (typeof(obj.parent) == 'string') {
18315                 this.elmodules.push(obj);
18316                 return;
18317             }
18318             if (obj.parent.constructor != Roo.XComponent) {
18319                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18320             }
18321             if (!obj.parent.modules) {
18322                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18323                     function(o) { return o.order + '' }
18324                 );
18325             }
18326             if (obj.parent.disabled) {
18327                 obj.disabled = true;
18328             }
18329             obj.parent.modules.add(obj);
18330         }, this);
18331     },
18332     
18333      /**
18334      * make a list of modules to build.
18335      * @return {Array} list of modules. 
18336      */ 
18337     
18338     buildOrder : function()
18339     {
18340         var _this = this;
18341         var cmp = function(a,b) {   
18342             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18343         };
18344         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18345             throw "No top level modules to build";
18346         }
18347         
18348         // make a flat list in order of modules to build.
18349         var mods = this.topModule ? [ this.topModule ] : [];
18350                 
18351         
18352         // elmodules (is a list of DOM based modules )
18353         Roo.each(this.elmodules, function(e) {
18354             mods.push(e);
18355             if (!this.topModule &&
18356                 typeof(e.parent) == 'string' &&
18357                 e.parent.substring(0,1) == '#' &&
18358                 Roo.get(e.parent.substr(1))
18359                ) {
18360                 
18361                 _this.topModule = e;
18362             }
18363             
18364         });
18365
18366         
18367         // add modules to their parents..
18368         var addMod = function(m) {
18369             Roo.debug && Roo.log("build Order: add: " + m.name);
18370                 
18371             mods.push(m);
18372             if (m.modules && !m.disabled) {
18373                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18374                 m.modules.keySort('ASC',  cmp );
18375                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18376     
18377                 m.modules.each(addMod);
18378             } else {
18379                 Roo.debug && Roo.log("build Order: no child modules");
18380             }
18381             // not sure if this is used any more..
18382             if (m.finalize) {
18383                 m.finalize.name = m.name + " (clean up) ";
18384                 mods.push(m.finalize);
18385             }
18386             
18387         }
18388         if (this.topModule && this.topModule.modules) { 
18389             this.topModule.modules.keySort('ASC',  cmp );
18390             this.topModule.modules.each(addMod);
18391         } 
18392         return mods;
18393     },
18394     
18395      /**
18396      * Build the registered modules.
18397      * @param {Object} parent element.
18398      * @param {Function} optional method to call after module has been added.
18399      * 
18400      */ 
18401    
18402     build : function(opts) 
18403     {
18404         
18405         if (typeof(opts) != 'undefined') {
18406             Roo.apply(this,opts);
18407         }
18408         
18409         this.preBuild();
18410         var mods = this.buildOrder();
18411       
18412         //this.allmods = mods;
18413         //Roo.debug && Roo.log(mods);
18414         //return;
18415         if (!mods.length) { // should not happen
18416             throw "NO modules!!!";
18417         }
18418         
18419         
18420         var msg = "Building Interface...";
18421         // flash it up as modal - so we store the mask!?
18422         if (!this.hideProgress && Roo.MessageBox) {
18423             Roo.MessageBox.show({ title: 'loading' });
18424             Roo.MessageBox.show({
18425                title: "Please wait...",
18426                msg: msg,
18427                width:450,
18428                progress:true,
18429                buttons : false,
18430                closable:false,
18431                modal: false
18432               
18433             });
18434         }
18435         var total = mods.length;
18436         
18437         var _this = this;
18438         var progressRun = function() {
18439             if (!mods.length) {
18440                 Roo.debug && Roo.log('hide?');
18441                 if (!this.hideProgress && Roo.MessageBox) {
18442                     Roo.MessageBox.hide();
18443                 }
18444                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18445                 
18446                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18447                 
18448                 // THE END...
18449                 return false;   
18450             }
18451             
18452             var m = mods.shift();
18453             
18454             
18455             Roo.debug && Roo.log(m);
18456             // not sure if this is supported any more.. - modules that are are just function
18457             if (typeof(m) == 'function') { 
18458                 m.call(this);
18459                 return progressRun.defer(10, _this);
18460             } 
18461             
18462             
18463             msg = "Building Interface " + (total  - mods.length) + 
18464                     " of " + total + 
18465                     (m.name ? (' - ' + m.name) : '');
18466                         Roo.debug && Roo.log(msg);
18467             if (!_this.hideProgress &&  Roo.MessageBox) { 
18468                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18469             }
18470             
18471          
18472             // is the module disabled?
18473             var disabled = (typeof(m.disabled) == 'function') ?
18474                 m.disabled.call(m.module.disabled) : m.disabled;    
18475             
18476             
18477             if (disabled) {
18478                 return progressRun(); // we do not update the display!
18479             }
18480             
18481             // now build 
18482             
18483                         
18484                         
18485             m.render();
18486             // it's 10 on top level, and 1 on others??? why...
18487             return progressRun.defer(10, _this);
18488              
18489         }
18490         progressRun.defer(1, _this);
18491      
18492         
18493         
18494     },
18495     /**
18496      * Overlay a set of modified strings onto a component
18497      * This is dependant on our builder exporting the strings and 'named strings' elements.
18498      * 
18499      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18500      * @param {Object} associative array of 'named' string and it's new value.
18501      * 
18502      */
18503         overlayStrings : function( component, strings )
18504     {
18505         if (typeof(component['_named_strings']) == 'undefined') {
18506             throw "ERROR: component does not have _named_strings";
18507         }
18508         for ( var k in strings ) {
18509             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18510             if (md !== false) {
18511                 component['_strings'][md] = strings[k];
18512             } else {
18513                 Roo.log('could not find named string: ' + k + ' in');
18514                 Roo.log(component);
18515             }
18516             
18517         }
18518         
18519     },
18520     
18521         
18522         /**
18523          * Event Object.
18524          *
18525          *
18526          */
18527         event: false, 
18528     /**
18529          * wrapper for event.on - aliased later..  
18530          * Typically use to register a event handler for register:
18531          *
18532          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18533          *
18534          */
18535     on : false
18536    
18537     
18538     
18539 });
18540
18541 Roo.XComponent.event = new Roo.util.Observable({
18542                 events : { 
18543                         /**
18544                          * @event register
18545                          * Fires when an Component is registered,
18546                          * set the disable property on the Component to stop registration.
18547                          * @param {Roo.XComponent} c the component being registerd.
18548                          * 
18549                          */
18550                         'register' : true,
18551             /**
18552                          * @event beforebuild
18553                          * Fires before each Component is built
18554                          * can be used to apply permissions.
18555                          * @param {Roo.XComponent} c the component being registerd.
18556                          * 
18557                          */
18558                         'beforebuild' : true,
18559                         /**
18560                          * @event buildcomplete
18561                          * Fires on the top level element when all elements have been built
18562                          * @param {Roo.XComponent} the top level component.
18563                          */
18564                         'buildcomplete' : true
18565                         
18566                 }
18567 });
18568
18569 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18570  //
18571  /**
18572  * marked - a markdown parser
18573  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18574  * https://github.com/chjj/marked
18575  */
18576
18577
18578 /**
18579  *
18580  * Roo.Markdown - is a very crude wrapper around marked..
18581  *
18582  * usage:
18583  * 
18584  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18585  * 
18586  * Note: move the sample code to the bottom of this
18587  * file before uncommenting it.
18588  *
18589  */
18590
18591 Roo.Markdown = {};
18592 Roo.Markdown.toHtml = function(text) {
18593     
18594     var c = new Roo.Markdown.marked.setOptions({
18595             renderer: new Roo.Markdown.marked.Renderer(),
18596             gfm: true,
18597             tables: true,
18598             breaks: false,
18599             pedantic: false,
18600             sanitize: false,
18601             smartLists: true,
18602             smartypants: false
18603           });
18604     // A FEW HACKS!!?
18605     
18606     text = text.replace(/\\\n/g,' ');
18607     return Roo.Markdown.marked(text);
18608 };
18609 //
18610 // converter
18611 //
18612 // Wraps all "globals" so that the only thing
18613 // exposed is makeHtml().
18614 //
18615 (function() {
18616     
18617      /**
18618          * eval:var:escape
18619          * eval:var:unescape
18620          * eval:var:replace
18621          */
18622       
18623     /**
18624      * Helpers
18625      */
18626     
18627     var escape = function (html, encode) {
18628       return html
18629         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18630         .replace(/</g, '&lt;')
18631         .replace(/>/g, '&gt;')
18632         .replace(/"/g, '&quot;')
18633         .replace(/'/g, '&#39;');
18634     }
18635     
18636     var unescape = function (html) {
18637         // explicitly match decimal, hex, and named HTML entities 
18638       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18639         n = n.toLowerCase();
18640         if (n === 'colon') { return ':'; }
18641         if (n.charAt(0) === '#') {
18642           return n.charAt(1) === 'x'
18643             ? String.fromCharCode(parseInt(n.substring(2), 16))
18644             : String.fromCharCode(+n.substring(1));
18645         }
18646         return '';
18647       });
18648     }
18649     
18650     var replace = function (regex, opt) {
18651       regex = regex.source;
18652       opt = opt || '';
18653       return function self(name, val) {
18654         if (!name) { return new RegExp(regex, opt); }
18655         val = val.source || val;
18656         val = val.replace(/(^|[^\[])\^/g, '$1');
18657         regex = regex.replace(name, val);
18658         return self;
18659       };
18660     }
18661
18662
18663          /**
18664          * eval:var:noop
18665     */
18666     var noop = function () {}
18667     noop.exec = noop;
18668     
18669          /**
18670          * eval:var:merge
18671     */
18672     var merge = function (obj) {
18673       var i = 1
18674         , target
18675         , key;
18676     
18677       for (; i < arguments.length; i++) {
18678         target = arguments[i];
18679         for (key in target) {
18680           if (Object.prototype.hasOwnProperty.call(target, key)) {
18681             obj[key] = target[key];
18682           }
18683         }
18684       }
18685     
18686       return obj;
18687     }
18688     
18689     
18690     /**
18691      * Block-Level Grammar
18692      */
18693     
18694     
18695     
18696     
18697     var block = {
18698       newline: /^\n+/,
18699       code: /^( {4}[^\n]+\n*)+/,
18700       fences: noop,
18701       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18702       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18703       nptable: noop,
18704       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18705       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18706       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18707       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18708       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18709       table: noop,
18710       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18711       text: /^[^\n]+/
18712     };
18713     
18714     block.bullet = /(?:[*+-]|\d+\.)/;
18715     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18716     block.item = replace(block.item, 'gm')
18717       (/bull/g, block.bullet)
18718       ();
18719     
18720     block.list = replace(block.list)
18721       (/bull/g, block.bullet)
18722       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18723       ('def', '\\n+(?=' + block.def.source + ')')
18724       ();
18725     
18726     block.blockquote = replace(block.blockquote)
18727       ('def', block.def)
18728       ();
18729     
18730     block._tag = '(?!(?:'
18731       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18732       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18733       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18734     
18735     block.html = replace(block.html)
18736       ('comment', /<!--[\s\S]*?-->/)
18737       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18738       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18739       (/tag/g, block._tag)
18740       ();
18741     
18742     block.paragraph = replace(block.paragraph)
18743       ('hr', block.hr)
18744       ('heading', block.heading)
18745       ('lheading', block.lheading)
18746       ('blockquote', block.blockquote)
18747       ('tag', '<' + block._tag)
18748       ('def', block.def)
18749       ();
18750     
18751     /**
18752      * Normal Block Grammar
18753      */
18754     
18755     block.normal = merge({}, block);
18756     
18757     /**
18758      * GFM Block Grammar
18759      */
18760     
18761     block.gfm = merge({}, block.normal, {
18762       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18763       paragraph: /^/,
18764       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18765     });
18766     
18767     block.gfm.paragraph = replace(block.paragraph)
18768       ('(?!', '(?!'
18769         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18770         + block.list.source.replace('\\1', '\\3') + '|')
18771       ();
18772     
18773     /**
18774      * GFM + Tables Block Grammar
18775      */
18776     
18777     block.tables = merge({}, block.gfm, {
18778       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18779       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18780     });
18781     
18782     /**
18783      * Block Lexer
18784      */
18785     
18786     var Lexer = function (options) {
18787       this.tokens = [];
18788       this.tokens.links = {};
18789       this.options = options || marked.defaults;
18790       this.rules = block.normal;
18791     
18792       if (this.options.gfm) {
18793         if (this.options.tables) {
18794           this.rules = block.tables;
18795         } else {
18796           this.rules = block.gfm;
18797         }
18798       }
18799     }
18800     
18801     /**
18802      * Expose Block Rules
18803      */
18804     
18805     Lexer.rules = block;
18806     
18807     /**
18808      * Static Lex Method
18809      */
18810     
18811     Lexer.lex = function(src, options) {
18812       var lexer = new Lexer(options);
18813       return lexer.lex(src);
18814     };
18815     
18816     /**
18817      * Preprocessing
18818      */
18819     
18820     Lexer.prototype.lex = function(src) {
18821       src = src
18822         .replace(/\r\n|\r/g, '\n')
18823         .replace(/\t/g, '    ')
18824         .replace(/\u00a0/g, ' ')
18825         .replace(/\u2424/g, '\n');
18826     
18827       return this.token(src, true);
18828     };
18829     
18830     /**
18831      * Lexing
18832      */
18833     
18834     Lexer.prototype.token = function(src, top, bq) {
18835       var src = src.replace(/^ +$/gm, '')
18836         , next
18837         , loose
18838         , cap
18839         , bull
18840         , b
18841         , item
18842         , space
18843         , i
18844         , l;
18845     
18846       while (src) {
18847         // newline
18848         if (cap = this.rules.newline.exec(src)) {
18849           src = src.substring(cap[0].length);
18850           if (cap[0].length > 1) {
18851             this.tokens.push({
18852               type: 'space'
18853             });
18854           }
18855         }
18856     
18857         // code
18858         if (cap = this.rules.code.exec(src)) {
18859           src = src.substring(cap[0].length);
18860           cap = cap[0].replace(/^ {4}/gm, '');
18861           this.tokens.push({
18862             type: 'code',
18863             text: !this.options.pedantic
18864               ? cap.replace(/\n+$/, '')
18865               : cap
18866           });
18867           continue;
18868         }
18869     
18870         // fences (gfm)
18871         if (cap = this.rules.fences.exec(src)) {
18872           src = src.substring(cap[0].length);
18873           this.tokens.push({
18874             type: 'code',
18875             lang: cap[2],
18876             text: cap[3] || ''
18877           });
18878           continue;
18879         }
18880     
18881         // heading
18882         if (cap = this.rules.heading.exec(src)) {
18883           src = src.substring(cap[0].length);
18884           this.tokens.push({
18885             type: 'heading',
18886             depth: cap[1].length,
18887             text: cap[2]
18888           });
18889           continue;
18890         }
18891     
18892         // table no leading pipe (gfm)
18893         if (top && (cap = this.rules.nptable.exec(src))) {
18894           src = src.substring(cap[0].length);
18895     
18896           item = {
18897             type: 'table',
18898             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18899             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18900             cells: cap[3].replace(/\n$/, '').split('\n')
18901           };
18902     
18903           for (i = 0; i < item.align.length; i++) {
18904             if (/^ *-+: *$/.test(item.align[i])) {
18905               item.align[i] = 'right';
18906             } else if (/^ *:-+: *$/.test(item.align[i])) {
18907               item.align[i] = 'center';
18908             } else if (/^ *:-+ *$/.test(item.align[i])) {
18909               item.align[i] = 'left';
18910             } else {
18911               item.align[i] = null;
18912             }
18913           }
18914     
18915           for (i = 0; i < item.cells.length; i++) {
18916             item.cells[i] = item.cells[i].split(/ *\| */);
18917           }
18918     
18919           this.tokens.push(item);
18920     
18921           continue;
18922         }
18923     
18924         // lheading
18925         if (cap = this.rules.lheading.exec(src)) {
18926           src = src.substring(cap[0].length);
18927           this.tokens.push({
18928             type: 'heading',
18929             depth: cap[2] === '=' ? 1 : 2,
18930             text: cap[1]
18931           });
18932           continue;
18933         }
18934     
18935         // hr
18936         if (cap = this.rules.hr.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           this.tokens.push({
18939             type: 'hr'
18940           });
18941           continue;
18942         }
18943     
18944         // blockquote
18945         if (cap = this.rules.blockquote.exec(src)) {
18946           src = src.substring(cap[0].length);
18947     
18948           this.tokens.push({
18949             type: 'blockquote_start'
18950           });
18951     
18952           cap = cap[0].replace(/^ *> ?/gm, '');
18953     
18954           // Pass `top` to keep the current
18955           // "toplevel" state. This is exactly
18956           // how markdown.pl works.
18957           this.token(cap, top, true);
18958     
18959           this.tokens.push({
18960             type: 'blockquote_end'
18961           });
18962     
18963           continue;
18964         }
18965     
18966         // list
18967         if (cap = this.rules.list.exec(src)) {
18968           src = src.substring(cap[0].length);
18969           bull = cap[2];
18970     
18971           this.tokens.push({
18972             type: 'list_start',
18973             ordered: bull.length > 1
18974           });
18975     
18976           // Get each top-level item.
18977           cap = cap[0].match(this.rules.item);
18978     
18979           next = false;
18980           l = cap.length;
18981           i = 0;
18982     
18983           for (; i < l; i++) {
18984             item = cap[i];
18985     
18986             // Remove the list item's bullet
18987             // so it is seen as the next token.
18988             space = item.length;
18989             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18990     
18991             // Outdent whatever the
18992             // list item contains. Hacky.
18993             if (~item.indexOf('\n ')) {
18994               space -= item.length;
18995               item = !this.options.pedantic
18996                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18997                 : item.replace(/^ {1,4}/gm, '');
18998             }
18999     
19000             // Determine whether the next list item belongs here.
19001             // Backpedal if it does not belong in this list.
19002             if (this.options.smartLists && i !== l - 1) {
19003               b = block.bullet.exec(cap[i + 1])[0];
19004               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19005                 src = cap.slice(i + 1).join('\n') + src;
19006                 i = l - 1;
19007               }
19008             }
19009     
19010             // Determine whether item is loose or not.
19011             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19012             // for discount behavior.
19013             loose = next || /\n\n(?!\s*$)/.test(item);
19014             if (i !== l - 1) {
19015               next = item.charAt(item.length - 1) === '\n';
19016               if (!loose) { loose = next; }
19017             }
19018     
19019             this.tokens.push({
19020               type: loose
19021                 ? 'loose_item_start'
19022                 : 'list_item_start'
19023             });
19024     
19025             // Recurse.
19026             this.token(item, false, bq);
19027     
19028             this.tokens.push({
19029               type: 'list_item_end'
19030             });
19031           }
19032     
19033           this.tokens.push({
19034             type: 'list_end'
19035           });
19036     
19037           continue;
19038         }
19039     
19040         // html
19041         if (cap = this.rules.html.exec(src)) {
19042           src = src.substring(cap[0].length);
19043           this.tokens.push({
19044             type: this.options.sanitize
19045               ? 'paragraph'
19046               : 'html',
19047             pre: !this.options.sanitizer
19048               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19049             text: cap[0]
19050           });
19051           continue;
19052         }
19053     
19054         // def
19055         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19056           src = src.substring(cap[0].length);
19057           this.tokens.links[cap[1].toLowerCase()] = {
19058             href: cap[2],
19059             title: cap[3]
19060           };
19061           continue;
19062         }
19063     
19064         // table (gfm)
19065         if (top && (cap = this.rules.table.exec(src))) {
19066           src = src.substring(cap[0].length);
19067     
19068           item = {
19069             type: 'table',
19070             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19071             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19072             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19073           };
19074     
19075           for (i = 0; i < item.align.length; i++) {
19076             if (/^ *-+: *$/.test(item.align[i])) {
19077               item.align[i] = 'right';
19078             } else if (/^ *:-+: *$/.test(item.align[i])) {
19079               item.align[i] = 'center';
19080             } else if (/^ *:-+ *$/.test(item.align[i])) {
19081               item.align[i] = 'left';
19082             } else {
19083               item.align[i] = null;
19084             }
19085           }
19086     
19087           for (i = 0; i < item.cells.length; i++) {
19088             item.cells[i] = item.cells[i]
19089               .replace(/^ *\| *| *\| *$/g, '')
19090               .split(/ *\| */);
19091           }
19092     
19093           this.tokens.push(item);
19094     
19095           continue;
19096         }
19097     
19098         // top-level paragraph
19099         if (top && (cap = this.rules.paragraph.exec(src))) {
19100           src = src.substring(cap[0].length);
19101           this.tokens.push({
19102             type: 'paragraph',
19103             text: cap[1].charAt(cap[1].length - 1) === '\n'
19104               ? cap[1].slice(0, -1)
19105               : cap[1]
19106           });
19107           continue;
19108         }
19109     
19110         // text
19111         if (cap = this.rules.text.exec(src)) {
19112           // Top-level should never reach here.
19113           src = src.substring(cap[0].length);
19114           this.tokens.push({
19115             type: 'text',
19116             text: cap[0]
19117           });
19118           continue;
19119         }
19120     
19121         if (src) {
19122           throw new
19123             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19124         }
19125       }
19126     
19127       return this.tokens;
19128     };
19129     
19130     /**
19131      * Inline-Level Grammar
19132      */
19133     
19134     var inline = {
19135       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19136       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19137       url: noop,
19138       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19139       link: /^!?\[(inside)\]\(href\)/,
19140       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19141       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19142       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19143       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19144       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19145       br: /^ {2,}\n(?!\s*$)/,
19146       del: noop,
19147       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19148     };
19149     
19150     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19151     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19152     
19153     inline.link = replace(inline.link)
19154       ('inside', inline._inside)
19155       ('href', inline._href)
19156       ();
19157     
19158     inline.reflink = replace(inline.reflink)
19159       ('inside', inline._inside)
19160       ();
19161     
19162     /**
19163      * Normal Inline Grammar
19164      */
19165     
19166     inline.normal = merge({}, inline);
19167     
19168     /**
19169      * Pedantic Inline Grammar
19170      */
19171     
19172     inline.pedantic = merge({}, inline.normal, {
19173       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19174       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19175     });
19176     
19177     /**
19178      * GFM Inline Grammar
19179      */
19180     
19181     inline.gfm = merge({}, inline.normal, {
19182       escape: replace(inline.escape)('])', '~|])')(),
19183       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19184       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19185       text: replace(inline.text)
19186         (']|', '~]|')
19187         ('|', '|https?://|')
19188         ()
19189     });
19190     
19191     /**
19192      * GFM + Line Breaks Inline Grammar
19193      */
19194     
19195     inline.breaks = merge({}, inline.gfm, {
19196       br: replace(inline.br)('{2,}', '*')(),
19197       text: replace(inline.gfm.text)('{2,}', '*')()
19198     });
19199     
19200     /**
19201      * Inline Lexer & Compiler
19202      */
19203     
19204     var InlineLexer  = function (links, options) {
19205       this.options = options || marked.defaults;
19206       this.links = links;
19207       this.rules = inline.normal;
19208       this.renderer = this.options.renderer || new Renderer;
19209       this.renderer.options = this.options;
19210     
19211       if (!this.links) {
19212         throw new
19213           Error('Tokens array requires a `links` property.');
19214       }
19215     
19216       if (this.options.gfm) {
19217         if (this.options.breaks) {
19218           this.rules = inline.breaks;
19219         } else {
19220           this.rules = inline.gfm;
19221         }
19222       } else if (this.options.pedantic) {
19223         this.rules = inline.pedantic;
19224       }
19225     }
19226     
19227     /**
19228      * Expose Inline Rules
19229      */
19230     
19231     InlineLexer.rules = inline;
19232     
19233     /**
19234      * Static Lexing/Compiling Method
19235      */
19236     
19237     InlineLexer.output = function(src, links, options) {
19238       var inline = new InlineLexer(links, options);
19239       return inline.output(src);
19240     };
19241     
19242     /**
19243      * Lexing/Compiling
19244      */
19245     
19246     InlineLexer.prototype.output = function(src) {
19247       var out = ''
19248         , link
19249         , text
19250         , href
19251         , cap;
19252     
19253       while (src) {
19254         // escape
19255         if (cap = this.rules.escape.exec(src)) {
19256           src = src.substring(cap[0].length);
19257           out += cap[1];
19258           continue;
19259         }
19260     
19261         // autolink
19262         if (cap = this.rules.autolink.exec(src)) {
19263           src = src.substring(cap[0].length);
19264           if (cap[2] === '@') {
19265             text = cap[1].charAt(6) === ':'
19266               ? this.mangle(cap[1].substring(7))
19267               : this.mangle(cap[1]);
19268             href = this.mangle('mailto:') + text;
19269           } else {
19270             text = escape(cap[1]);
19271             href = text;
19272           }
19273           out += this.renderer.link(href, null, text);
19274           continue;
19275         }
19276     
19277         // url (gfm)
19278         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19279           src = src.substring(cap[0].length);
19280           text = escape(cap[1]);
19281           href = text;
19282           out += this.renderer.link(href, null, text);
19283           continue;
19284         }
19285     
19286         // tag
19287         if (cap = this.rules.tag.exec(src)) {
19288           if (!this.inLink && /^<a /i.test(cap[0])) {
19289             this.inLink = true;
19290           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19291             this.inLink = false;
19292           }
19293           src = src.substring(cap[0].length);
19294           out += this.options.sanitize
19295             ? this.options.sanitizer
19296               ? this.options.sanitizer(cap[0])
19297               : escape(cap[0])
19298             : cap[0];
19299           continue;
19300         }
19301     
19302         // link
19303         if (cap = this.rules.link.exec(src)) {
19304           src = src.substring(cap[0].length);
19305           this.inLink = true;
19306           out += this.outputLink(cap, {
19307             href: cap[2],
19308             title: cap[3]
19309           });
19310           this.inLink = false;
19311           continue;
19312         }
19313     
19314         // reflink, nolink
19315         if ((cap = this.rules.reflink.exec(src))
19316             || (cap = this.rules.nolink.exec(src))) {
19317           src = src.substring(cap[0].length);
19318           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19319           link = this.links[link.toLowerCase()];
19320           if (!link || !link.href) {
19321             out += cap[0].charAt(0);
19322             src = cap[0].substring(1) + src;
19323             continue;
19324           }
19325           this.inLink = true;
19326           out += this.outputLink(cap, link);
19327           this.inLink = false;
19328           continue;
19329         }
19330     
19331         // strong
19332         if (cap = this.rules.strong.exec(src)) {
19333           src = src.substring(cap[0].length);
19334           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19335           continue;
19336         }
19337     
19338         // em
19339         if (cap = this.rules.em.exec(src)) {
19340           src = src.substring(cap[0].length);
19341           out += this.renderer.em(this.output(cap[2] || cap[1]));
19342           continue;
19343         }
19344     
19345         // code
19346         if (cap = this.rules.code.exec(src)) {
19347           src = src.substring(cap[0].length);
19348           out += this.renderer.codespan(escape(cap[2], true));
19349           continue;
19350         }
19351     
19352         // br
19353         if (cap = this.rules.br.exec(src)) {
19354           src = src.substring(cap[0].length);
19355           out += this.renderer.br();
19356           continue;
19357         }
19358     
19359         // del (gfm)
19360         if (cap = this.rules.del.exec(src)) {
19361           src = src.substring(cap[0].length);
19362           out += this.renderer.del(this.output(cap[1]));
19363           continue;
19364         }
19365     
19366         // text
19367         if (cap = this.rules.text.exec(src)) {
19368           src = src.substring(cap[0].length);
19369           out += this.renderer.text(escape(this.smartypants(cap[0])));
19370           continue;
19371         }
19372     
19373         if (src) {
19374           throw new
19375             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19376         }
19377       }
19378     
19379       return out;
19380     };
19381     
19382     /**
19383      * Compile Link
19384      */
19385     
19386     InlineLexer.prototype.outputLink = function(cap, link) {
19387       var href = escape(link.href)
19388         , title = link.title ? escape(link.title) : null;
19389     
19390       return cap[0].charAt(0) !== '!'
19391         ? this.renderer.link(href, title, this.output(cap[1]))
19392         : this.renderer.image(href, title, escape(cap[1]));
19393     };
19394     
19395     /**
19396      * Smartypants Transformations
19397      */
19398     
19399     InlineLexer.prototype.smartypants = function(text) {
19400       if (!this.options.smartypants)  { return text; }
19401       return text
19402         // em-dashes
19403         .replace(/---/g, '\u2014')
19404         // en-dashes
19405         .replace(/--/g, '\u2013')
19406         // opening singles
19407         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19408         // closing singles & apostrophes
19409         .replace(/'/g, '\u2019')
19410         // opening doubles
19411         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19412         // closing doubles
19413         .replace(/"/g, '\u201d')
19414         // ellipses
19415         .replace(/\.{3}/g, '\u2026');
19416     };
19417     
19418     /**
19419      * Mangle Links
19420      */
19421     
19422     InlineLexer.prototype.mangle = function(text) {
19423       if (!this.options.mangle) { return text; }
19424       var out = ''
19425         , l = text.length
19426         , i = 0
19427         , ch;
19428     
19429       for (; i < l; i++) {
19430         ch = text.charCodeAt(i);
19431         if (Math.random() > 0.5) {
19432           ch = 'x' + ch.toString(16);
19433         }
19434         out += '&#' + ch + ';';
19435       }
19436     
19437       return out;
19438     };
19439     
19440     /**
19441      * Renderer
19442      */
19443     
19444      /**
19445          * eval:var:Renderer
19446     */
19447     
19448     var Renderer   = function (options) {
19449       this.options = options || {};
19450     }
19451     
19452     Renderer.prototype.code = function(code, lang, escaped) {
19453       if (this.options.highlight) {
19454         var out = this.options.highlight(code, lang);
19455         if (out != null && out !== code) {
19456           escaped = true;
19457           code = out;
19458         }
19459       } else {
19460             // hack!!! - it's already escapeD?
19461             escaped = true;
19462       }
19463     
19464       if (!lang) {
19465         return '<pre><code>'
19466           + (escaped ? code : escape(code, true))
19467           + '\n</code></pre>';
19468       }
19469     
19470       return '<pre><code class="'
19471         + this.options.langPrefix
19472         + escape(lang, true)
19473         + '">'
19474         + (escaped ? code : escape(code, true))
19475         + '\n</code></pre>\n';
19476     };
19477     
19478     Renderer.prototype.blockquote = function(quote) {
19479       return '<blockquote>\n' + quote + '</blockquote>\n';
19480     };
19481     
19482     Renderer.prototype.html = function(html) {
19483       return html;
19484     };
19485     
19486     Renderer.prototype.heading = function(text, level, raw) {
19487       return '<h'
19488         + level
19489         + ' id="'
19490         + this.options.headerPrefix
19491         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19492         + '">'
19493         + text
19494         + '</h'
19495         + level
19496         + '>\n';
19497     };
19498     
19499     Renderer.prototype.hr = function() {
19500       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19501     };
19502     
19503     Renderer.prototype.list = function(body, ordered) {
19504       var type = ordered ? 'ol' : 'ul';
19505       return '<' + type + '>\n' + body + '</' + type + '>\n';
19506     };
19507     
19508     Renderer.prototype.listitem = function(text) {
19509       return '<li>' + text + '</li>\n';
19510     };
19511     
19512     Renderer.prototype.paragraph = function(text) {
19513       return '<p>' + text + '</p>\n';
19514     };
19515     
19516     Renderer.prototype.table = function(header, body) {
19517       return '<table class="table table-striped">\n'
19518         + '<thead>\n'
19519         + header
19520         + '</thead>\n'
19521         + '<tbody>\n'
19522         + body
19523         + '</tbody>\n'
19524         + '</table>\n';
19525     };
19526     
19527     Renderer.prototype.tablerow = function(content) {
19528       return '<tr>\n' + content + '</tr>\n';
19529     };
19530     
19531     Renderer.prototype.tablecell = function(content, flags) {
19532       var type = flags.header ? 'th' : 'td';
19533       var tag = flags.align
19534         ? '<' + type + ' style="text-align:' + flags.align + '">'
19535         : '<' + type + '>';
19536       return tag + content + '</' + type + '>\n';
19537     };
19538     
19539     // span level renderer
19540     Renderer.prototype.strong = function(text) {
19541       return '<strong>' + text + '</strong>';
19542     };
19543     
19544     Renderer.prototype.em = function(text) {
19545       return '<em>' + text + '</em>';
19546     };
19547     
19548     Renderer.prototype.codespan = function(text) {
19549       return '<code>' + text + '</code>';
19550     };
19551     
19552     Renderer.prototype.br = function() {
19553       return this.options.xhtml ? '<br/>' : '<br>';
19554     };
19555     
19556     Renderer.prototype.del = function(text) {
19557       return '<del>' + text + '</del>';
19558     };
19559     
19560     Renderer.prototype.link = function(href, title, text) {
19561       if (this.options.sanitize) {
19562         try {
19563           var prot = decodeURIComponent(unescape(href))
19564             .replace(/[^\w:]/g, '')
19565             .toLowerCase();
19566         } catch (e) {
19567           return '';
19568         }
19569         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19570           return '';
19571         }
19572       }
19573       var out = '<a href="' + href + '"';
19574       if (title) {
19575         out += ' title="' + title + '"';
19576       }
19577       out += '>' + text + '</a>';
19578       return out;
19579     };
19580     
19581     Renderer.prototype.image = function(href, title, text) {
19582       var out = '<img src="' + href + '" alt="' + text + '"';
19583       if (title) {
19584         out += ' title="' + title + '"';
19585       }
19586       out += this.options.xhtml ? '/>' : '>';
19587       return out;
19588     };
19589     
19590     Renderer.prototype.text = function(text) {
19591       return text;
19592     };
19593     
19594     /**
19595      * Parsing & Compiling
19596      */
19597          /**
19598          * eval:var:Parser
19599     */
19600     
19601     var Parser= function (options) {
19602       this.tokens = [];
19603       this.token = null;
19604       this.options = options || marked.defaults;
19605       this.options.renderer = this.options.renderer || new Renderer;
19606       this.renderer = this.options.renderer;
19607       this.renderer.options = this.options;
19608     }
19609     
19610     /**
19611      * Static Parse Method
19612      */
19613     
19614     Parser.parse = function(src, options, renderer) {
19615       var parser = new Parser(options, renderer);
19616       return parser.parse(src);
19617     };
19618     
19619     /**
19620      * Parse Loop
19621      */
19622     
19623     Parser.prototype.parse = function(src) {
19624       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19625       this.tokens = src.reverse();
19626     
19627       var out = '';
19628       while (this.next()) {
19629         out += this.tok();
19630       }
19631     
19632       return out;
19633     };
19634     
19635     /**
19636      * Next Token
19637      */
19638     
19639     Parser.prototype.next = function() {
19640       return this.token = this.tokens.pop();
19641     };
19642     
19643     /**
19644      * Preview Next Token
19645      */
19646     
19647     Parser.prototype.peek = function() {
19648       return this.tokens[this.tokens.length - 1] || 0;
19649     };
19650     
19651     /**
19652      * Parse Text Tokens
19653      */
19654     
19655     Parser.prototype.parseText = function() {
19656       var body = this.token.text;
19657     
19658       while (this.peek().type === 'text') {
19659         body += '\n' + this.next().text;
19660       }
19661     
19662       return this.inline.output(body);
19663     };
19664     
19665     /**
19666      * Parse Current Token
19667      */
19668     
19669     Parser.prototype.tok = function() {
19670       switch (this.token.type) {
19671         case 'space': {
19672           return '';
19673         }
19674         case 'hr': {
19675           return this.renderer.hr();
19676         }
19677         case 'heading': {
19678           return this.renderer.heading(
19679             this.inline.output(this.token.text),
19680             this.token.depth,
19681             this.token.text);
19682         }
19683         case 'code': {
19684           return this.renderer.code(this.token.text,
19685             this.token.lang,
19686             this.token.escaped);
19687         }
19688         case 'table': {
19689           var header = ''
19690             , body = ''
19691             , i
19692             , row
19693             , cell
19694             , flags
19695             , j;
19696     
19697           // header
19698           cell = '';
19699           for (i = 0; i < this.token.header.length; i++) {
19700             flags = { header: true, align: this.token.align[i] };
19701             cell += this.renderer.tablecell(
19702               this.inline.output(this.token.header[i]),
19703               { header: true, align: this.token.align[i] }
19704             );
19705           }
19706           header += this.renderer.tablerow(cell);
19707     
19708           for (i = 0; i < this.token.cells.length; i++) {
19709             row = this.token.cells[i];
19710     
19711             cell = '';
19712             for (j = 0; j < row.length; j++) {
19713               cell += this.renderer.tablecell(
19714                 this.inline.output(row[j]),
19715                 { header: false, align: this.token.align[j] }
19716               );
19717             }
19718     
19719             body += this.renderer.tablerow(cell);
19720           }
19721           return this.renderer.table(header, body);
19722         }
19723         case 'blockquote_start': {
19724           var body = '';
19725     
19726           while (this.next().type !== 'blockquote_end') {
19727             body += this.tok();
19728           }
19729     
19730           return this.renderer.blockquote(body);
19731         }
19732         case 'list_start': {
19733           var body = ''
19734             , ordered = this.token.ordered;
19735     
19736           while (this.next().type !== 'list_end') {
19737             body += this.tok();
19738           }
19739     
19740           return this.renderer.list(body, ordered);
19741         }
19742         case 'list_item_start': {
19743           var body = '';
19744     
19745           while (this.next().type !== 'list_item_end') {
19746             body += this.token.type === 'text'
19747               ? this.parseText()
19748               : this.tok();
19749           }
19750     
19751           return this.renderer.listitem(body);
19752         }
19753         case 'loose_item_start': {
19754           var body = '';
19755     
19756           while (this.next().type !== 'list_item_end') {
19757             body += this.tok();
19758           }
19759     
19760           return this.renderer.listitem(body);
19761         }
19762         case 'html': {
19763           var html = !this.token.pre && !this.options.pedantic
19764             ? this.inline.output(this.token.text)
19765             : this.token.text;
19766           return this.renderer.html(html);
19767         }
19768         case 'paragraph': {
19769           return this.renderer.paragraph(this.inline.output(this.token.text));
19770         }
19771         case 'text': {
19772           return this.renderer.paragraph(this.parseText());
19773         }
19774       }
19775     };
19776   
19777     
19778     /**
19779      * Marked
19780      */
19781          /**
19782          * eval:var:marked
19783     */
19784     var marked = function (src, opt, callback) {
19785       if (callback || typeof opt === 'function') {
19786         if (!callback) {
19787           callback = opt;
19788           opt = null;
19789         }
19790     
19791         opt = merge({}, marked.defaults, opt || {});
19792     
19793         var highlight = opt.highlight
19794           , tokens
19795           , pending
19796           , i = 0;
19797     
19798         try {
19799           tokens = Lexer.lex(src, opt)
19800         } catch (e) {
19801           return callback(e);
19802         }
19803     
19804         pending = tokens.length;
19805          /**
19806          * eval:var:done
19807     */
19808         var done = function(err) {
19809           if (err) {
19810             opt.highlight = highlight;
19811             return callback(err);
19812           }
19813     
19814           var out;
19815     
19816           try {
19817             out = Parser.parse(tokens, opt);
19818           } catch (e) {
19819             err = e;
19820           }
19821     
19822           opt.highlight = highlight;
19823     
19824           return err
19825             ? callback(err)
19826             : callback(null, out);
19827         };
19828     
19829         if (!highlight || highlight.length < 3) {
19830           return done();
19831         }
19832     
19833         delete opt.highlight;
19834     
19835         if (!pending) { return done(); }
19836     
19837         for (; i < tokens.length; i++) {
19838           (function(token) {
19839             if (token.type !== 'code') {
19840               return --pending || done();
19841             }
19842             return highlight(token.text, token.lang, function(err, code) {
19843               if (err) { return done(err); }
19844               if (code == null || code === token.text) {
19845                 return --pending || done();
19846               }
19847               token.text = code;
19848               token.escaped = true;
19849               --pending || done();
19850             });
19851           })(tokens[i]);
19852         }
19853     
19854         return;
19855       }
19856       try {
19857         if (opt) { opt = merge({}, marked.defaults, opt); }
19858         return Parser.parse(Lexer.lex(src, opt), opt);
19859       } catch (e) {
19860         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19861         if ((opt || marked.defaults).silent) {
19862           return '<p>An error occured:</p><pre>'
19863             + escape(e.message + '', true)
19864             + '</pre>';
19865         }
19866         throw e;
19867       }
19868     }
19869     
19870     /**
19871      * Options
19872      */
19873     
19874     marked.options =
19875     marked.setOptions = function(opt) {
19876       merge(marked.defaults, opt);
19877       return marked;
19878     };
19879     
19880     marked.defaults = {
19881       gfm: true,
19882       tables: true,
19883       breaks: false,
19884       pedantic: false,
19885       sanitize: false,
19886       sanitizer: null,
19887       mangle: true,
19888       smartLists: false,
19889       silent: false,
19890       highlight: null,
19891       langPrefix: 'lang-',
19892       smartypants: false,
19893       headerPrefix: '',
19894       renderer: new Renderer,
19895       xhtml: false
19896     };
19897     
19898     /**
19899      * Expose
19900      */
19901     
19902     marked.Parser = Parser;
19903     marked.parser = Parser.parse;
19904     
19905     marked.Renderer = Renderer;
19906     
19907     marked.Lexer = Lexer;
19908     marked.lexer = Lexer.lex;
19909     
19910     marked.InlineLexer = InlineLexer;
19911     marked.inlineLexer = InlineLexer.output;
19912     
19913     marked.parse = marked;
19914     
19915     Roo.Markdown.marked = marked;
19916
19917 })();/*
19918  * Based on:
19919  * Ext JS Library 1.1.1
19920  * Copyright(c) 2006-2007, Ext JS, LLC.
19921  *
19922  * Originally Released Under LGPL - original licence link has changed is not relivant.
19923  *
19924  * Fork - LGPL
19925  * <script type="text/javascript">
19926  */
19927
19928
19929
19930 /*
19931  * These classes are derivatives of the similarly named classes in the YUI Library.
19932  * The original license:
19933  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19934  * Code licensed under the BSD License:
19935  * http://developer.yahoo.net/yui/license.txt
19936  */
19937
19938 (function() {
19939
19940 var Event=Roo.EventManager;
19941 var Dom=Roo.lib.Dom;
19942
19943 /**
19944  * @class Roo.dd.DragDrop
19945  * @extends Roo.util.Observable
19946  * Defines the interface and base operation of items that that can be
19947  * dragged or can be drop targets.  It was designed to be extended, overriding
19948  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19949  * Up to three html elements can be associated with a DragDrop instance:
19950  * <ul>
19951  * <li>linked element: the element that is passed into the constructor.
19952  * This is the element which defines the boundaries for interaction with
19953  * other DragDrop objects.</li>
19954  * <li>handle element(s): The drag operation only occurs if the element that
19955  * was clicked matches a handle element.  By default this is the linked
19956  * element, but there are times that you will want only a portion of the
19957  * linked element to initiate the drag operation, and the setHandleElId()
19958  * method provides a way to define this.</li>
19959  * <li>drag element: this represents the element that would be moved along
19960  * with the cursor during a drag operation.  By default, this is the linked
19961  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19962  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19963  * </li>
19964  * </ul>
19965  * This class should not be instantiated until the onload event to ensure that
19966  * the associated elements are available.
19967  * The following would define a DragDrop obj that would interact with any
19968  * other DragDrop obj in the "group1" group:
19969  * <pre>
19970  *  dd = new Roo.dd.DragDrop("div1", "group1");
19971  * </pre>
19972  * Since none of the event handlers have been implemented, nothing would
19973  * actually happen if you were to run the code above.  Normally you would
19974  * override this class or one of the default implementations, but you can
19975  * also override the methods you want on an instance of the class...
19976  * <pre>
19977  *  dd.onDragDrop = function(e, id) {
19978  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19979  *  }
19980  * </pre>
19981  * @constructor
19982  * @param {String} id of the element that is linked to this instance
19983  * @param {String} sGroup the group of related DragDrop objects
19984  * @param {object} config an object containing configurable attributes
19985  *                Valid properties for DragDrop:
19986  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19987  */
19988 Roo.dd.DragDrop = function(id, sGroup, config) {
19989     if (id) {
19990         this.init(id, sGroup, config);
19991     }
19992     
19993 };
19994
19995 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19996
19997     /**
19998      * The id of the element associated with this object.  This is what we
19999      * refer to as the "linked element" because the size and position of
20000      * this element is used to determine when the drag and drop objects have
20001      * interacted.
20002      * @property id
20003      * @type String
20004      */
20005     id: null,
20006
20007     /**
20008      * Configuration attributes passed into the constructor
20009      * @property config
20010      * @type object
20011      */
20012     config: null,
20013
20014     /**
20015      * The id of the element that will be dragged.  By default this is same
20016      * as the linked element , but could be changed to another element. Ex:
20017      * Roo.dd.DDProxy
20018      * @property dragElId
20019      * @type String
20020      * @private
20021      */
20022     dragElId: null,
20023
20024     /**
20025      * the id of the element that initiates the drag operation.  By default
20026      * this is the linked element, but could be changed to be a child of this
20027      * element.  This lets us do things like only starting the drag when the
20028      * header element within the linked html element is clicked.
20029      * @property handleElId
20030      * @type String
20031      * @private
20032      */
20033     handleElId: null,
20034
20035     /**
20036      * An associative array of HTML tags that will be ignored if clicked.
20037      * @property invalidHandleTypes
20038      * @type {string: string}
20039      */
20040     invalidHandleTypes: null,
20041
20042     /**
20043      * An associative array of ids for elements that will be ignored if clicked
20044      * @property invalidHandleIds
20045      * @type {string: string}
20046      */
20047     invalidHandleIds: null,
20048
20049     /**
20050      * An indexted array of css class names for elements that will be ignored
20051      * if clicked.
20052      * @property invalidHandleClasses
20053      * @type string[]
20054      */
20055     invalidHandleClasses: null,
20056
20057     /**
20058      * The linked element's absolute X position at the time the drag was
20059      * started
20060      * @property startPageX
20061      * @type int
20062      * @private
20063      */
20064     startPageX: 0,
20065
20066     /**
20067      * The linked element's absolute X position at the time the drag was
20068      * started
20069      * @property startPageY
20070      * @type int
20071      * @private
20072      */
20073     startPageY: 0,
20074
20075     /**
20076      * The group defines a logical collection of DragDrop objects that are
20077      * related.  Instances only get events when interacting with other
20078      * DragDrop object in the same group.  This lets us define multiple
20079      * groups using a single DragDrop subclass if we want.
20080      * @property groups
20081      * @type {string: string}
20082      */
20083     groups: null,
20084
20085     /**
20086      * Individual drag/drop instances can be locked.  This will prevent
20087      * onmousedown start drag.
20088      * @property locked
20089      * @type boolean
20090      * @private
20091      */
20092     locked: false,
20093
20094     /**
20095      * Lock this instance
20096      * @method lock
20097      */
20098     lock: function() { this.locked = true; },
20099
20100     /**
20101      * Unlock this instace
20102      * @method unlock
20103      */
20104     unlock: function() { this.locked = false; },
20105
20106     /**
20107      * By default, all insances can be a drop target.  This can be disabled by
20108      * setting isTarget to false.
20109      * @method isTarget
20110      * @type boolean
20111      */
20112     isTarget: true,
20113
20114     /**
20115      * The padding configured for this drag and drop object for calculating
20116      * the drop zone intersection with this object.
20117      * @method padding
20118      * @type int[]
20119      */
20120     padding: null,
20121
20122     /**
20123      * Cached reference to the linked element
20124      * @property _domRef
20125      * @private
20126      */
20127     _domRef: null,
20128
20129     /**
20130      * Internal typeof flag
20131      * @property __ygDragDrop
20132      * @private
20133      */
20134     __ygDragDrop: true,
20135
20136     /**
20137      * Set to true when horizontal contraints are applied
20138      * @property constrainX
20139      * @type boolean
20140      * @private
20141      */
20142     constrainX: false,
20143
20144     /**
20145      * Set to true when vertical contraints are applied
20146      * @property constrainY
20147      * @type boolean
20148      * @private
20149      */
20150     constrainY: false,
20151
20152     /**
20153      * The left constraint
20154      * @property minX
20155      * @type int
20156      * @private
20157      */
20158     minX: 0,
20159
20160     /**
20161      * The right constraint
20162      * @property maxX
20163      * @type int
20164      * @private
20165      */
20166     maxX: 0,
20167
20168     /**
20169      * The up constraint
20170      * @property minY
20171      * @type int
20172      * @type int
20173      * @private
20174      */
20175     minY: 0,
20176
20177     /**
20178      * The down constraint
20179      * @property maxY
20180      * @type int
20181      * @private
20182      */
20183     maxY: 0,
20184
20185     /**
20186      * Maintain offsets when we resetconstraints.  Set to true when you want
20187      * the position of the element relative to its parent to stay the same
20188      * when the page changes
20189      *
20190      * @property maintainOffset
20191      * @type boolean
20192      */
20193     maintainOffset: false,
20194
20195     /**
20196      * Array of pixel locations the element will snap to if we specified a
20197      * horizontal graduation/interval.  This array is generated automatically
20198      * when you define a tick interval.
20199      * @property xTicks
20200      * @type int[]
20201      */
20202     xTicks: null,
20203
20204     /**
20205      * Array of pixel locations the element will snap to if we specified a
20206      * vertical graduation/interval.  This array is generated automatically
20207      * when you define a tick interval.
20208      * @property yTicks
20209      * @type int[]
20210      */
20211     yTicks: null,
20212
20213     /**
20214      * By default the drag and drop instance will only respond to the primary
20215      * button click (left button for a right-handed mouse).  Set to true to
20216      * allow drag and drop to start with any mouse click that is propogated
20217      * by the browser
20218      * @property primaryButtonOnly
20219      * @type boolean
20220      */
20221     primaryButtonOnly: true,
20222
20223     /**
20224      * The availabe property is false until the linked dom element is accessible.
20225      * @property available
20226      * @type boolean
20227      */
20228     available: false,
20229
20230     /**
20231      * By default, drags can only be initiated if the mousedown occurs in the
20232      * region the linked element is.  This is done in part to work around a
20233      * bug in some browsers that mis-report the mousedown if the previous
20234      * mouseup happened outside of the window.  This property is set to true
20235      * if outer handles are defined.
20236      *
20237      * @property hasOuterHandles
20238      * @type boolean
20239      * @default false
20240      */
20241     hasOuterHandles: false,
20242
20243     /**
20244      * Code that executes immediately before the startDrag event
20245      * @method b4StartDrag
20246      * @private
20247      */
20248     b4StartDrag: function(x, y) { },
20249
20250     /**
20251      * Abstract method called after a drag/drop object is clicked
20252      * and the drag or mousedown time thresholds have beeen met.
20253      * @method startDrag
20254      * @param {int} X click location
20255      * @param {int} Y click location
20256      */
20257     startDrag: function(x, y) { /* override this */ },
20258
20259     /**
20260      * Code that executes immediately before the onDrag event
20261      * @method b4Drag
20262      * @private
20263      */
20264     b4Drag: function(e) { },
20265
20266     /**
20267      * Abstract method called during the onMouseMove event while dragging an
20268      * object.
20269      * @method onDrag
20270      * @param {Event} e the mousemove event
20271      */
20272     onDrag: function(e) { /* override this */ },
20273
20274     /**
20275      * Abstract method called when this element fist begins hovering over
20276      * another DragDrop obj
20277      * @method onDragEnter
20278      * @param {Event} e the mousemove event
20279      * @param {String|DragDrop[]} id In POINT mode, the element
20280      * id this is hovering over.  In INTERSECT mode, an array of one or more
20281      * dragdrop items being hovered over.
20282      */
20283     onDragEnter: function(e, id) { /* override this */ },
20284
20285     /**
20286      * Code that executes immediately before the onDragOver event
20287      * @method b4DragOver
20288      * @private
20289      */
20290     b4DragOver: function(e) { },
20291
20292     /**
20293      * Abstract method called when this element is hovering over another
20294      * DragDrop obj
20295      * @method onDragOver
20296      * @param {Event} e the mousemove event
20297      * @param {String|DragDrop[]} id In POINT mode, the element
20298      * id this is hovering over.  In INTERSECT mode, an array of dd items
20299      * being hovered over.
20300      */
20301     onDragOver: function(e, id) { /* override this */ },
20302
20303     /**
20304      * Code that executes immediately before the onDragOut event
20305      * @method b4DragOut
20306      * @private
20307      */
20308     b4DragOut: function(e) { },
20309
20310     /**
20311      * Abstract method called when we are no longer hovering over an element
20312      * @method onDragOut
20313      * @param {Event} e the mousemove event
20314      * @param {String|DragDrop[]} id In POINT mode, the element
20315      * id this was hovering over.  In INTERSECT mode, an array of dd items
20316      * that the mouse is no longer over.
20317      */
20318     onDragOut: function(e, id) { /* override this */ },
20319
20320     /**
20321      * Code that executes immediately before the onDragDrop event
20322      * @method b4DragDrop
20323      * @private
20324      */
20325     b4DragDrop: function(e) { },
20326
20327     /**
20328      * Abstract method called when this item is dropped on another DragDrop
20329      * obj
20330      * @method onDragDrop
20331      * @param {Event} e the mouseup event
20332      * @param {String|DragDrop[]} id In POINT mode, the element
20333      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20334      * was dropped on.
20335      */
20336     onDragDrop: function(e, id) { /* override this */ },
20337
20338     /**
20339      * Abstract method called when this item is dropped on an area with no
20340      * drop target
20341      * @method onInvalidDrop
20342      * @param {Event} e the mouseup event
20343      */
20344     onInvalidDrop: function(e) { /* override this */ },
20345
20346     /**
20347      * Code that executes immediately before the endDrag event
20348      * @method b4EndDrag
20349      * @private
20350      */
20351     b4EndDrag: function(e) { },
20352
20353     /**
20354      * Fired when we are done dragging the object
20355      * @method endDrag
20356      * @param {Event} e the mouseup event
20357      */
20358     endDrag: function(e) { /* override this */ },
20359
20360     /**
20361      * Code executed immediately before the onMouseDown event
20362      * @method b4MouseDown
20363      * @param {Event} e the mousedown event
20364      * @private
20365      */
20366     b4MouseDown: function(e) {  },
20367
20368     /**
20369      * Event handler that fires when a drag/drop obj gets a mousedown
20370      * @method onMouseDown
20371      * @param {Event} e the mousedown event
20372      */
20373     onMouseDown: function(e) { /* override this */ },
20374
20375     /**
20376      * Event handler that fires when a drag/drop obj gets a mouseup
20377      * @method onMouseUp
20378      * @param {Event} e the mouseup event
20379      */
20380     onMouseUp: function(e) { /* override this */ },
20381
20382     /**
20383      * Override the onAvailable method to do what is needed after the initial
20384      * position was determined.
20385      * @method onAvailable
20386      */
20387     onAvailable: function () {
20388     },
20389
20390     /*
20391      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20392      * @type Object
20393      */
20394     defaultPadding : {left:0, right:0, top:0, bottom:0},
20395
20396     /*
20397      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20398  *
20399  * Usage:
20400  <pre><code>
20401  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20402                 { dragElId: "existingProxyDiv" });
20403  dd.startDrag = function(){
20404      this.constrainTo("parent-id");
20405  };
20406  </code></pre>
20407  * Or you can initalize it using the {@link Roo.Element} object:
20408  <pre><code>
20409  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20410      startDrag : function(){
20411          this.constrainTo("parent-id");
20412      }
20413  });
20414  </code></pre>
20415      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20416      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20417      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20418      * an object containing the sides to pad. For example: {right:10, bottom:10}
20419      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20420      */
20421     constrainTo : function(constrainTo, pad, inContent){
20422         if(typeof pad == "number"){
20423             pad = {left: pad, right:pad, top:pad, bottom:pad};
20424         }
20425         pad = pad || this.defaultPadding;
20426         var b = Roo.get(this.getEl()).getBox();
20427         var ce = Roo.get(constrainTo);
20428         var s = ce.getScroll();
20429         var c, cd = ce.dom;
20430         if(cd == document.body){
20431             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20432         }else{
20433             xy = ce.getXY();
20434             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20435         }
20436
20437
20438         var topSpace = b.y - c.y;
20439         var leftSpace = b.x - c.x;
20440
20441         this.resetConstraints();
20442         this.setXConstraint(leftSpace - (pad.left||0), // left
20443                 c.width - leftSpace - b.width - (pad.right||0) //right
20444         );
20445         this.setYConstraint(topSpace - (pad.top||0), //top
20446                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20447         );
20448     },
20449
20450     /**
20451      * Returns a reference to the linked element
20452      * @method getEl
20453      * @return {HTMLElement} the html element
20454      */
20455     getEl: function() {
20456         if (!this._domRef) {
20457             this._domRef = Roo.getDom(this.id);
20458         }
20459
20460         return this._domRef;
20461     },
20462
20463     /**
20464      * Returns a reference to the actual element to drag.  By default this is
20465      * the same as the html element, but it can be assigned to another
20466      * element. An example of this can be found in Roo.dd.DDProxy
20467      * @method getDragEl
20468      * @return {HTMLElement} the html element
20469      */
20470     getDragEl: function() {
20471         return Roo.getDom(this.dragElId);
20472     },
20473
20474     /**
20475      * Sets up the DragDrop object.  Must be called in the constructor of any
20476      * Roo.dd.DragDrop subclass
20477      * @method init
20478      * @param id the id of the linked element
20479      * @param {String} sGroup the group of related items
20480      * @param {object} config configuration attributes
20481      */
20482     init: function(id, sGroup, config) {
20483         this.initTarget(id, sGroup, config);
20484         if (!Roo.isTouch) {
20485             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20486         }
20487         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20488         // Event.on(this.id, "selectstart", Event.preventDefault);
20489     },
20490
20491     /**
20492      * Initializes Targeting functionality only... the object does not
20493      * get a mousedown handler.
20494      * @method initTarget
20495      * @param id the id of the linked element
20496      * @param {String} sGroup the group of related items
20497      * @param {object} config configuration attributes
20498      */
20499     initTarget: function(id, sGroup, config) {
20500
20501         // configuration attributes
20502         this.config = config || {};
20503
20504         // create a local reference to the drag and drop manager
20505         this.DDM = Roo.dd.DDM;
20506         // initialize the groups array
20507         this.groups = {};
20508
20509         // assume that we have an element reference instead of an id if the
20510         // parameter is not a string
20511         if (typeof id !== "string") {
20512             id = Roo.id(id);
20513         }
20514
20515         // set the id
20516         this.id = id;
20517
20518         // add to an interaction group
20519         this.addToGroup((sGroup) ? sGroup : "default");
20520
20521         // We don't want to register this as the handle with the manager
20522         // so we just set the id rather than calling the setter.
20523         this.handleElId = id;
20524
20525         // the linked element is the element that gets dragged by default
20526         this.setDragElId(id);
20527
20528         // by default, clicked anchors will not start drag operations.
20529         this.invalidHandleTypes = { A: "A" };
20530         this.invalidHandleIds = {};
20531         this.invalidHandleClasses = [];
20532
20533         this.applyConfig();
20534
20535         this.handleOnAvailable();
20536     },
20537
20538     /**
20539      * Applies the configuration parameters that were passed into the constructor.
20540      * This is supposed to happen at each level through the inheritance chain.  So
20541      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20542      * DragDrop in order to get all of the parameters that are available in
20543      * each object.
20544      * @method applyConfig
20545      */
20546     applyConfig: function() {
20547
20548         // configurable properties:
20549         //    padding, isTarget, maintainOffset, primaryButtonOnly
20550         this.padding           = this.config.padding || [0, 0, 0, 0];
20551         this.isTarget          = (this.config.isTarget !== false);
20552         this.maintainOffset    = (this.config.maintainOffset);
20553         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20554
20555     },
20556
20557     /**
20558      * Executed when the linked element is available
20559      * @method handleOnAvailable
20560      * @private
20561      */
20562     handleOnAvailable: function() {
20563         this.available = true;
20564         this.resetConstraints();
20565         this.onAvailable();
20566     },
20567
20568      /**
20569      * Configures the padding for the target zone in px.  Effectively expands
20570      * (or reduces) the virtual object size for targeting calculations.
20571      * Supports css-style shorthand; if only one parameter is passed, all sides
20572      * will have that padding, and if only two are passed, the top and bottom
20573      * will have the first param, the left and right the second.
20574      * @method setPadding
20575      * @param {int} iTop    Top pad
20576      * @param {int} iRight  Right pad
20577      * @param {int} iBot    Bot pad
20578      * @param {int} iLeft   Left pad
20579      */
20580     setPadding: function(iTop, iRight, iBot, iLeft) {
20581         // this.padding = [iLeft, iRight, iTop, iBot];
20582         if (!iRight && 0 !== iRight) {
20583             this.padding = [iTop, iTop, iTop, iTop];
20584         } else if (!iBot && 0 !== iBot) {
20585             this.padding = [iTop, iRight, iTop, iRight];
20586         } else {
20587             this.padding = [iTop, iRight, iBot, iLeft];
20588         }
20589     },
20590
20591     /**
20592      * Stores the initial placement of the linked element.
20593      * @method setInitialPosition
20594      * @param {int} diffX   the X offset, default 0
20595      * @param {int} diffY   the Y offset, default 0
20596      */
20597     setInitPosition: function(diffX, diffY) {
20598         var el = this.getEl();
20599
20600         if (!this.DDM.verifyEl(el)) {
20601             return;
20602         }
20603
20604         var dx = diffX || 0;
20605         var dy = diffY || 0;
20606
20607         var p = Dom.getXY( el );
20608
20609         this.initPageX = p[0] - dx;
20610         this.initPageY = p[1] - dy;
20611
20612         this.lastPageX = p[0];
20613         this.lastPageY = p[1];
20614
20615
20616         this.setStartPosition(p);
20617     },
20618
20619     /**
20620      * Sets the start position of the element.  This is set when the obj
20621      * is initialized, the reset when a drag is started.
20622      * @method setStartPosition
20623      * @param pos current position (from previous lookup)
20624      * @private
20625      */
20626     setStartPosition: function(pos) {
20627         var p = pos || Dom.getXY( this.getEl() );
20628         this.deltaSetXY = null;
20629
20630         this.startPageX = p[0];
20631         this.startPageY = p[1];
20632     },
20633
20634     /**
20635      * Add this instance to a group of related drag/drop objects.  All
20636      * instances belong to at least one group, and can belong to as many
20637      * groups as needed.
20638      * @method addToGroup
20639      * @param sGroup {string} the name of the group
20640      */
20641     addToGroup: function(sGroup) {
20642         this.groups[sGroup] = true;
20643         this.DDM.regDragDrop(this, sGroup);
20644     },
20645
20646     /**
20647      * Remove's this instance from the supplied interaction group
20648      * @method removeFromGroup
20649      * @param {string}  sGroup  The group to drop
20650      */
20651     removeFromGroup: function(sGroup) {
20652         if (this.groups[sGroup]) {
20653             delete this.groups[sGroup];
20654         }
20655
20656         this.DDM.removeDDFromGroup(this, sGroup);
20657     },
20658
20659     /**
20660      * Allows you to specify that an element other than the linked element
20661      * will be moved with the cursor during a drag
20662      * @method setDragElId
20663      * @param id {string} the id of the element that will be used to initiate the drag
20664      */
20665     setDragElId: function(id) {
20666         this.dragElId = id;
20667     },
20668
20669     /**
20670      * Allows you to specify a child of the linked element that should be
20671      * used to initiate the drag operation.  An example of this would be if
20672      * you have a content div with text and links.  Clicking anywhere in the
20673      * content area would normally start the drag operation.  Use this method
20674      * to specify that an element inside of the content div is the element
20675      * that starts the drag operation.
20676      * @method setHandleElId
20677      * @param id {string} the id of the element that will be used to
20678      * initiate the drag.
20679      */
20680     setHandleElId: function(id) {
20681         if (typeof id !== "string") {
20682             id = Roo.id(id);
20683         }
20684         this.handleElId = id;
20685         this.DDM.regHandle(this.id, id);
20686     },
20687
20688     /**
20689      * Allows you to set an element outside of the linked element as a drag
20690      * handle
20691      * @method setOuterHandleElId
20692      * @param id the id of the element that will be used to initiate the drag
20693      */
20694     setOuterHandleElId: function(id) {
20695         if (typeof id !== "string") {
20696             id = Roo.id(id);
20697         }
20698         Event.on(id, "mousedown",
20699                 this.handleMouseDown, this);
20700         this.setHandleElId(id);
20701
20702         this.hasOuterHandles = true;
20703     },
20704
20705     /**
20706      * Remove all drag and drop hooks for this element
20707      * @method unreg
20708      */
20709     unreg: function() {
20710         Event.un(this.id, "mousedown",
20711                 this.handleMouseDown);
20712         Event.un(this.id, "touchstart",
20713                 this.handleMouseDown);
20714         this._domRef = null;
20715         this.DDM._remove(this);
20716     },
20717
20718     destroy : function(){
20719         this.unreg();
20720     },
20721
20722     /**
20723      * Returns true if this instance is locked, or the drag drop mgr is locked
20724      * (meaning that all drag/drop is disabled on the page.)
20725      * @method isLocked
20726      * @return {boolean} true if this obj or all drag/drop is locked, else
20727      * false
20728      */
20729     isLocked: function() {
20730         return (this.DDM.isLocked() || this.locked);
20731     },
20732
20733     /**
20734      * Fired when this object is clicked
20735      * @method handleMouseDown
20736      * @param {Event} e
20737      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20738      * @private
20739      */
20740     handleMouseDown: function(e, oDD){
20741      
20742         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20743             //Roo.log('not touch/ button !=0');
20744             return;
20745         }
20746         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20747             return; // double touch..
20748         }
20749         
20750
20751         if (this.isLocked()) {
20752             //Roo.log('locked');
20753             return;
20754         }
20755
20756         this.DDM.refreshCache(this.groups);
20757 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20758         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20759         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20760             //Roo.log('no outer handes or not over target');
20761                 // do nothing.
20762         } else {
20763 //            Roo.log('check validator');
20764             if (this.clickValidator(e)) {
20765 //                Roo.log('validate success');
20766                 // set the initial element position
20767                 this.setStartPosition();
20768
20769
20770                 this.b4MouseDown(e);
20771                 this.onMouseDown(e);
20772
20773                 this.DDM.handleMouseDown(e, this);
20774
20775                 this.DDM.stopEvent(e);
20776             } else {
20777
20778
20779             }
20780         }
20781     },
20782
20783     clickValidator: function(e) {
20784         var target = e.getTarget();
20785         return ( this.isValidHandleChild(target) &&
20786                     (this.id == this.handleElId ||
20787                         this.DDM.handleWasClicked(target, this.id)) );
20788     },
20789
20790     /**
20791      * Allows you to specify a tag name that should not start a drag operation
20792      * when clicked.  This is designed to facilitate embedding links within a
20793      * drag handle that do something other than start the drag.
20794      * @method addInvalidHandleType
20795      * @param {string} tagName the type of element to exclude
20796      */
20797     addInvalidHandleType: function(tagName) {
20798         var type = tagName.toUpperCase();
20799         this.invalidHandleTypes[type] = type;
20800     },
20801
20802     /**
20803      * Lets you to specify an element id for a child of a drag handle
20804      * that should not initiate a drag
20805      * @method addInvalidHandleId
20806      * @param {string} id the element id of the element you wish to ignore
20807      */
20808     addInvalidHandleId: function(id) {
20809         if (typeof id !== "string") {
20810             id = Roo.id(id);
20811         }
20812         this.invalidHandleIds[id] = id;
20813     },
20814
20815     /**
20816      * Lets you specify a css class of elements that will not initiate a drag
20817      * @method addInvalidHandleClass
20818      * @param {string} cssClass the class of the elements you wish to ignore
20819      */
20820     addInvalidHandleClass: function(cssClass) {
20821         this.invalidHandleClasses.push(cssClass);
20822     },
20823
20824     /**
20825      * Unsets an excluded tag name set by addInvalidHandleType
20826      * @method removeInvalidHandleType
20827      * @param {string} tagName the type of element to unexclude
20828      */
20829     removeInvalidHandleType: function(tagName) {
20830         var type = tagName.toUpperCase();
20831         // this.invalidHandleTypes[type] = null;
20832         delete this.invalidHandleTypes[type];
20833     },
20834
20835     /**
20836      * Unsets an invalid handle id
20837      * @method removeInvalidHandleId
20838      * @param {string} id the id of the element to re-enable
20839      */
20840     removeInvalidHandleId: function(id) {
20841         if (typeof id !== "string") {
20842             id = Roo.id(id);
20843         }
20844         delete this.invalidHandleIds[id];
20845     },
20846
20847     /**
20848      * Unsets an invalid css class
20849      * @method removeInvalidHandleClass
20850      * @param {string} cssClass the class of the element(s) you wish to
20851      * re-enable
20852      */
20853     removeInvalidHandleClass: function(cssClass) {
20854         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20855             if (this.invalidHandleClasses[i] == cssClass) {
20856                 delete this.invalidHandleClasses[i];
20857             }
20858         }
20859     },
20860
20861     /**
20862      * Checks the tag exclusion list to see if this click should be ignored
20863      * @method isValidHandleChild
20864      * @param {HTMLElement} node the HTMLElement to evaluate
20865      * @return {boolean} true if this is a valid tag type, false if not
20866      */
20867     isValidHandleChild: function(node) {
20868
20869         var valid = true;
20870         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20871         var nodeName;
20872         try {
20873             nodeName = node.nodeName.toUpperCase();
20874         } catch(e) {
20875             nodeName = node.nodeName;
20876         }
20877         valid = valid && !this.invalidHandleTypes[nodeName];
20878         valid = valid && !this.invalidHandleIds[node.id];
20879
20880         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20881             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20882         }
20883
20884
20885         return valid;
20886
20887     },
20888
20889     /**
20890      * Create the array of horizontal tick marks if an interval was specified
20891      * in setXConstraint().
20892      * @method setXTicks
20893      * @private
20894      */
20895     setXTicks: function(iStartX, iTickSize) {
20896         this.xTicks = [];
20897         this.xTickSize = iTickSize;
20898
20899         var tickMap = {};
20900
20901         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20902             if (!tickMap[i]) {
20903                 this.xTicks[this.xTicks.length] = i;
20904                 tickMap[i] = true;
20905             }
20906         }
20907
20908         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20909             if (!tickMap[i]) {
20910                 this.xTicks[this.xTicks.length] = i;
20911                 tickMap[i] = true;
20912             }
20913         }
20914
20915         this.xTicks.sort(this.DDM.numericSort) ;
20916     },
20917
20918     /**
20919      * Create the array of vertical tick marks if an interval was specified in
20920      * setYConstraint().
20921      * @method setYTicks
20922      * @private
20923      */
20924     setYTicks: function(iStartY, iTickSize) {
20925         this.yTicks = [];
20926         this.yTickSize = iTickSize;
20927
20928         var tickMap = {};
20929
20930         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20931             if (!tickMap[i]) {
20932                 this.yTicks[this.yTicks.length] = i;
20933                 tickMap[i] = true;
20934             }
20935         }
20936
20937         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20938             if (!tickMap[i]) {
20939                 this.yTicks[this.yTicks.length] = i;
20940                 tickMap[i] = true;
20941             }
20942         }
20943
20944         this.yTicks.sort(this.DDM.numericSort) ;
20945     },
20946
20947     /**
20948      * By default, the element can be dragged any place on the screen.  Use
20949      * this method to limit the horizontal travel of the element.  Pass in
20950      * 0,0 for the parameters if you want to lock the drag to the y axis.
20951      * @method setXConstraint
20952      * @param {int} iLeft the number of pixels the element can move to the left
20953      * @param {int} iRight the number of pixels the element can move to the
20954      * right
20955      * @param {int} iTickSize optional parameter for specifying that the
20956      * element
20957      * should move iTickSize pixels at a time.
20958      */
20959     setXConstraint: function(iLeft, iRight, iTickSize) {
20960         this.leftConstraint = iLeft;
20961         this.rightConstraint = iRight;
20962
20963         this.minX = this.initPageX - iLeft;
20964         this.maxX = this.initPageX + iRight;
20965         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20966
20967         this.constrainX = true;
20968     },
20969
20970     /**
20971      * Clears any constraints applied to this instance.  Also clears ticks
20972      * since they can't exist independent of a constraint at this time.
20973      * @method clearConstraints
20974      */
20975     clearConstraints: function() {
20976         this.constrainX = false;
20977         this.constrainY = false;
20978         this.clearTicks();
20979     },
20980
20981     /**
20982      * Clears any tick interval defined for this instance
20983      * @method clearTicks
20984      */
20985     clearTicks: function() {
20986         this.xTicks = null;
20987         this.yTicks = null;
20988         this.xTickSize = 0;
20989         this.yTickSize = 0;
20990     },
20991
20992     /**
20993      * By default, the element can be dragged any place on the screen.  Set
20994      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20995      * parameters if you want to lock the drag to the x axis.
20996      * @method setYConstraint
20997      * @param {int} iUp the number of pixels the element can move up
20998      * @param {int} iDown the number of pixels the element can move down
20999      * @param {int} iTickSize optional parameter for specifying that the
21000      * element should move iTickSize pixels at a time.
21001      */
21002     setYConstraint: function(iUp, iDown, iTickSize) {
21003         this.topConstraint = iUp;
21004         this.bottomConstraint = iDown;
21005
21006         this.minY = this.initPageY - iUp;
21007         this.maxY = this.initPageY + iDown;
21008         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21009
21010         this.constrainY = true;
21011
21012     },
21013
21014     /**
21015      * resetConstraints must be called if you manually reposition a dd element.
21016      * @method resetConstraints
21017      * @param {boolean} maintainOffset
21018      */
21019     resetConstraints: function() {
21020
21021
21022         // Maintain offsets if necessary
21023         if (this.initPageX || this.initPageX === 0) {
21024             // figure out how much this thing has moved
21025             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21026             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21027
21028             this.setInitPosition(dx, dy);
21029
21030         // This is the first time we have detected the element's position
21031         } else {
21032             this.setInitPosition();
21033         }
21034
21035         if (this.constrainX) {
21036             this.setXConstraint( this.leftConstraint,
21037                                  this.rightConstraint,
21038                                  this.xTickSize        );
21039         }
21040
21041         if (this.constrainY) {
21042             this.setYConstraint( this.topConstraint,
21043                                  this.bottomConstraint,
21044                                  this.yTickSize         );
21045         }
21046     },
21047
21048     /**
21049      * Normally the drag element is moved pixel by pixel, but we can specify
21050      * that it move a number of pixels at a time.  This method resolves the
21051      * location when we have it set up like this.
21052      * @method getTick
21053      * @param {int} val where we want to place the object
21054      * @param {int[]} tickArray sorted array of valid points
21055      * @return {int} the closest tick
21056      * @private
21057      */
21058     getTick: function(val, tickArray) {
21059
21060         if (!tickArray) {
21061             // If tick interval is not defined, it is effectively 1 pixel,
21062             // so we return the value passed to us.
21063             return val;
21064         } else if (tickArray[0] >= val) {
21065             // The value is lower than the first tick, so we return the first
21066             // tick.
21067             return tickArray[0];
21068         } else {
21069             for (var i=0, len=tickArray.length; i<len; ++i) {
21070                 var next = i + 1;
21071                 if (tickArray[next] && tickArray[next] >= val) {
21072                     var diff1 = val - tickArray[i];
21073                     var diff2 = tickArray[next] - val;
21074                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21075                 }
21076             }
21077
21078             // The value is larger than the last tick, so we return the last
21079             // tick.
21080             return tickArray[tickArray.length - 1];
21081         }
21082     },
21083
21084     /**
21085      * toString method
21086      * @method toString
21087      * @return {string} string representation of the dd obj
21088      */
21089     toString: function() {
21090         return ("DragDrop " + this.id);
21091     }
21092
21093 });
21094
21095 })();
21096 /*
21097  * Based on:
21098  * Ext JS Library 1.1.1
21099  * Copyright(c) 2006-2007, Ext JS, LLC.
21100  *
21101  * Originally Released Under LGPL - original licence link has changed is not relivant.
21102  *
21103  * Fork - LGPL
21104  * <script type="text/javascript">
21105  */
21106
21107
21108 /**
21109  * The drag and drop utility provides a framework for building drag and drop
21110  * applications.  In addition to enabling drag and drop for specific elements,
21111  * the drag and drop elements are tracked by the manager class, and the
21112  * interactions between the various elements are tracked during the drag and
21113  * the implementing code is notified about these important moments.
21114  */
21115
21116 // Only load the library once.  Rewriting the manager class would orphan
21117 // existing drag and drop instances.
21118 if (!Roo.dd.DragDropMgr) {
21119
21120 /**
21121  * @class Roo.dd.DragDropMgr
21122  * DragDropMgr is a singleton that tracks the element interaction for
21123  * all DragDrop items in the window.  Generally, you will not call
21124  * this class directly, but it does have helper methods that could
21125  * be useful in your DragDrop implementations.
21126  * @static
21127  */
21128 Roo.dd.DragDropMgr = function() {
21129
21130     var Event = Roo.EventManager;
21131
21132     return {
21133
21134         /**
21135          * Two dimensional Array of registered DragDrop objects.  The first
21136          * dimension is the DragDrop item group, the second the DragDrop
21137          * object.
21138          * @property ids
21139          * @type {string: string}
21140          * @private
21141          * @static
21142          */
21143         ids: {},
21144
21145         /**
21146          * Array of element ids defined as drag handles.  Used to determine
21147          * if the element that generated the mousedown event is actually the
21148          * handle and not the html element itself.
21149          * @property handleIds
21150          * @type {string: string}
21151          * @private
21152          * @static
21153          */
21154         handleIds: {},
21155
21156         /**
21157          * the DragDrop object that is currently being dragged
21158          * @property dragCurrent
21159          * @type DragDrop
21160          * @private
21161          * @static
21162          **/
21163         dragCurrent: null,
21164
21165         /**
21166          * the DragDrop object(s) that are being hovered over
21167          * @property dragOvers
21168          * @type Array
21169          * @private
21170          * @static
21171          */
21172         dragOvers: {},
21173
21174         /**
21175          * the X distance between the cursor and the object being dragged
21176          * @property deltaX
21177          * @type int
21178          * @private
21179          * @static
21180          */
21181         deltaX: 0,
21182
21183         /**
21184          * the Y distance between the cursor and the object being dragged
21185          * @property deltaY
21186          * @type int
21187          * @private
21188          * @static
21189          */
21190         deltaY: 0,
21191
21192         /**
21193          * Flag to determine if we should prevent the default behavior of the
21194          * events we define. By default this is true, but this can be set to
21195          * false if you need the default behavior (not recommended)
21196          * @property preventDefault
21197          * @type boolean
21198          * @static
21199          */
21200         preventDefault: true,
21201
21202         /**
21203          * Flag to determine if we should stop the propagation of the events
21204          * we generate. This is true by default but you may want to set it to
21205          * false if the html element contains other features that require the
21206          * mouse click.
21207          * @property stopPropagation
21208          * @type boolean
21209          * @static
21210          */
21211         stopPropagation: true,
21212
21213         /**
21214          * Internal flag that is set to true when drag and drop has been
21215          * intialized
21216          * @property initialized
21217          * @private
21218          * @static
21219          */
21220         initalized: false,
21221
21222         /**
21223          * All drag and drop can be disabled.
21224          * @property locked
21225          * @private
21226          * @static
21227          */
21228         locked: false,
21229
21230         /**
21231          * Called the first time an element is registered.
21232          * @method init
21233          * @private
21234          * @static
21235          */
21236         init: function() {
21237             this.initialized = true;
21238         },
21239
21240         /**
21241          * In point mode, drag and drop interaction is defined by the
21242          * location of the cursor during the drag/drop
21243          * @property POINT
21244          * @type int
21245          * @static
21246          */
21247         POINT: 0,
21248
21249         /**
21250          * In intersect mode, drag and drop interactio nis defined by the
21251          * overlap of two or more drag and drop objects.
21252          * @property INTERSECT
21253          * @type int
21254          * @static
21255          */
21256         INTERSECT: 1,
21257
21258         /**
21259          * The current drag and drop mode.  Default: POINT
21260          * @property mode
21261          * @type int
21262          * @static
21263          */
21264         mode: 0,
21265
21266         /**
21267          * Runs method on all drag and drop objects
21268          * @method _execOnAll
21269          * @private
21270          * @static
21271          */
21272         _execOnAll: function(sMethod, args) {
21273             for (var i in this.ids) {
21274                 for (var j in this.ids[i]) {
21275                     var oDD = this.ids[i][j];
21276                     if (! this.isTypeOfDD(oDD)) {
21277                         continue;
21278                     }
21279                     oDD[sMethod].apply(oDD, args);
21280                 }
21281             }
21282         },
21283
21284         /**
21285          * Drag and drop initialization.  Sets up the global event handlers
21286          * @method _onLoad
21287          * @private
21288          * @static
21289          */
21290         _onLoad: function() {
21291
21292             this.init();
21293
21294             if (!Roo.isTouch) {
21295                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21296                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21297             }
21298             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21299             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21300             
21301             Event.on(window,   "unload",    this._onUnload, this, true);
21302             Event.on(window,   "resize",    this._onResize, this, true);
21303             // Event.on(window,   "mouseout",    this._test);
21304
21305         },
21306
21307         /**
21308          * Reset constraints on all drag and drop objs
21309          * @method _onResize
21310          * @private
21311          * @static
21312          */
21313         _onResize: function(e) {
21314             this._execOnAll("resetConstraints", []);
21315         },
21316
21317         /**
21318          * Lock all drag and drop functionality
21319          * @method lock
21320          * @static
21321          */
21322         lock: function() { this.locked = true; },
21323
21324         /**
21325          * Unlock all drag and drop functionality
21326          * @method unlock
21327          * @static
21328          */
21329         unlock: function() { this.locked = false; },
21330
21331         /**
21332          * Is drag and drop locked?
21333          * @method isLocked
21334          * @return {boolean} True if drag and drop is locked, false otherwise.
21335          * @static
21336          */
21337         isLocked: function() { return this.locked; },
21338
21339         /**
21340          * Location cache that is set for all drag drop objects when a drag is
21341          * initiated, cleared when the drag is finished.
21342          * @property locationCache
21343          * @private
21344          * @static
21345          */
21346         locationCache: {},
21347
21348         /**
21349          * Set useCache to false if you want to force object the lookup of each
21350          * drag and drop linked element constantly during a drag.
21351          * @property useCache
21352          * @type boolean
21353          * @static
21354          */
21355         useCache: true,
21356
21357         /**
21358          * The number of pixels that the mouse needs to move after the
21359          * mousedown before the drag is initiated.  Default=3;
21360          * @property clickPixelThresh
21361          * @type int
21362          * @static
21363          */
21364         clickPixelThresh: 3,
21365
21366         /**
21367          * The number of milliseconds after the mousedown event to initiate the
21368          * drag if we don't get a mouseup event. Default=1000
21369          * @property clickTimeThresh
21370          * @type int
21371          * @static
21372          */
21373         clickTimeThresh: 350,
21374
21375         /**
21376          * Flag that indicates that either the drag pixel threshold or the
21377          * mousdown time threshold has been met
21378          * @property dragThreshMet
21379          * @type boolean
21380          * @private
21381          * @static
21382          */
21383         dragThreshMet: false,
21384
21385         /**
21386          * Timeout used for the click time threshold
21387          * @property clickTimeout
21388          * @type Object
21389          * @private
21390          * @static
21391          */
21392         clickTimeout: null,
21393
21394         /**
21395          * The X position of the mousedown event stored for later use when a
21396          * drag threshold is met.
21397          * @property startX
21398          * @type int
21399          * @private
21400          * @static
21401          */
21402         startX: 0,
21403
21404         /**
21405          * The Y position of the mousedown event stored for later use when a
21406          * drag threshold is met.
21407          * @property startY
21408          * @type int
21409          * @private
21410          * @static
21411          */
21412         startY: 0,
21413
21414         /**
21415          * Each DragDrop instance must be registered with the DragDropMgr.
21416          * This is executed in DragDrop.init()
21417          * @method regDragDrop
21418          * @param {DragDrop} oDD the DragDrop object to register
21419          * @param {String} sGroup the name of the group this element belongs to
21420          * @static
21421          */
21422         regDragDrop: function(oDD, sGroup) {
21423             if (!this.initialized) { this.init(); }
21424
21425             if (!this.ids[sGroup]) {
21426                 this.ids[sGroup] = {};
21427             }
21428             this.ids[sGroup][oDD.id] = oDD;
21429         },
21430
21431         /**
21432          * Removes the supplied dd instance from the supplied group. Executed
21433          * by DragDrop.removeFromGroup, so don't call this function directly.
21434          * @method removeDDFromGroup
21435          * @private
21436          * @static
21437          */
21438         removeDDFromGroup: function(oDD, sGroup) {
21439             if (!this.ids[sGroup]) {
21440                 this.ids[sGroup] = {};
21441             }
21442
21443             var obj = this.ids[sGroup];
21444             if (obj && obj[oDD.id]) {
21445                 delete obj[oDD.id];
21446             }
21447         },
21448
21449         /**
21450          * Unregisters a drag and drop item.  This is executed in
21451          * DragDrop.unreg, use that method instead of calling this directly.
21452          * @method _remove
21453          * @private
21454          * @static
21455          */
21456         _remove: function(oDD) {
21457             for (var g in oDD.groups) {
21458                 if (g && this.ids[g][oDD.id]) {
21459                     delete this.ids[g][oDD.id];
21460                 }
21461             }
21462             delete this.handleIds[oDD.id];
21463         },
21464
21465         /**
21466          * Each DragDrop handle element must be registered.  This is done
21467          * automatically when executing DragDrop.setHandleElId()
21468          * @method regHandle
21469          * @param {String} sDDId the DragDrop id this element is a handle for
21470          * @param {String} sHandleId the id of the element that is the drag
21471          * handle
21472          * @static
21473          */
21474         regHandle: function(sDDId, sHandleId) {
21475             if (!this.handleIds[sDDId]) {
21476                 this.handleIds[sDDId] = {};
21477             }
21478             this.handleIds[sDDId][sHandleId] = sHandleId;
21479         },
21480
21481         /**
21482          * Utility function to determine if a given element has been
21483          * registered as a drag drop item.
21484          * @method isDragDrop
21485          * @param {String} id the element id to check
21486          * @return {boolean} true if this element is a DragDrop item,
21487          * false otherwise
21488          * @static
21489          */
21490         isDragDrop: function(id) {
21491             return ( this.getDDById(id) ) ? true : false;
21492         },
21493
21494         /**
21495          * Returns the drag and drop instances that are in all groups the
21496          * passed in instance belongs to.
21497          * @method getRelated
21498          * @param {DragDrop} p_oDD the obj to get related data for
21499          * @param {boolean} bTargetsOnly if true, only return targetable objs
21500          * @return {DragDrop[]} the related instances
21501          * @static
21502          */
21503         getRelated: function(p_oDD, bTargetsOnly) {
21504             var oDDs = [];
21505             for (var i in p_oDD.groups) {
21506                 for (j in this.ids[i]) {
21507                     var dd = this.ids[i][j];
21508                     if (! this.isTypeOfDD(dd)) {
21509                         continue;
21510                     }
21511                     if (!bTargetsOnly || dd.isTarget) {
21512                         oDDs[oDDs.length] = dd;
21513                     }
21514                 }
21515             }
21516
21517             return oDDs;
21518         },
21519
21520         /**
21521          * Returns true if the specified dd target is a legal target for
21522          * the specifice drag obj
21523          * @method isLegalTarget
21524          * @param {DragDrop} the drag obj
21525          * @param {DragDrop} the target
21526          * @return {boolean} true if the target is a legal target for the
21527          * dd obj
21528          * @static
21529          */
21530         isLegalTarget: function (oDD, oTargetDD) {
21531             var targets = this.getRelated(oDD, true);
21532             for (var i=0, len=targets.length;i<len;++i) {
21533                 if (targets[i].id == oTargetDD.id) {
21534                     return true;
21535                 }
21536             }
21537
21538             return false;
21539         },
21540
21541         /**
21542          * My goal is to be able to transparently determine if an object is
21543          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21544          * returns "object", oDD.constructor.toString() always returns
21545          * "DragDrop" and not the name of the subclass.  So for now it just
21546          * evaluates a well-known variable in DragDrop.
21547          * @method isTypeOfDD
21548          * @param {Object} the object to evaluate
21549          * @return {boolean} true if typeof oDD = DragDrop
21550          * @static
21551          */
21552         isTypeOfDD: function (oDD) {
21553             return (oDD && oDD.__ygDragDrop);
21554         },
21555
21556         /**
21557          * Utility function to determine if a given element has been
21558          * registered as a drag drop handle for the given Drag Drop object.
21559          * @method isHandle
21560          * @param {String} id the element id to check
21561          * @return {boolean} true if this element is a DragDrop handle, false
21562          * otherwise
21563          * @static
21564          */
21565         isHandle: function(sDDId, sHandleId) {
21566             return ( this.handleIds[sDDId] &&
21567                             this.handleIds[sDDId][sHandleId] );
21568         },
21569
21570         /**
21571          * Returns the DragDrop instance for a given id
21572          * @method getDDById
21573          * @param {String} id the id of the DragDrop object
21574          * @return {DragDrop} the drag drop object, null if it is not found
21575          * @static
21576          */
21577         getDDById: function(id) {
21578             for (var i in this.ids) {
21579                 if (this.ids[i][id]) {
21580                     return this.ids[i][id];
21581                 }
21582             }
21583             return null;
21584         },
21585
21586         /**
21587          * Fired after a registered DragDrop object gets the mousedown event.
21588          * Sets up the events required to track the object being dragged
21589          * @method handleMouseDown
21590          * @param {Event} e the event
21591          * @param oDD the DragDrop object being dragged
21592          * @private
21593          * @static
21594          */
21595         handleMouseDown: function(e, oDD) {
21596             if(Roo.QuickTips){
21597                 Roo.QuickTips.disable();
21598             }
21599             this.currentTarget = e.getTarget();
21600
21601             this.dragCurrent = oDD;
21602
21603             var el = oDD.getEl();
21604
21605             // track start position
21606             this.startX = e.getPageX();
21607             this.startY = e.getPageY();
21608
21609             this.deltaX = this.startX - el.offsetLeft;
21610             this.deltaY = this.startY - el.offsetTop;
21611
21612             this.dragThreshMet = false;
21613
21614             this.clickTimeout = setTimeout(
21615                     function() {
21616                         var DDM = Roo.dd.DDM;
21617                         DDM.startDrag(DDM.startX, DDM.startY);
21618                     },
21619                     this.clickTimeThresh );
21620         },
21621
21622         /**
21623          * Fired when either the drag pixel threshol or the mousedown hold
21624          * time threshold has been met.
21625          * @method startDrag
21626          * @param x {int} the X position of the original mousedown
21627          * @param y {int} the Y position of the original mousedown
21628          * @static
21629          */
21630         startDrag: function(x, y) {
21631             clearTimeout(this.clickTimeout);
21632             if (this.dragCurrent) {
21633                 this.dragCurrent.b4StartDrag(x, y);
21634                 this.dragCurrent.startDrag(x, y);
21635             }
21636             this.dragThreshMet = true;
21637         },
21638
21639         /**
21640          * Internal function to handle the mouseup event.  Will be invoked
21641          * from the context of the document.
21642          * @method handleMouseUp
21643          * @param {Event} e the event
21644          * @private
21645          * @static
21646          */
21647         handleMouseUp: function(e) {
21648
21649             if(Roo.QuickTips){
21650                 Roo.QuickTips.enable();
21651             }
21652             if (! this.dragCurrent) {
21653                 return;
21654             }
21655
21656             clearTimeout(this.clickTimeout);
21657
21658             if (this.dragThreshMet) {
21659                 this.fireEvents(e, true);
21660             } else {
21661             }
21662
21663             this.stopDrag(e);
21664
21665             this.stopEvent(e);
21666         },
21667
21668         /**
21669          * Utility to stop event propagation and event default, if these
21670          * features are turned on.
21671          * @method stopEvent
21672          * @param {Event} e the event as returned by this.getEvent()
21673          * @static
21674          */
21675         stopEvent: function(e){
21676             if(this.stopPropagation) {
21677                 e.stopPropagation();
21678             }
21679
21680             if (this.preventDefault) {
21681                 e.preventDefault();
21682             }
21683         },
21684
21685         /**
21686          * Internal function to clean up event handlers after the drag
21687          * operation is complete
21688          * @method stopDrag
21689          * @param {Event} e the event
21690          * @private
21691          * @static
21692          */
21693         stopDrag: function(e) {
21694             // Fire the drag end event for the item that was dragged
21695             if (this.dragCurrent) {
21696                 if (this.dragThreshMet) {
21697                     this.dragCurrent.b4EndDrag(e);
21698                     this.dragCurrent.endDrag(e);
21699                 }
21700
21701                 this.dragCurrent.onMouseUp(e);
21702             }
21703
21704             this.dragCurrent = null;
21705             this.dragOvers = {};
21706         },
21707
21708         /**
21709          * Internal function to handle the mousemove event.  Will be invoked
21710          * from the context of the html element.
21711          *
21712          * @TODO figure out what we can do about mouse events lost when the
21713          * user drags objects beyond the window boundary.  Currently we can
21714          * detect this in internet explorer by verifying that the mouse is
21715          * down during the mousemove event.  Firefox doesn't give us the
21716          * button state on the mousemove event.
21717          * @method handleMouseMove
21718          * @param {Event} e the event
21719          * @private
21720          * @static
21721          */
21722         handleMouseMove: function(e) {
21723             if (! this.dragCurrent) {
21724                 return true;
21725             }
21726
21727             // var button = e.which || e.button;
21728
21729             // check for IE mouseup outside of page boundary
21730             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21731                 this.stopEvent(e);
21732                 return this.handleMouseUp(e);
21733             }
21734
21735             if (!this.dragThreshMet) {
21736                 var diffX = Math.abs(this.startX - e.getPageX());
21737                 var diffY = Math.abs(this.startY - e.getPageY());
21738                 if (diffX > this.clickPixelThresh ||
21739                             diffY > this.clickPixelThresh) {
21740                     this.startDrag(this.startX, this.startY);
21741                 }
21742             }
21743
21744             if (this.dragThreshMet) {
21745                 this.dragCurrent.b4Drag(e);
21746                 this.dragCurrent.onDrag(e);
21747                 if(!this.dragCurrent.moveOnly){
21748                     this.fireEvents(e, false);
21749                 }
21750             }
21751
21752             this.stopEvent(e);
21753
21754             return true;
21755         },
21756
21757         /**
21758          * Iterates over all of the DragDrop elements to find ones we are
21759          * hovering over or dropping on
21760          * @method fireEvents
21761          * @param {Event} e the event
21762          * @param {boolean} isDrop is this a drop op or a mouseover op?
21763          * @private
21764          * @static
21765          */
21766         fireEvents: function(e, isDrop) {
21767             var dc = this.dragCurrent;
21768
21769             // If the user did the mouse up outside of the window, we could
21770             // get here even though we have ended the drag.
21771             if (!dc || dc.isLocked()) {
21772                 return;
21773             }
21774
21775             var pt = e.getPoint();
21776
21777             // cache the previous dragOver array
21778             var oldOvers = [];
21779
21780             var outEvts   = [];
21781             var overEvts  = [];
21782             var dropEvts  = [];
21783             var enterEvts = [];
21784
21785             // Check to see if the object(s) we were hovering over is no longer
21786             // being hovered over so we can fire the onDragOut event
21787             for (var i in this.dragOvers) {
21788
21789                 var ddo = this.dragOvers[i];
21790
21791                 if (! this.isTypeOfDD(ddo)) {
21792                     continue;
21793                 }
21794
21795                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21796                     outEvts.push( ddo );
21797                 }
21798
21799                 oldOvers[i] = true;
21800                 delete this.dragOvers[i];
21801             }
21802
21803             for (var sGroup in dc.groups) {
21804
21805                 if ("string" != typeof sGroup) {
21806                     continue;
21807                 }
21808
21809                 for (i in this.ids[sGroup]) {
21810                     var oDD = this.ids[sGroup][i];
21811                     if (! this.isTypeOfDD(oDD)) {
21812                         continue;
21813                     }
21814
21815                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21816                         if (this.isOverTarget(pt, oDD, this.mode)) {
21817                             // look for drop interactions
21818                             if (isDrop) {
21819                                 dropEvts.push( oDD );
21820                             // look for drag enter and drag over interactions
21821                             } else {
21822
21823                                 // initial drag over: dragEnter fires
21824                                 if (!oldOvers[oDD.id]) {
21825                                     enterEvts.push( oDD );
21826                                 // subsequent drag overs: dragOver fires
21827                                 } else {
21828                                     overEvts.push( oDD );
21829                                 }
21830
21831                                 this.dragOvers[oDD.id] = oDD;
21832                             }
21833                         }
21834                     }
21835                 }
21836             }
21837
21838             if (this.mode) {
21839                 if (outEvts.length) {
21840                     dc.b4DragOut(e, outEvts);
21841                     dc.onDragOut(e, outEvts);
21842                 }
21843
21844                 if (enterEvts.length) {
21845                     dc.onDragEnter(e, enterEvts);
21846                 }
21847
21848                 if (overEvts.length) {
21849                     dc.b4DragOver(e, overEvts);
21850                     dc.onDragOver(e, overEvts);
21851                 }
21852
21853                 if (dropEvts.length) {
21854                     dc.b4DragDrop(e, dropEvts);
21855                     dc.onDragDrop(e, dropEvts);
21856                 }
21857
21858             } else {
21859                 // fire dragout events
21860                 var len = 0;
21861                 for (i=0, len=outEvts.length; i<len; ++i) {
21862                     dc.b4DragOut(e, outEvts[i].id);
21863                     dc.onDragOut(e, outEvts[i].id);
21864                 }
21865
21866                 // fire enter events
21867                 for (i=0,len=enterEvts.length; i<len; ++i) {
21868                     // dc.b4DragEnter(e, oDD.id);
21869                     dc.onDragEnter(e, enterEvts[i].id);
21870                 }
21871
21872                 // fire over events
21873                 for (i=0,len=overEvts.length; i<len; ++i) {
21874                     dc.b4DragOver(e, overEvts[i].id);
21875                     dc.onDragOver(e, overEvts[i].id);
21876                 }
21877
21878                 // fire drop events
21879                 for (i=0, len=dropEvts.length; i<len; ++i) {
21880                     dc.b4DragDrop(e, dropEvts[i].id);
21881                     dc.onDragDrop(e, dropEvts[i].id);
21882                 }
21883
21884             }
21885
21886             // notify about a drop that did not find a target
21887             if (isDrop && !dropEvts.length) {
21888                 dc.onInvalidDrop(e);
21889             }
21890
21891         },
21892
21893         /**
21894          * Helper function for getting the best match from the list of drag
21895          * and drop objects returned by the drag and drop events when we are
21896          * in INTERSECT mode.  It returns either the first object that the
21897          * cursor is over, or the object that has the greatest overlap with
21898          * the dragged element.
21899          * @method getBestMatch
21900          * @param  {DragDrop[]} dds The array of drag and drop objects
21901          * targeted
21902          * @return {DragDrop}       The best single match
21903          * @static
21904          */
21905         getBestMatch: function(dds) {
21906             var winner = null;
21907             // Return null if the input is not what we expect
21908             //if (!dds || !dds.length || dds.length == 0) {
21909                // winner = null;
21910             // If there is only one item, it wins
21911             //} else if (dds.length == 1) {
21912
21913             var len = dds.length;
21914
21915             if (len == 1) {
21916                 winner = dds[0];
21917             } else {
21918                 // Loop through the targeted items
21919                 for (var i=0; i<len; ++i) {
21920                     var dd = dds[i];
21921                     // If the cursor is over the object, it wins.  If the
21922                     // cursor is over multiple matches, the first one we come
21923                     // to wins.
21924                     if (dd.cursorIsOver) {
21925                         winner = dd;
21926                         break;
21927                     // Otherwise the object with the most overlap wins
21928                     } else {
21929                         if (!winner ||
21930                             winner.overlap.getArea() < dd.overlap.getArea()) {
21931                             winner = dd;
21932                         }
21933                     }
21934                 }
21935             }
21936
21937             return winner;
21938         },
21939
21940         /**
21941          * Refreshes the cache of the top-left and bottom-right points of the
21942          * drag and drop objects in the specified group(s).  This is in the
21943          * format that is stored in the drag and drop instance, so typical
21944          * usage is:
21945          * <code>
21946          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21947          * </code>
21948          * Alternatively:
21949          * <code>
21950          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21951          * </code>
21952          * @TODO this really should be an indexed array.  Alternatively this
21953          * method could accept both.
21954          * @method refreshCache
21955          * @param {Object} groups an associative array of groups to refresh
21956          * @static
21957          */
21958         refreshCache: function(groups) {
21959             for (var sGroup in groups) {
21960                 if ("string" != typeof sGroup) {
21961                     continue;
21962                 }
21963                 for (var i in this.ids[sGroup]) {
21964                     var oDD = this.ids[sGroup][i];
21965
21966                     if (this.isTypeOfDD(oDD)) {
21967                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21968                         var loc = this.getLocation(oDD);
21969                         if (loc) {
21970                             this.locationCache[oDD.id] = loc;
21971                         } else {
21972                             delete this.locationCache[oDD.id];
21973                             // this will unregister the drag and drop object if
21974                             // the element is not in a usable state
21975                             // oDD.unreg();
21976                         }
21977                     }
21978                 }
21979             }
21980         },
21981
21982         /**
21983          * This checks to make sure an element exists and is in the DOM.  The
21984          * main purpose is to handle cases where innerHTML is used to remove
21985          * drag and drop objects from the DOM.  IE provides an 'unspecified
21986          * error' when trying to access the offsetParent of such an element
21987          * @method verifyEl
21988          * @param {HTMLElement} el the element to check
21989          * @return {boolean} true if the element looks usable
21990          * @static
21991          */
21992         verifyEl: function(el) {
21993             if (el) {
21994                 var parent;
21995                 if(Roo.isIE){
21996                     try{
21997                         parent = el.offsetParent;
21998                     }catch(e){}
21999                 }else{
22000                     parent = el.offsetParent;
22001                 }
22002                 if (parent) {
22003                     return true;
22004                 }
22005             }
22006
22007             return false;
22008         },
22009
22010         /**
22011          * Returns a Region object containing the drag and drop element's position
22012          * and size, including the padding configured for it
22013          * @method getLocation
22014          * @param {DragDrop} oDD the drag and drop object to get the
22015          *                       location for
22016          * @return {Roo.lib.Region} a Region object representing the total area
22017          *                             the element occupies, including any padding
22018          *                             the instance is configured for.
22019          * @static
22020          */
22021         getLocation: function(oDD) {
22022             if (! this.isTypeOfDD(oDD)) {
22023                 return null;
22024             }
22025
22026             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22027
22028             try {
22029                 pos= Roo.lib.Dom.getXY(el);
22030             } catch (e) { }
22031
22032             if (!pos) {
22033                 return null;
22034             }
22035
22036             x1 = pos[0];
22037             x2 = x1 + el.offsetWidth;
22038             y1 = pos[1];
22039             y2 = y1 + el.offsetHeight;
22040
22041             t = y1 - oDD.padding[0];
22042             r = x2 + oDD.padding[1];
22043             b = y2 + oDD.padding[2];
22044             l = x1 - oDD.padding[3];
22045
22046             return new Roo.lib.Region( t, r, b, l );
22047         },
22048
22049         /**
22050          * Checks the cursor location to see if it over the target
22051          * @method isOverTarget
22052          * @param {Roo.lib.Point} pt The point to evaluate
22053          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22054          * @return {boolean} true if the mouse is over the target
22055          * @private
22056          * @static
22057          */
22058         isOverTarget: function(pt, oTarget, intersect) {
22059             // use cache if available
22060             var loc = this.locationCache[oTarget.id];
22061             if (!loc || !this.useCache) {
22062                 loc = this.getLocation(oTarget);
22063                 this.locationCache[oTarget.id] = loc;
22064
22065             }
22066
22067             if (!loc) {
22068                 return false;
22069             }
22070
22071             oTarget.cursorIsOver = loc.contains( pt );
22072
22073             // DragDrop is using this as a sanity check for the initial mousedown
22074             // in this case we are done.  In POINT mode, if the drag obj has no
22075             // contraints, we are also done. Otherwise we need to evaluate the
22076             // location of the target as related to the actual location of the
22077             // dragged element.
22078             var dc = this.dragCurrent;
22079             if (!dc || !dc.getTargetCoord ||
22080                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22081                 return oTarget.cursorIsOver;
22082             }
22083
22084             oTarget.overlap = null;
22085
22086             // Get the current location of the drag element, this is the
22087             // location of the mouse event less the delta that represents
22088             // where the original mousedown happened on the element.  We
22089             // need to consider constraints and ticks as well.
22090             var pos = dc.getTargetCoord(pt.x, pt.y);
22091
22092             var el = dc.getDragEl();
22093             var curRegion = new Roo.lib.Region( pos.y,
22094                                                    pos.x + el.offsetWidth,
22095                                                    pos.y + el.offsetHeight,
22096                                                    pos.x );
22097
22098             var overlap = curRegion.intersect(loc);
22099
22100             if (overlap) {
22101                 oTarget.overlap = overlap;
22102                 return (intersect) ? true : oTarget.cursorIsOver;
22103             } else {
22104                 return false;
22105             }
22106         },
22107
22108         /**
22109          * unload event handler
22110          * @method _onUnload
22111          * @private
22112          * @static
22113          */
22114         _onUnload: function(e, me) {
22115             Roo.dd.DragDropMgr.unregAll();
22116         },
22117
22118         /**
22119          * Cleans up the drag and drop events and objects.
22120          * @method unregAll
22121          * @private
22122          * @static
22123          */
22124         unregAll: function() {
22125
22126             if (this.dragCurrent) {
22127                 this.stopDrag();
22128                 this.dragCurrent = null;
22129             }
22130
22131             this._execOnAll("unreg", []);
22132
22133             for (i in this.elementCache) {
22134                 delete this.elementCache[i];
22135             }
22136
22137             this.elementCache = {};
22138             this.ids = {};
22139         },
22140
22141         /**
22142          * A cache of DOM elements
22143          * @property elementCache
22144          * @private
22145          * @static
22146          */
22147         elementCache: {},
22148
22149         /**
22150          * Get the wrapper for the DOM element specified
22151          * @method getElWrapper
22152          * @param {String} id the id of the element to get
22153          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22154          * @private
22155          * @deprecated This wrapper isn't that useful
22156          * @static
22157          */
22158         getElWrapper: function(id) {
22159             var oWrapper = this.elementCache[id];
22160             if (!oWrapper || !oWrapper.el) {
22161                 oWrapper = this.elementCache[id] =
22162                     new this.ElementWrapper(Roo.getDom(id));
22163             }
22164             return oWrapper;
22165         },
22166
22167         /**
22168          * Returns the actual DOM element
22169          * @method getElement
22170          * @param {String} id the id of the elment to get
22171          * @return {Object} The element
22172          * @deprecated use Roo.getDom instead
22173          * @static
22174          */
22175         getElement: function(id) {
22176             return Roo.getDom(id);
22177         },
22178
22179         /**
22180          * Returns the style property for the DOM element (i.e.,
22181          * document.getElById(id).style)
22182          * @method getCss
22183          * @param {String} id the id of the elment to get
22184          * @return {Object} The style property of the element
22185          * @deprecated use Roo.getDom instead
22186          * @static
22187          */
22188         getCss: function(id) {
22189             var el = Roo.getDom(id);
22190             return (el) ? el.style : null;
22191         },
22192
22193         /**
22194          * Inner class for cached elements
22195          * @class DragDropMgr.ElementWrapper
22196          * @for DragDropMgr
22197          * @private
22198          * @deprecated
22199          */
22200         ElementWrapper: function(el) {
22201                 /**
22202                  * The element
22203                  * @property el
22204                  */
22205                 this.el = el || null;
22206                 /**
22207                  * The element id
22208                  * @property id
22209                  */
22210                 this.id = this.el && el.id;
22211                 /**
22212                  * A reference to the style property
22213                  * @property css
22214                  */
22215                 this.css = this.el && el.style;
22216             },
22217
22218         /**
22219          * Returns the X position of an html element
22220          * @method getPosX
22221          * @param el the element for which to get the position
22222          * @return {int} the X coordinate
22223          * @for DragDropMgr
22224          * @deprecated use Roo.lib.Dom.getX instead
22225          * @static
22226          */
22227         getPosX: function(el) {
22228             return Roo.lib.Dom.getX(el);
22229         },
22230
22231         /**
22232          * Returns the Y position of an html element
22233          * @method getPosY
22234          * @param el the element for which to get the position
22235          * @return {int} the Y coordinate
22236          * @deprecated use Roo.lib.Dom.getY instead
22237          * @static
22238          */
22239         getPosY: function(el) {
22240             return Roo.lib.Dom.getY(el);
22241         },
22242
22243         /**
22244          * Swap two nodes.  In IE, we use the native method, for others we
22245          * emulate the IE behavior
22246          * @method swapNode
22247          * @param n1 the first node to swap
22248          * @param n2 the other node to swap
22249          * @static
22250          */
22251         swapNode: function(n1, n2) {
22252             if (n1.swapNode) {
22253                 n1.swapNode(n2);
22254             } else {
22255                 var p = n2.parentNode;
22256                 var s = n2.nextSibling;
22257
22258                 if (s == n1) {
22259                     p.insertBefore(n1, n2);
22260                 } else if (n2 == n1.nextSibling) {
22261                     p.insertBefore(n2, n1);
22262                 } else {
22263                     n1.parentNode.replaceChild(n2, n1);
22264                     p.insertBefore(n1, s);
22265                 }
22266             }
22267         },
22268
22269         /**
22270          * Returns the current scroll position
22271          * @method getScroll
22272          * @private
22273          * @static
22274          */
22275         getScroll: function () {
22276             var t, l, dde=document.documentElement, db=document.body;
22277             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22278                 t = dde.scrollTop;
22279                 l = dde.scrollLeft;
22280             } else if (db) {
22281                 t = db.scrollTop;
22282                 l = db.scrollLeft;
22283             } else {
22284
22285             }
22286             return { top: t, left: l };
22287         },
22288
22289         /**
22290          * Returns the specified element style property
22291          * @method getStyle
22292          * @param {HTMLElement} el          the element
22293          * @param {string}      styleProp   the style property
22294          * @return {string} The value of the style property
22295          * @deprecated use Roo.lib.Dom.getStyle
22296          * @static
22297          */
22298         getStyle: function(el, styleProp) {
22299             return Roo.fly(el).getStyle(styleProp);
22300         },
22301
22302         /**
22303          * Gets the scrollTop
22304          * @method getScrollTop
22305          * @return {int} the document's scrollTop
22306          * @static
22307          */
22308         getScrollTop: function () { return this.getScroll().top; },
22309
22310         /**
22311          * Gets the scrollLeft
22312          * @method getScrollLeft
22313          * @return {int} the document's scrollTop
22314          * @static
22315          */
22316         getScrollLeft: function () { return this.getScroll().left; },
22317
22318         /**
22319          * Sets the x/y position of an element to the location of the
22320          * target element.
22321          * @method moveToEl
22322          * @param {HTMLElement} moveEl      The element to move
22323          * @param {HTMLElement} targetEl    The position reference element
22324          * @static
22325          */
22326         moveToEl: function (moveEl, targetEl) {
22327             var aCoord = Roo.lib.Dom.getXY(targetEl);
22328             Roo.lib.Dom.setXY(moveEl, aCoord);
22329         },
22330
22331         /**
22332          * Numeric array sort function
22333          * @method numericSort
22334          * @static
22335          */
22336         numericSort: function(a, b) { return (a - b); },
22337
22338         /**
22339          * Internal counter
22340          * @property _timeoutCount
22341          * @private
22342          * @static
22343          */
22344         _timeoutCount: 0,
22345
22346         /**
22347          * Trying to make the load order less important.  Without this we get
22348          * an error if this file is loaded before the Event Utility.
22349          * @method _addListeners
22350          * @private
22351          * @static
22352          */
22353         _addListeners: function() {
22354             var DDM = Roo.dd.DDM;
22355             if ( Roo.lib.Event && document ) {
22356                 DDM._onLoad();
22357             } else {
22358                 if (DDM._timeoutCount > 2000) {
22359                 } else {
22360                     setTimeout(DDM._addListeners, 10);
22361                     if (document && document.body) {
22362                         DDM._timeoutCount += 1;
22363                     }
22364                 }
22365             }
22366         },
22367
22368         /**
22369          * Recursively searches the immediate parent and all child nodes for
22370          * the handle element in order to determine wheter or not it was
22371          * clicked.
22372          * @method handleWasClicked
22373          * @param node the html element to inspect
22374          * @static
22375          */
22376         handleWasClicked: function(node, id) {
22377             if (this.isHandle(id, node.id)) {
22378                 return true;
22379             } else {
22380                 // check to see if this is a text node child of the one we want
22381                 var p = node.parentNode;
22382
22383                 while (p) {
22384                     if (this.isHandle(id, p.id)) {
22385                         return true;
22386                     } else {
22387                         p = p.parentNode;
22388                     }
22389                 }
22390             }
22391
22392             return false;
22393         }
22394
22395     };
22396
22397 }();
22398
22399 // shorter alias, save a few bytes
22400 Roo.dd.DDM = Roo.dd.DragDropMgr;
22401 Roo.dd.DDM._addListeners();
22402
22403 }/*
22404  * Based on:
22405  * Ext JS Library 1.1.1
22406  * Copyright(c) 2006-2007, Ext JS, LLC.
22407  *
22408  * Originally Released Under LGPL - original licence link has changed is not relivant.
22409  *
22410  * Fork - LGPL
22411  * <script type="text/javascript">
22412  */
22413
22414 /**
22415  * @class Roo.dd.DD
22416  * A DragDrop implementation where the linked element follows the
22417  * mouse cursor during a drag.
22418  * @extends Roo.dd.DragDrop
22419  * @constructor
22420  * @param {String} id the id of the linked element
22421  * @param {String} sGroup the group of related DragDrop items
22422  * @param {object} config an object containing configurable attributes
22423  *                Valid properties for DD:
22424  *                    scroll
22425  */
22426 Roo.dd.DD = function(id, sGroup, config) {
22427     if (id) {
22428         this.init(id, sGroup, config);
22429     }
22430 };
22431
22432 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22433
22434     /**
22435      * When set to true, the utility automatically tries to scroll the browser
22436      * window wehn a drag and drop element is dragged near the viewport boundary.
22437      * Defaults to true.
22438      * @property scroll
22439      * @type boolean
22440      */
22441     scroll: true,
22442
22443     /**
22444      * Sets the pointer offset to the distance between the linked element's top
22445      * left corner and the location the element was clicked
22446      * @method autoOffset
22447      * @param {int} iPageX the X coordinate of the click
22448      * @param {int} iPageY the Y coordinate of the click
22449      */
22450     autoOffset: function(iPageX, iPageY) {
22451         var x = iPageX - this.startPageX;
22452         var y = iPageY - this.startPageY;
22453         this.setDelta(x, y);
22454     },
22455
22456     /**
22457      * Sets the pointer offset.  You can call this directly to force the
22458      * offset to be in a particular location (e.g., pass in 0,0 to set it
22459      * to the center of the object)
22460      * @method setDelta
22461      * @param {int} iDeltaX the distance from the left
22462      * @param {int} iDeltaY the distance from the top
22463      */
22464     setDelta: function(iDeltaX, iDeltaY) {
22465         this.deltaX = iDeltaX;
22466         this.deltaY = iDeltaY;
22467     },
22468
22469     /**
22470      * Sets the drag element to the location of the mousedown or click event,
22471      * maintaining the cursor location relative to the location on the element
22472      * that was clicked.  Override this if you want to place the element in a
22473      * location other than where the cursor is.
22474      * @method setDragElPos
22475      * @param {int} iPageX the X coordinate of the mousedown or drag event
22476      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22477      */
22478     setDragElPos: function(iPageX, iPageY) {
22479         // the first time we do this, we are going to check to make sure
22480         // the element has css positioning
22481
22482         var el = this.getDragEl();
22483         this.alignElWithMouse(el, iPageX, iPageY);
22484     },
22485
22486     /**
22487      * Sets the element to the location of the mousedown or click event,
22488      * maintaining the cursor location relative to the location on the element
22489      * that was clicked.  Override this if you want to place the element in a
22490      * location other than where the cursor is.
22491      * @method alignElWithMouse
22492      * @param {HTMLElement} el the element to move
22493      * @param {int} iPageX the X coordinate of the mousedown or drag event
22494      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22495      */
22496     alignElWithMouse: function(el, iPageX, iPageY) {
22497         var oCoord = this.getTargetCoord(iPageX, iPageY);
22498         var fly = el.dom ? el : Roo.fly(el);
22499         if (!this.deltaSetXY) {
22500             var aCoord = [oCoord.x, oCoord.y];
22501             fly.setXY(aCoord);
22502             var newLeft = fly.getLeft(true);
22503             var newTop  = fly.getTop(true);
22504             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22505         } else {
22506             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22507         }
22508
22509         this.cachePosition(oCoord.x, oCoord.y);
22510         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22511         return oCoord;
22512     },
22513
22514     /**
22515      * Saves the most recent position so that we can reset the constraints and
22516      * tick marks on-demand.  We need to know this so that we can calculate the
22517      * number of pixels the element is offset from its original position.
22518      * @method cachePosition
22519      * @param iPageX the current x position (optional, this just makes it so we
22520      * don't have to look it up again)
22521      * @param iPageY the current y position (optional, this just makes it so we
22522      * don't have to look it up again)
22523      */
22524     cachePosition: function(iPageX, iPageY) {
22525         if (iPageX) {
22526             this.lastPageX = iPageX;
22527             this.lastPageY = iPageY;
22528         } else {
22529             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22530             this.lastPageX = aCoord[0];
22531             this.lastPageY = aCoord[1];
22532         }
22533     },
22534
22535     /**
22536      * Auto-scroll the window if the dragged object has been moved beyond the
22537      * visible window boundary.
22538      * @method autoScroll
22539      * @param {int} x the drag element's x position
22540      * @param {int} y the drag element's y position
22541      * @param {int} h the height of the drag element
22542      * @param {int} w the width of the drag element
22543      * @private
22544      */
22545     autoScroll: function(x, y, h, w) {
22546
22547         if (this.scroll) {
22548             // The client height
22549             var clientH = Roo.lib.Dom.getViewWidth();
22550
22551             // The client width
22552             var clientW = Roo.lib.Dom.getViewHeight();
22553
22554             // The amt scrolled down
22555             var st = this.DDM.getScrollTop();
22556
22557             // The amt scrolled right
22558             var sl = this.DDM.getScrollLeft();
22559
22560             // Location of the bottom of the element
22561             var bot = h + y;
22562
22563             // Location of the right of the element
22564             var right = w + x;
22565
22566             // The distance from the cursor to the bottom of the visible area,
22567             // adjusted so that we don't scroll if the cursor is beyond the
22568             // element drag constraints
22569             var toBot = (clientH + st - y - this.deltaY);
22570
22571             // The distance from the cursor to the right of the visible area
22572             var toRight = (clientW + sl - x - this.deltaX);
22573
22574
22575             // How close to the edge the cursor must be before we scroll
22576             // var thresh = (document.all) ? 100 : 40;
22577             var thresh = 40;
22578
22579             // How many pixels to scroll per autoscroll op.  This helps to reduce
22580             // clunky scrolling. IE is more sensitive about this ... it needs this
22581             // value to be higher.
22582             var scrAmt = (document.all) ? 80 : 30;
22583
22584             // Scroll down if we are near the bottom of the visible page and the
22585             // obj extends below the crease
22586             if ( bot > clientH && toBot < thresh ) {
22587                 window.scrollTo(sl, st + scrAmt);
22588             }
22589
22590             // Scroll up if the window is scrolled down and the top of the object
22591             // goes above the top border
22592             if ( y < st && st > 0 && y - st < thresh ) {
22593                 window.scrollTo(sl, st - scrAmt);
22594             }
22595
22596             // Scroll right if the obj is beyond the right border and the cursor is
22597             // near the border.
22598             if ( right > clientW && toRight < thresh ) {
22599                 window.scrollTo(sl + scrAmt, st);
22600             }
22601
22602             // Scroll left if the window has been scrolled to the right and the obj
22603             // extends past the left border
22604             if ( x < sl && sl > 0 && x - sl < thresh ) {
22605                 window.scrollTo(sl - scrAmt, st);
22606             }
22607         }
22608     },
22609
22610     /**
22611      * Finds the location the element should be placed if we want to move
22612      * it to where the mouse location less the click offset would place us.
22613      * @method getTargetCoord
22614      * @param {int} iPageX the X coordinate of the click
22615      * @param {int} iPageY the Y coordinate of the click
22616      * @return an object that contains the coordinates (Object.x and Object.y)
22617      * @private
22618      */
22619     getTargetCoord: function(iPageX, iPageY) {
22620
22621
22622         var x = iPageX - this.deltaX;
22623         var y = iPageY - this.deltaY;
22624
22625         if (this.constrainX) {
22626             if (x < this.minX) { x = this.minX; }
22627             if (x > this.maxX) { x = this.maxX; }
22628         }
22629
22630         if (this.constrainY) {
22631             if (y < this.minY) { y = this.minY; }
22632             if (y > this.maxY) { y = this.maxY; }
22633         }
22634
22635         x = this.getTick(x, this.xTicks);
22636         y = this.getTick(y, this.yTicks);
22637
22638
22639         return {x:x, y:y};
22640     },
22641
22642     /*
22643      * Sets up config options specific to this class. Overrides
22644      * Roo.dd.DragDrop, but all versions of this method through the
22645      * inheritance chain are called
22646      */
22647     applyConfig: function() {
22648         Roo.dd.DD.superclass.applyConfig.call(this);
22649         this.scroll = (this.config.scroll !== false);
22650     },
22651
22652     /*
22653      * Event that fires prior to the onMouseDown event.  Overrides
22654      * Roo.dd.DragDrop.
22655      */
22656     b4MouseDown: function(e) {
22657         // this.resetConstraints();
22658         this.autoOffset(e.getPageX(),
22659                             e.getPageY());
22660     },
22661
22662     /*
22663      * Event that fires prior to the onDrag event.  Overrides
22664      * Roo.dd.DragDrop.
22665      */
22666     b4Drag: function(e) {
22667         this.setDragElPos(e.getPageX(),
22668                             e.getPageY());
22669     },
22670
22671     toString: function() {
22672         return ("DD " + this.id);
22673     }
22674
22675     //////////////////////////////////////////////////////////////////////////
22676     // Debugging ygDragDrop events that can be overridden
22677     //////////////////////////////////////////////////////////////////////////
22678     /*
22679     startDrag: function(x, y) {
22680     },
22681
22682     onDrag: function(e) {
22683     },
22684
22685     onDragEnter: function(e, id) {
22686     },
22687
22688     onDragOver: function(e, id) {
22689     },
22690
22691     onDragOut: function(e, id) {
22692     },
22693
22694     onDragDrop: function(e, id) {
22695     },
22696
22697     endDrag: function(e) {
22698     }
22699
22700     */
22701
22702 });/*
22703  * Based on:
22704  * Ext JS Library 1.1.1
22705  * Copyright(c) 2006-2007, Ext JS, LLC.
22706  *
22707  * Originally Released Under LGPL - original licence link has changed is not relivant.
22708  *
22709  * Fork - LGPL
22710  * <script type="text/javascript">
22711  */
22712
22713 /**
22714  * @class Roo.dd.DDProxy
22715  * A DragDrop implementation that inserts an empty, bordered div into
22716  * the document that follows the cursor during drag operations.  At the time of
22717  * the click, the frame div is resized to the dimensions of the linked html
22718  * element, and moved to the exact location of the linked element.
22719  *
22720  * References to the "frame" element refer to the single proxy element that
22721  * was created to be dragged in place of all DDProxy elements on the
22722  * page.
22723  *
22724  * @extends Roo.dd.DD
22725  * @constructor
22726  * @param {String} id the id of the linked html element
22727  * @param {String} sGroup the group of related DragDrop objects
22728  * @param {object} config an object containing configurable attributes
22729  *                Valid properties for DDProxy in addition to those in DragDrop:
22730  *                   resizeFrame, centerFrame, dragElId
22731  */
22732 Roo.dd.DDProxy = function(id, sGroup, config) {
22733     if (id) {
22734         this.init(id, sGroup, config);
22735         this.initFrame();
22736     }
22737 };
22738
22739 /**
22740  * The default drag frame div id
22741  * @property Roo.dd.DDProxy.dragElId
22742  * @type String
22743  * @static
22744  */
22745 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22746
22747 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22748
22749     /**
22750      * By default we resize the drag frame to be the same size as the element
22751      * we want to drag (this is to get the frame effect).  We can turn it off
22752      * if we want a different behavior.
22753      * @property resizeFrame
22754      * @type boolean
22755      */
22756     resizeFrame: true,
22757
22758     /**
22759      * By default the frame is positioned exactly where the drag element is, so
22760      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22761      * you do not have constraints on the obj is to have the drag frame centered
22762      * around the cursor.  Set centerFrame to true for this effect.
22763      * @property centerFrame
22764      * @type boolean
22765      */
22766     centerFrame: false,
22767
22768     /**
22769      * Creates the proxy element if it does not yet exist
22770      * @method createFrame
22771      */
22772     createFrame: function() {
22773         var self = this;
22774         var body = document.body;
22775
22776         if (!body || !body.firstChild) {
22777             setTimeout( function() { self.createFrame(); }, 50 );
22778             return;
22779         }
22780
22781         var div = this.getDragEl();
22782
22783         if (!div) {
22784             div    = document.createElement("div");
22785             div.id = this.dragElId;
22786             var s  = div.style;
22787
22788             s.position   = "absolute";
22789             s.visibility = "hidden";
22790             s.cursor     = "move";
22791             s.border     = "2px solid #aaa";
22792             s.zIndex     = 999;
22793
22794             // appendChild can blow up IE if invoked prior to the window load event
22795             // while rendering a table.  It is possible there are other scenarios
22796             // that would cause this to happen as well.
22797             body.insertBefore(div, body.firstChild);
22798         }
22799     },
22800
22801     /**
22802      * Initialization for the drag frame element.  Must be called in the
22803      * constructor of all subclasses
22804      * @method initFrame
22805      */
22806     initFrame: function() {
22807         this.createFrame();
22808     },
22809
22810     applyConfig: function() {
22811         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22812
22813         this.resizeFrame = (this.config.resizeFrame !== false);
22814         this.centerFrame = (this.config.centerFrame);
22815         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22816     },
22817
22818     /**
22819      * Resizes the drag frame to the dimensions of the clicked object, positions
22820      * it over the object, and finally displays it
22821      * @method showFrame
22822      * @param {int} iPageX X click position
22823      * @param {int} iPageY Y click position
22824      * @private
22825      */
22826     showFrame: function(iPageX, iPageY) {
22827         var el = this.getEl();
22828         var dragEl = this.getDragEl();
22829         var s = dragEl.style;
22830
22831         this._resizeProxy();
22832
22833         if (this.centerFrame) {
22834             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22835                            Math.round(parseInt(s.height, 10)/2) );
22836         }
22837
22838         this.setDragElPos(iPageX, iPageY);
22839
22840         Roo.fly(dragEl).show();
22841     },
22842
22843     /**
22844      * The proxy is automatically resized to the dimensions of the linked
22845      * element when a drag is initiated, unless resizeFrame is set to false
22846      * @method _resizeProxy
22847      * @private
22848      */
22849     _resizeProxy: function() {
22850         if (this.resizeFrame) {
22851             var el = this.getEl();
22852             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22853         }
22854     },
22855
22856     // overrides Roo.dd.DragDrop
22857     b4MouseDown: function(e) {
22858         var x = e.getPageX();
22859         var y = e.getPageY();
22860         this.autoOffset(x, y);
22861         this.setDragElPos(x, y);
22862     },
22863
22864     // overrides Roo.dd.DragDrop
22865     b4StartDrag: function(x, y) {
22866         // show the drag frame
22867         this.showFrame(x, y);
22868     },
22869
22870     // overrides Roo.dd.DragDrop
22871     b4EndDrag: function(e) {
22872         Roo.fly(this.getDragEl()).hide();
22873     },
22874
22875     // overrides Roo.dd.DragDrop
22876     // By default we try to move the element to the last location of the frame.
22877     // This is so that the default behavior mirrors that of Roo.dd.DD.
22878     endDrag: function(e) {
22879
22880         var lel = this.getEl();
22881         var del = this.getDragEl();
22882
22883         // Show the drag frame briefly so we can get its position
22884         del.style.visibility = "";
22885
22886         this.beforeMove();
22887         // Hide the linked element before the move to get around a Safari
22888         // rendering bug.
22889         lel.style.visibility = "hidden";
22890         Roo.dd.DDM.moveToEl(lel, del);
22891         del.style.visibility = "hidden";
22892         lel.style.visibility = "";
22893
22894         this.afterDrag();
22895     },
22896
22897     beforeMove : function(){
22898
22899     },
22900
22901     afterDrag : function(){
22902
22903     },
22904
22905     toString: function() {
22906         return ("DDProxy " + this.id);
22907     }
22908
22909 });
22910 /*
22911  * Based on:
22912  * Ext JS Library 1.1.1
22913  * Copyright(c) 2006-2007, Ext JS, LLC.
22914  *
22915  * Originally Released Under LGPL - original licence link has changed is not relivant.
22916  *
22917  * Fork - LGPL
22918  * <script type="text/javascript">
22919  */
22920
22921  /**
22922  * @class Roo.dd.DDTarget
22923  * A DragDrop implementation that does not move, but can be a drop
22924  * target.  You would get the same result by simply omitting implementation
22925  * for the event callbacks, but this way we reduce the processing cost of the
22926  * event listener and the callbacks.
22927  * @extends Roo.dd.DragDrop
22928  * @constructor
22929  * @param {String} id the id of the element that is a drop target
22930  * @param {String} sGroup the group of related DragDrop objects
22931  * @param {object} config an object containing configurable attributes
22932  *                 Valid properties for DDTarget in addition to those in
22933  *                 DragDrop:
22934  *                    none
22935  */
22936 Roo.dd.DDTarget = function(id, sGroup, config) {
22937     if (id) {
22938         this.initTarget(id, sGroup, config);
22939     }
22940     if (config && (config.listeners || config.events)) { 
22941         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22942             listeners : config.listeners || {}, 
22943             events : config.events || {} 
22944         });    
22945     }
22946 };
22947
22948 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22949 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22950     toString: function() {
22951         return ("DDTarget " + this.id);
22952     }
22953 });
22954 /*
22955  * Based on:
22956  * Ext JS Library 1.1.1
22957  * Copyright(c) 2006-2007, Ext JS, LLC.
22958  *
22959  * Originally Released Under LGPL - original licence link has changed is not relivant.
22960  *
22961  * Fork - LGPL
22962  * <script type="text/javascript">
22963  */
22964  
22965
22966 /**
22967  * @class Roo.dd.ScrollManager
22968  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22969  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22970  * @static
22971  */
22972 Roo.dd.ScrollManager = function(){
22973     var ddm = Roo.dd.DragDropMgr;
22974     var els = {};
22975     var dragEl = null;
22976     var proc = {};
22977     
22978     
22979     
22980     var onStop = function(e){
22981         dragEl = null;
22982         clearProc();
22983     };
22984     
22985     var triggerRefresh = function(){
22986         if(ddm.dragCurrent){
22987              ddm.refreshCache(ddm.dragCurrent.groups);
22988         }
22989     };
22990     
22991     var doScroll = function(){
22992         if(ddm.dragCurrent){
22993             var dds = Roo.dd.ScrollManager;
22994             if(!dds.animate){
22995                 if(proc.el.scroll(proc.dir, dds.increment)){
22996                     triggerRefresh();
22997                 }
22998             }else{
22999                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23000             }
23001         }
23002     };
23003     
23004     var clearProc = function(){
23005         if(proc.id){
23006             clearInterval(proc.id);
23007         }
23008         proc.id = 0;
23009         proc.el = null;
23010         proc.dir = "";
23011     };
23012     
23013     var startProc = function(el, dir){
23014          Roo.log('scroll startproc');
23015         clearProc();
23016         proc.el = el;
23017         proc.dir = dir;
23018         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23019     };
23020     
23021     var onFire = function(e, isDrop){
23022        
23023         if(isDrop || !ddm.dragCurrent){ return; }
23024         var dds = Roo.dd.ScrollManager;
23025         if(!dragEl || dragEl != ddm.dragCurrent){
23026             dragEl = ddm.dragCurrent;
23027             // refresh regions on drag start
23028             dds.refreshCache();
23029         }
23030         
23031         var xy = Roo.lib.Event.getXY(e);
23032         var pt = new Roo.lib.Point(xy[0], xy[1]);
23033         for(var id in els){
23034             var el = els[id], r = el._region;
23035             if(r && r.contains(pt) && el.isScrollable()){
23036                 if(r.bottom - pt.y <= dds.thresh){
23037                     if(proc.el != el){
23038                         startProc(el, "down");
23039                     }
23040                     return;
23041                 }else if(r.right - pt.x <= dds.thresh){
23042                     if(proc.el != el){
23043                         startProc(el, "left");
23044                     }
23045                     return;
23046                 }else if(pt.y - r.top <= dds.thresh){
23047                     if(proc.el != el){
23048                         startProc(el, "up");
23049                     }
23050                     return;
23051                 }else if(pt.x - r.left <= dds.thresh){
23052                     if(proc.el != el){
23053                         startProc(el, "right");
23054                     }
23055                     return;
23056                 }
23057             }
23058         }
23059         clearProc();
23060     };
23061     
23062     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23063     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23064     
23065     return {
23066         /**
23067          * Registers new overflow element(s) to auto scroll
23068          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23069          */
23070         register : function(el){
23071             if(el instanceof Array){
23072                 for(var i = 0, len = el.length; i < len; i++) {
23073                         this.register(el[i]);
23074                 }
23075             }else{
23076                 el = Roo.get(el);
23077                 els[el.id] = el;
23078             }
23079             Roo.dd.ScrollManager.els = els;
23080         },
23081         
23082         /**
23083          * Unregisters overflow element(s) so they are no longer scrolled
23084          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23085          */
23086         unregister : function(el){
23087             if(el instanceof Array){
23088                 for(var i = 0, len = el.length; i < len; i++) {
23089                         this.unregister(el[i]);
23090                 }
23091             }else{
23092                 el = Roo.get(el);
23093                 delete els[el.id];
23094             }
23095         },
23096         
23097         /**
23098          * The number of pixels from the edge of a container the pointer needs to be to 
23099          * trigger scrolling (defaults to 25)
23100          * @type Number
23101          */
23102         thresh : 25,
23103         
23104         /**
23105          * The number of pixels to scroll in each scroll increment (defaults to 50)
23106          * @type Number
23107          */
23108         increment : 100,
23109         
23110         /**
23111          * The frequency of scrolls in milliseconds (defaults to 500)
23112          * @type Number
23113          */
23114         frequency : 500,
23115         
23116         /**
23117          * True to animate the scroll (defaults to true)
23118          * @type Boolean
23119          */
23120         animate: true,
23121         
23122         /**
23123          * The animation duration in seconds - 
23124          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23125          * @type Number
23126          */
23127         animDuration: .4,
23128         
23129         /**
23130          * Manually trigger a cache refresh.
23131          */
23132         refreshCache : function(){
23133             for(var id in els){
23134                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23135                     els[id]._region = els[id].getRegion();
23136                 }
23137             }
23138         }
23139     };
23140 }();/*
23141  * Based on:
23142  * Ext JS Library 1.1.1
23143  * Copyright(c) 2006-2007, Ext JS, LLC.
23144  *
23145  * Originally Released Under LGPL - original licence link has changed is not relivant.
23146  *
23147  * Fork - LGPL
23148  * <script type="text/javascript">
23149  */
23150  
23151
23152 /**
23153  * @class Roo.dd.Registry
23154  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23155  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23156  * @static
23157  */
23158 Roo.dd.Registry = function(){
23159     var elements = {}; 
23160     var handles = {}; 
23161     var autoIdSeed = 0;
23162
23163     var getId = function(el, autogen){
23164         if(typeof el == "string"){
23165             return el;
23166         }
23167         var id = el.id;
23168         if(!id && autogen !== false){
23169             id = "roodd-" + (++autoIdSeed);
23170             el.id = id;
23171         }
23172         return id;
23173     };
23174     
23175     return {
23176     /**
23177      * Register a drag drop element
23178      * @param {String|HTMLElement} element The id or DOM node to register
23179      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23180      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23181      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23182      * populated in the data object (if applicable):
23183      * <pre>
23184 Value      Description<br />
23185 ---------  ------------------------------------------<br />
23186 handles    Array of DOM nodes that trigger dragging<br />
23187            for the element being registered<br />
23188 isHandle   True if the element passed in triggers<br />
23189            dragging itself, else false
23190 </pre>
23191      */
23192         register : function(el, data){
23193             data = data || {};
23194             if(typeof el == "string"){
23195                 el = document.getElementById(el);
23196             }
23197             data.ddel = el;
23198             elements[getId(el)] = data;
23199             if(data.isHandle !== false){
23200                 handles[data.ddel.id] = data;
23201             }
23202             if(data.handles){
23203                 var hs = data.handles;
23204                 for(var i = 0, len = hs.length; i < len; i++){
23205                         handles[getId(hs[i])] = data;
23206                 }
23207             }
23208         },
23209
23210     /**
23211      * Unregister a drag drop element
23212      * @param {String|HTMLElement}  element The id or DOM node to unregister
23213      */
23214         unregister : function(el){
23215             var id = getId(el, false);
23216             var data = elements[id];
23217             if(data){
23218                 delete elements[id];
23219                 if(data.handles){
23220                     var hs = data.handles;
23221                     for(var i = 0, len = hs.length; i < len; i++){
23222                         delete handles[getId(hs[i], false)];
23223                     }
23224                 }
23225             }
23226         },
23227
23228     /**
23229      * Returns the handle registered for a DOM Node by id
23230      * @param {String|HTMLElement} id The DOM node or id to look up
23231      * @return {Object} handle The custom handle data
23232      */
23233         getHandle : function(id){
23234             if(typeof id != "string"){ // must be element?
23235                 id = id.id;
23236             }
23237             return handles[id];
23238         },
23239
23240     /**
23241      * Returns the handle that is registered for the DOM node that is the target of the event
23242      * @param {Event} e The event
23243      * @return {Object} handle The custom handle data
23244      */
23245         getHandleFromEvent : function(e){
23246             var t = Roo.lib.Event.getTarget(e);
23247             return t ? handles[t.id] : null;
23248         },
23249
23250     /**
23251      * Returns a custom data object that is registered for a DOM node by id
23252      * @param {String|HTMLElement} id The DOM node or id to look up
23253      * @return {Object} data The custom data
23254      */
23255         getTarget : function(id){
23256             if(typeof id != "string"){ // must be element?
23257                 id = id.id;
23258             }
23259             return elements[id];
23260         },
23261
23262     /**
23263      * Returns a custom data object that is registered for the DOM node that is the target of the event
23264      * @param {Event} e The event
23265      * @return {Object} data The custom data
23266      */
23267         getTargetFromEvent : function(e){
23268             var t = Roo.lib.Event.getTarget(e);
23269             return t ? elements[t.id] || handles[t.id] : null;
23270         }
23271     };
23272 }();/*
23273  * Based on:
23274  * Ext JS Library 1.1.1
23275  * Copyright(c) 2006-2007, Ext JS, LLC.
23276  *
23277  * Originally Released Under LGPL - original licence link has changed is not relivant.
23278  *
23279  * Fork - LGPL
23280  * <script type="text/javascript">
23281  */
23282  
23283
23284 /**
23285  * @class Roo.dd.StatusProxy
23286  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23287  * default drag proxy used by all Roo.dd components.
23288  * @constructor
23289  * @param {Object} config
23290  */
23291 Roo.dd.StatusProxy = function(config){
23292     Roo.apply(this, config);
23293     this.id = this.id || Roo.id();
23294     this.el = new Roo.Layer({
23295         dh: {
23296             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23297                 {tag: "div", cls: "x-dd-drop-icon"},
23298                 {tag: "div", cls: "x-dd-drag-ghost"}
23299             ]
23300         }, 
23301         shadow: !config || config.shadow !== false
23302     });
23303     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23304     this.dropStatus = this.dropNotAllowed;
23305 };
23306
23307 Roo.dd.StatusProxy.prototype = {
23308     /**
23309      * @cfg {String} dropAllowed
23310      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23311      */
23312     dropAllowed : "x-dd-drop-ok",
23313     /**
23314      * @cfg {String} dropNotAllowed
23315      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23316      */
23317     dropNotAllowed : "x-dd-drop-nodrop",
23318
23319     /**
23320      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23321      * over the current target element.
23322      * @param {String} cssClass The css class for the new drop status indicator image
23323      */
23324     setStatus : function(cssClass){
23325         cssClass = cssClass || this.dropNotAllowed;
23326         if(this.dropStatus != cssClass){
23327             this.el.replaceClass(this.dropStatus, cssClass);
23328             this.dropStatus = cssClass;
23329         }
23330     },
23331
23332     /**
23333      * Resets the status indicator to the default dropNotAllowed value
23334      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23335      */
23336     reset : function(clearGhost){
23337         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23338         this.dropStatus = this.dropNotAllowed;
23339         if(clearGhost){
23340             this.ghost.update("");
23341         }
23342     },
23343
23344     /**
23345      * Updates the contents of the ghost element
23346      * @param {String} html The html that will replace the current innerHTML of the ghost element
23347      */
23348     update : function(html){
23349         if(typeof html == "string"){
23350             this.ghost.update(html);
23351         }else{
23352             this.ghost.update("");
23353             html.style.margin = "0";
23354             this.ghost.dom.appendChild(html);
23355         }
23356         // ensure float = none set?? cant remember why though.
23357         var el = this.ghost.dom.firstChild;
23358                 if(el){
23359                         Roo.fly(el).setStyle('float', 'none');
23360                 }
23361     },
23362     
23363     /**
23364      * Returns the underlying proxy {@link Roo.Layer}
23365      * @return {Roo.Layer} el
23366     */
23367     getEl : function(){
23368         return this.el;
23369     },
23370
23371     /**
23372      * Returns the ghost element
23373      * @return {Roo.Element} el
23374      */
23375     getGhost : function(){
23376         return this.ghost;
23377     },
23378
23379     /**
23380      * Hides the proxy
23381      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23382      */
23383     hide : function(clear){
23384         this.el.hide();
23385         if(clear){
23386             this.reset(true);
23387         }
23388     },
23389
23390     /**
23391      * Stops the repair animation if it's currently running
23392      */
23393     stop : function(){
23394         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23395             this.anim.stop();
23396         }
23397     },
23398
23399     /**
23400      * Displays this proxy
23401      */
23402     show : function(){
23403         this.el.show();
23404     },
23405
23406     /**
23407      * Force the Layer to sync its shadow and shim positions to the element
23408      */
23409     sync : function(){
23410         this.el.sync();
23411     },
23412
23413     /**
23414      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23415      * invalid drop operation by the item being dragged.
23416      * @param {Array} xy The XY position of the element ([x, y])
23417      * @param {Function} callback The function to call after the repair is complete
23418      * @param {Object} scope The scope in which to execute the callback
23419      */
23420     repair : function(xy, callback, scope){
23421         this.callback = callback;
23422         this.scope = scope;
23423         if(xy && this.animRepair !== false){
23424             this.el.addClass("x-dd-drag-repair");
23425             this.el.hideUnders(true);
23426             this.anim = this.el.shift({
23427                 duration: this.repairDuration || .5,
23428                 easing: 'easeOut',
23429                 xy: xy,
23430                 stopFx: true,
23431                 callback: this.afterRepair,
23432                 scope: this
23433             });
23434         }else{
23435             this.afterRepair();
23436         }
23437     },
23438
23439     // private
23440     afterRepair : function(){
23441         this.hide(true);
23442         if(typeof this.callback == "function"){
23443             this.callback.call(this.scope || this);
23444         }
23445         this.callback = null;
23446         this.scope = null;
23447     }
23448 };/*
23449  * Based on:
23450  * Ext JS Library 1.1.1
23451  * Copyright(c) 2006-2007, Ext JS, LLC.
23452  *
23453  * Originally Released Under LGPL - original licence link has changed is not relivant.
23454  *
23455  * Fork - LGPL
23456  * <script type="text/javascript">
23457  */
23458
23459 /**
23460  * @class Roo.dd.DragSource
23461  * @extends Roo.dd.DDProxy
23462  * A simple class that provides the basic implementation needed to make any element draggable.
23463  * @constructor
23464  * @param {String/HTMLElement/Element} el The container element
23465  * @param {Object} config
23466  */
23467 Roo.dd.DragSource = function(el, config){
23468     this.el = Roo.get(el);
23469     this.dragData = {};
23470     
23471     Roo.apply(this, config);
23472     
23473     if(!this.proxy){
23474         this.proxy = new Roo.dd.StatusProxy();
23475     }
23476
23477     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23478           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23479     
23480     this.dragging = false;
23481 };
23482
23483 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23484     /**
23485      * @cfg {String} dropAllowed
23486      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23487      */
23488     dropAllowed : "x-dd-drop-ok",
23489     /**
23490      * @cfg {String} dropNotAllowed
23491      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23492      */
23493     dropNotAllowed : "x-dd-drop-nodrop",
23494
23495     /**
23496      * Returns the data object associated with this drag source
23497      * @return {Object} data An object containing arbitrary data
23498      */
23499     getDragData : function(e){
23500         return this.dragData;
23501     },
23502
23503     // private
23504     onDragEnter : function(e, id){
23505         var target = Roo.dd.DragDropMgr.getDDById(id);
23506         this.cachedTarget = target;
23507         if(this.beforeDragEnter(target, e, id) !== false){
23508             if(target.isNotifyTarget){
23509                 var status = target.notifyEnter(this, e, this.dragData);
23510                 this.proxy.setStatus(status);
23511             }else{
23512                 this.proxy.setStatus(this.dropAllowed);
23513             }
23514             
23515             if(this.afterDragEnter){
23516                 /**
23517                  * An empty function by default, but provided so that you can perform a custom action
23518                  * when the dragged item enters the drop target by providing an implementation.
23519                  * @param {Roo.dd.DragDrop} target The drop target
23520                  * @param {Event} e The event object
23521                  * @param {String} id The id of the dragged element
23522                  * @method afterDragEnter
23523                  */
23524                 this.afterDragEnter(target, e, id);
23525             }
23526         }
23527     },
23528
23529     /**
23530      * An empty function by default, but provided so that you can perform a custom action
23531      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23532      * @param {Roo.dd.DragDrop} target The drop target
23533      * @param {Event} e The event object
23534      * @param {String} id The id of the dragged element
23535      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23536      */
23537     beforeDragEnter : function(target, e, id){
23538         return true;
23539     },
23540
23541     // private
23542     alignElWithMouse: function() {
23543         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23544         this.proxy.sync();
23545     },
23546
23547     // private
23548     onDragOver : function(e, id){
23549         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23550         if(this.beforeDragOver(target, e, id) !== false){
23551             if(target.isNotifyTarget){
23552                 var status = target.notifyOver(this, e, this.dragData);
23553                 this.proxy.setStatus(status);
23554             }
23555
23556             if(this.afterDragOver){
23557                 /**
23558                  * An empty function by default, but provided so that you can perform a custom action
23559                  * while the dragged item is over the drop target by providing an implementation.
23560                  * @param {Roo.dd.DragDrop} target The drop target
23561                  * @param {Event} e The event object
23562                  * @param {String} id The id of the dragged element
23563                  * @method afterDragOver
23564                  */
23565                 this.afterDragOver(target, e, id);
23566             }
23567         }
23568     },
23569
23570     /**
23571      * An empty function by default, but provided so that you can perform a custom action
23572      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23573      * @param {Roo.dd.DragDrop} target The drop target
23574      * @param {Event} e The event object
23575      * @param {String} id The id of the dragged element
23576      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23577      */
23578     beforeDragOver : function(target, e, id){
23579         return true;
23580     },
23581
23582     // private
23583     onDragOut : function(e, id){
23584         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23585         if(this.beforeDragOut(target, e, id) !== false){
23586             if(target.isNotifyTarget){
23587                 target.notifyOut(this, e, this.dragData);
23588             }
23589             this.proxy.reset();
23590             if(this.afterDragOut){
23591                 /**
23592                  * An empty function by default, but provided so that you can perform a custom action
23593                  * after the dragged item is dragged out of the target without dropping.
23594                  * @param {Roo.dd.DragDrop} target The drop target
23595                  * @param {Event} e The event object
23596                  * @param {String} id The id of the dragged element
23597                  * @method afterDragOut
23598                  */
23599                 this.afterDragOut(target, e, id);
23600             }
23601         }
23602         this.cachedTarget = null;
23603     },
23604
23605     /**
23606      * An empty function by default, but provided so that you can perform a custom action before the dragged
23607      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23608      * @param {Roo.dd.DragDrop} target The drop target
23609      * @param {Event} e The event object
23610      * @param {String} id The id of the dragged element
23611      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23612      */
23613     beforeDragOut : function(target, e, id){
23614         return true;
23615     },
23616     
23617     // private
23618     onDragDrop : function(e, id){
23619         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23620         if(this.beforeDragDrop(target, e, id) !== false){
23621             if(target.isNotifyTarget){
23622                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23623                     this.onValidDrop(target, e, id);
23624                 }else{
23625                     this.onInvalidDrop(target, e, id);
23626                 }
23627             }else{
23628                 this.onValidDrop(target, e, id);
23629             }
23630             
23631             if(this.afterDragDrop){
23632                 /**
23633                  * An empty function by default, but provided so that you can perform a custom action
23634                  * after a valid drag drop has occurred by providing an implementation.
23635                  * @param {Roo.dd.DragDrop} target The drop target
23636                  * @param {Event} e The event object
23637                  * @param {String} id The id of the dropped element
23638                  * @method afterDragDrop
23639                  */
23640                 this.afterDragDrop(target, e, id);
23641             }
23642         }
23643         delete this.cachedTarget;
23644     },
23645
23646     /**
23647      * An empty function by default, but provided so that you can perform a custom action before the dragged
23648      * item is dropped onto the target and optionally cancel the onDragDrop.
23649      * @param {Roo.dd.DragDrop} target The drop target
23650      * @param {Event} e The event object
23651      * @param {String} id The id of the dragged element
23652      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23653      */
23654     beforeDragDrop : function(target, e, id){
23655         return true;
23656     },
23657
23658     // private
23659     onValidDrop : function(target, e, id){
23660         this.hideProxy();
23661         if(this.afterValidDrop){
23662             /**
23663              * An empty function by default, but provided so that you can perform a custom action
23664              * after a valid drop has occurred by providing an implementation.
23665              * @param {Object} target The target DD 
23666              * @param {Event} e The event object
23667              * @param {String} id The id of the dropped element
23668              * @method afterInvalidDrop
23669              */
23670             this.afterValidDrop(target, e, id);
23671         }
23672     },
23673
23674     // private
23675     getRepairXY : function(e, data){
23676         return this.el.getXY();  
23677     },
23678
23679     // private
23680     onInvalidDrop : function(target, e, id){
23681         this.beforeInvalidDrop(target, e, id);
23682         if(this.cachedTarget){
23683             if(this.cachedTarget.isNotifyTarget){
23684                 this.cachedTarget.notifyOut(this, e, this.dragData);
23685             }
23686             this.cacheTarget = null;
23687         }
23688         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23689
23690         if(this.afterInvalidDrop){
23691             /**
23692              * An empty function by default, but provided so that you can perform a custom action
23693              * after an invalid drop has occurred by providing an implementation.
23694              * @param {Event} e The event object
23695              * @param {String} id The id of the dropped element
23696              * @method afterInvalidDrop
23697              */
23698             this.afterInvalidDrop(e, id);
23699         }
23700     },
23701
23702     // private
23703     afterRepair : function(){
23704         if(Roo.enableFx){
23705             this.el.highlight(this.hlColor || "c3daf9");
23706         }
23707         this.dragging = false;
23708     },
23709
23710     /**
23711      * An empty function by default, but provided so that you can perform a custom action after an invalid
23712      * drop has occurred.
23713      * @param {Roo.dd.DragDrop} target The drop target
23714      * @param {Event} e The event object
23715      * @param {String} id The id of the dragged element
23716      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23717      */
23718     beforeInvalidDrop : function(target, e, id){
23719         return true;
23720     },
23721
23722     // private
23723     handleMouseDown : function(e){
23724         if(this.dragging) {
23725             return;
23726         }
23727         var data = this.getDragData(e);
23728         if(data && this.onBeforeDrag(data, e) !== false){
23729             this.dragData = data;
23730             this.proxy.stop();
23731             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23732         } 
23733     },
23734
23735     /**
23736      * An empty function by default, but provided so that you can perform a custom action before the initial
23737      * drag event begins and optionally cancel it.
23738      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23739      * @param {Event} e The event object
23740      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23741      */
23742     onBeforeDrag : function(data, e){
23743         return true;
23744     },
23745
23746     /**
23747      * An empty function by default, but provided so that you can perform a custom action once the initial
23748      * drag event has begun.  The drag cannot be canceled from this function.
23749      * @param {Number} x The x position of the click on the dragged object
23750      * @param {Number} y The y position of the click on the dragged object
23751      */
23752     onStartDrag : Roo.emptyFn,
23753
23754     // private - YUI override
23755     startDrag : function(x, y){
23756         this.proxy.reset();
23757         this.dragging = true;
23758         this.proxy.update("");
23759         this.onInitDrag(x, y);
23760         this.proxy.show();
23761     },
23762
23763     // private
23764     onInitDrag : function(x, y){
23765         var clone = this.el.dom.cloneNode(true);
23766         clone.id = Roo.id(); // prevent duplicate ids
23767         this.proxy.update(clone);
23768         this.onStartDrag(x, y);
23769         return true;
23770     },
23771
23772     /**
23773      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23774      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23775      */
23776     getProxy : function(){
23777         return this.proxy;  
23778     },
23779
23780     /**
23781      * Hides the drag source's {@link Roo.dd.StatusProxy}
23782      */
23783     hideProxy : function(){
23784         this.proxy.hide();  
23785         this.proxy.reset(true);
23786         this.dragging = false;
23787     },
23788
23789     // private
23790     triggerCacheRefresh : function(){
23791         Roo.dd.DDM.refreshCache(this.groups);
23792     },
23793
23794     // private - override to prevent hiding
23795     b4EndDrag: function(e) {
23796     },
23797
23798     // private - override to prevent moving
23799     endDrag : function(e){
23800         this.onEndDrag(this.dragData, e);
23801     },
23802
23803     // private
23804     onEndDrag : function(data, e){
23805     },
23806     
23807     // private - pin to cursor
23808     autoOffset : function(x, y) {
23809         this.setDelta(-12, -20);
23810     }    
23811 });/*
23812  * Based on:
23813  * Ext JS Library 1.1.1
23814  * Copyright(c) 2006-2007, Ext JS, LLC.
23815  *
23816  * Originally Released Under LGPL - original licence link has changed is not relivant.
23817  *
23818  * Fork - LGPL
23819  * <script type="text/javascript">
23820  */
23821
23822
23823 /**
23824  * @class Roo.dd.DropTarget
23825  * @extends Roo.dd.DDTarget
23826  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23827  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23828  * @constructor
23829  * @param {String/HTMLElement/Element} el The container element
23830  * @param {Object} config
23831  */
23832 Roo.dd.DropTarget = function(el, config){
23833     this.el = Roo.get(el);
23834     
23835     var listeners = false; ;
23836     if (config && config.listeners) {
23837         listeners= config.listeners;
23838         delete config.listeners;
23839     }
23840     Roo.apply(this, config);
23841     
23842     if(this.containerScroll){
23843         Roo.dd.ScrollManager.register(this.el);
23844     }
23845     this.addEvents( {
23846          /**
23847          * @scope Roo.dd.DropTarget
23848          */
23849          
23850          /**
23851          * @event enter
23852          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23853          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23854          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23855          * 
23856          * IMPORTANT : it should set  this.valid to true|false
23857          * 
23858          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23859          * @param {Event} e The event
23860          * @param {Object} data An object containing arbitrary data supplied by the drag source
23861          */
23862         "enter" : true,
23863         
23864          /**
23865          * @event over
23866          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23867          * This method will be called on every mouse movement while the drag source is over the drop target.
23868          * This default implementation simply returns the dropAllowed config value.
23869          * 
23870          * IMPORTANT : it should set  this.valid to true|false
23871          * 
23872          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23873          * @param {Event} e The event
23874          * @param {Object} data An object containing arbitrary data supplied by the drag source
23875          
23876          */
23877         "over" : true,
23878         /**
23879          * @event out
23880          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23881          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23882          * overClass (if any) from the drop element.
23883          * 
23884          * 
23885          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23886          * @param {Event} e The event
23887          * @param {Object} data An object containing arbitrary data supplied by the drag source
23888          */
23889          "out" : true,
23890          
23891         /**
23892          * @event drop
23893          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23894          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23895          * implementation that does something to process the drop event and returns true so that the drag source's
23896          * repair action does not run.
23897          * 
23898          * IMPORTANT : it should set this.success
23899          * 
23900          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23901          * @param {Event} e The event
23902          * @param {Object} data An object containing arbitrary data supplied by the drag source
23903         */
23904          "drop" : true
23905     });
23906             
23907      
23908     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23909         this.el.dom, 
23910         this.ddGroup || this.group,
23911         {
23912             isTarget: true,
23913             listeners : listeners || {} 
23914            
23915         
23916         }
23917     );
23918
23919 };
23920
23921 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23922     /**
23923      * @cfg {String} overClass
23924      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23925      */
23926      /**
23927      * @cfg {String} ddGroup
23928      * The drag drop group to handle drop events for
23929      */
23930      
23931     /**
23932      * @cfg {String} dropAllowed
23933      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23934      */
23935     dropAllowed : "x-dd-drop-ok",
23936     /**
23937      * @cfg {String} dropNotAllowed
23938      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23939      */
23940     dropNotAllowed : "x-dd-drop-nodrop",
23941     /**
23942      * @cfg {boolean} success
23943      * set this after drop listener.. 
23944      */
23945     success : false,
23946     /**
23947      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23948      * if the drop point is valid for over/enter..
23949      */
23950     valid : false,
23951     // private
23952     isTarget : true,
23953
23954     // private
23955     isNotifyTarget : true,
23956     
23957     /**
23958      * @hide
23959      */
23960     notifyEnter : function(dd, e, data)
23961     {
23962         this.valid = true;
23963         this.fireEvent('enter', dd, e, data);
23964         if(this.overClass){
23965             this.el.addClass(this.overClass);
23966         }
23967         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23968             this.valid ? this.dropAllowed : this.dropNotAllowed
23969         );
23970     },
23971
23972     /**
23973      * @hide
23974      */
23975     notifyOver : function(dd, e, data)
23976     {
23977         this.valid = true;
23978         this.fireEvent('over', dd, e, data);
23979         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23980             this.valid ? this.dropAllowed : this.dropNotAllowed
23981         );
23982     },
23983
23984     /**
23985      * @hide
23986      */
23987     notifyOut : function(dd, e, data)
23988     {
23989         this.fireEvent('out', dd, e, data);
23990         if(this.overClass){
23991             this.el.removeClass(this.overClass);
23992         }
23993     },
23994
23995     /**
23996      * @hide
23997      */
23998     notifyDrop : function(dd, e, data)
23999     {
24000         this.success = false;
24001         this.fireEvent('drop', dd, e, data);
24002         return this.success;
24003     }
24004 });/*
24005  * Based on:
24006  * Ext JS Library 1.1.1
24007  * Copyright(c) 2006-2007, Ext JS, LLC.
24008  *
24009  * Originally Released Under LGPL - original licence link has changed is not relivant.
24010  *
24011  * Fork - LGPL
24012  * <script type="text/javascript">
24013  */
24014
24015
24016 /**
24017  * @class Roo.dd.DragZone
24018  * @extends Roo.dd.DragSource
24019  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24020  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24021  * @constructor
24022  * @param {String/HTMLElement/Element} el The container element
24023  * @param {Object} config
24024  */
24025 Roo.dd.DragZone = function(el, config){
24026     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24027     if(this.containerScroll){
24028         Roo.dd.ScrollManager.register(this.el);
24029     }
24030 };
24031
24032 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24033     /**
24034      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24035      * for auto scrolling during drag operations.
24036      */
24037     /**
24038      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24039      * method after a failed drop (defaults to "c3daf9" - light blue)
24040      */
24041
24042     /**
24043      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24044      * for a valid target to drag based on the mouse down. Override this method
24045      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24046      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24047      * @param {EventObject} e The mouse down event
24048      * @return {Object} The dragData
24049      */
24050     getDragData : function(e){
24051         return Roo.dd.Registry.getHandleFromEvent(e);
24052     },
24053     
24054     /**
24055      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24056      * this.dragData.ddel
24057      * @param {Number} x The x position of the click on the dragged object
24058      * @param {Number} y The y position of the click on the dragged object
24059      * @return {Boolean} true to continue the drag, false to cancel
24060      */
24061     onInitDrag : function(x, y){
24062         this.proxy.update(this.dragData.ddel.cloneNode(true));
24063         this.onStartDrag(x, y);
24064         return true;
24065     },
24066     
24067     /**
24068      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24069      */
24070     afterRepair : function(){
24071         if(Roo.enableFx){
24072             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24073         }
24074         this.dragging = false;
24075     },
24076
24077     /**
24078      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24079      * the XY of this.dragData.ddel
24080      * @param {EventObject} e The mouse up event
24081      * @return {Array} The xy location (e.g. [100, 200])
24082      */
24083     getRepairXY : function(e){
24084         return Roo.Element.fly(this.dragData.ddel).getXY();  
24085     }
24086 });/*
24087  * Based on:
24088  * Ext JS Library 1.1.1
24089  * Copyright(c) 2006-2007, Ext JS, LLC.
24090  *
24091  * Originally Released Under LGPL - original licence link has changed is not relivant.
24092  *
24093  * Fork - LGPL
24094  * <script type="text/javascript">
24095  */
24096 /**
24097  * @class Roo.dd.DropZone
24098  * @extends Roo.dd.DropTarget
24099  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24100  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24101  * @constructor
24102  * @param {String/HTMLElement/Element} el The container element
24103  * @param {Object} config
24104  */
24105 Roo.dd.DropZone = function(el, config){
24106     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24107 };
24108
24109 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24110     /**
24111      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24112      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24113      * provide your own custom lookup.
24114      * @param {Event} e The event
24115      * @return {Object} data The custom data
24116      */
24117     getTargetFromEvent : function(e){
24118         return Roo.dd.Registry.getTargetFromEvent(e);
24119     },
24120
24121     /**
24122      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24123      * that it has registered.  This method has no default implementation and should be overridden to provide
24124      * node-specific processing if necessary.
24125      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24126      * {@link #getTargetFromEvent} for this node)
24127      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24128      * @param {Event} e The event
24129      * @param {Object} data An object containing arbitrary data supplied by the drag source
24130      */
24131     onNodeEnter : function(n, dd, e, data){
24132         
24133     },
24134
24135     /**
24136      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24137      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24138      * overridden to provide the proper feedback.
24139      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24140      * {@link #getTargetFromEvent} for this node)
24141      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24142      * @param {Event} e The event
24143      * @param {Object} data An object containing arbitrary data supplied by the drag source
24144      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24145      * underlying {@link Roo.dd.StatusProxy} can be updated
24146      */
24147     onNodeOver : function(n, dd, e, data){
24148         return this.dropAllowed;
24149     },
24150
24151     /**
24152      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24153      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24154      * node-specific processing if necessary.
24155      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24156      * {@link #getTargetFromEvent} for this node)
24157      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24158      * @param {Event} e The event
24159      * @param {Object} data An object containing arbitrary data supplied by the drag source
24160      */
24161     onNodeOut : function(n, dd, e, data){
24162         
24163     },
24164
24165     /**
24166      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24167      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24168      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24169      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24170      * {@link #getTargetFromEvent} for this node)
24171      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24172      * @param {Event} e The event
24173      * @param {Object} data An object containing arbitrary data supplied by the drag source
24174      * @return {Boolean} True if the drop was valid, else false
24175      */
24176     onNodeDrop : function(n, dd, e, data){
24177         return false;
24178     },
24179
24180     /**
24181      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24182      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24183      * it should be overridden to provide the proper feedback if necessary.
24184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24185      * @param {Event} e The event
24186      * @param {Object} data An object containing arbitrary data supplied by the drag source
24187      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24188      * underlying {@link Roo.dd.StatusProxy} can be updated
24189      */
24190     onContainerOver : function(dd, e, data){
24191         return this.dropNotAllowed;
24192     },
24193
24194     /**
24195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24196      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24197      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24198      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24200      * @param {Event} e The event
24201      * @param {Object} data An object containing arbitrary data supplied by the drag source
24202      * @return {Boolean} True if the drop was valid, else false
24203      */
24204     onContainerDrop : function(dd, e, data){
24205         return false;
24206     },
24207
24208     /**
24209      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24210      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24211      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24212      * you should override this method and provide a custom implementation.
24213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24214      * @param {Event} e The event
24215      * @param {Object} data An object containing arbitrary data supplied by the drag source
24216      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24217      * underlying {@link Roo.dd.StatusProxy} can be updated
24218      */
24219     notifyEnter : function(dd, e, data){
24220         return this.dropNotAllowed;
24221     },
24222
24223     /**
24224      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24225      * This method will be called on every mouse movement while the drag source is over the drop zone.
24226      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24227      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24228      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24229      * registered node, it will call {@link #onContainerOver}.
24230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24231      * @param {Event} e The event
24232      * @param {Object} data An object containing arbitrary data supplied by the drag source
24233      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24234      * underlying {@link Roo.dd.StatusProxy} can be updated
24235      */
24236     notifyOver : function(dd, e, data){
24237         var n = this.getTargetFromEvent(e);
24238         if(!n){ // not over valid drop target
24239             if(this.lastOverNode){
24240                 this.onNodeOut(this.lastOverNode, dd, e, data);
24241                 this.lastOverNode = null;
24242             }
24243             return this.onContainerOver(dd, e, data);
24244         }
24245         if(this.lastOverNode != n){
24246             if(this.lastOverNode){
24247                 this.onNodeOut(this.lastOverNode, dd, e, data);
24248             }
24249             this.onNodeEnter(n, dd, e, data);
24250             this.lastOverNode = n;
24251         }
24252         return this.onNodeOver(n, dd, e, data);
24253     },
24254
24255     /**
24256      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24257      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24258      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24259      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24260      * @param {Event} e The event
24261      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24262      */
24263     notifyOut : function(dd, e, data){
24264         if(this.lastOverNode){
24265             this.onNodeOut(this.lastOverNode, dd, e, data);
24266             this.lastOverNode = null;
24267         }
24268     },
24269
24270     /**
24271      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24272      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24273      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24274      * otherwise it will call {@link #onContainerDrop}.
24275      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24276      * @param {Event} e The event
24277      * @param {Object} data An object containing arbitrary data supplied by the drag source
24278      * @return {Boolean} True if the drop was valid, else false
24279      */
24280     notifyDrop : function(dd, e, data){
24281         if(this.lastOverNode){
24282             this.onNodeOut(this.lastOverNode, dd, e, data);
24283             this.lastOverNode = null;
24284         }
24285         var n = this.getTargetFromEvent(e);
24286         return n ?
24287             this.onNodeDrop(n, dd, e, data) :
24288             this.onContainerDrop(dd, e, data);
24289     },
24290
24291     // private
24292     triggerCacheRefresh : function(){
24293         Roo.dd.DDM.refreshCache(this.groups);
24294     }  
24295 });/*
24296  * Based on:
24297  * Ext JS Library 1.1.1
24298  * Copyright(c) 2006-2007, Ext JS, LLC.
24299  *
24300  * Originally Released Under LGPL - original licence link has changed is not relivant.
24301  *
24302  * Fork - LGPL
24303  * <script type="text/javascript">
24304  */
24305
24306
24307 /**
24308  * @class Roo.data.SortTypes
24309  * @static
24310  * Defines the default sorting (casting?) comparison functions used when sorting data.
24311  */
24312 Roo.data.SortTypes = {
24313     /**
24314      * Default sort that does nothing
24315      * @param {Mixed} s The value being converted
24316      * @return {Mixed} The comparison value
24317      */
24318     none : function(s){
24319         return s;
24320     },
24321     
24322     /**
24323      * The regular expression used to strip tags
24324      * @type {RegExp}
24325      * @property
24326      */
24327     stripTagsRE : /<\/?[^>]+>/gi,
24328     
24329     /**
24330      * Strips all HTML tags to sort on text only
24331      * @param {Mixed} s The value being converted
24332      * @return {String} The comparison value
24333      */
24334     asText : function(s){
24335         return String(s).replace(this.stripTagsRE, "");
24336     },
24337     
24338     /**
24339      * Strips all HTML tags to sort on text only - Case insensitive
24340      * @param {Mixed} s The value being converted
24341      * @return {String} The comparison value
24342      */
24343     asUCText : function(s){
24344         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24345     },
24346     
24347     /**
24348      * Case insensitive string
24349      * @param {Mixed} s The value being converted
24350      * @return {String} The comparison value
24351      */
24352     asUCString : function(s) {
24353         return String(s).toUpperCase();
24354     },
24355     
24356     /**
24357      * Date sorting
24358      * @param {Mixed} s The value being converted
24359      * @return {Number} The comparison value
24360      */
24361     asDate : function(s) {
24362         if(!s){
24363             return 0;
24364         }
24365         if(s instanceof Date){
24366             return s.getTime();
24367         }
24368         return Date.parse(String(s));
24369     },
24370     
24371     /**
24372      * Float sorting
24373      * @param {Mixed} s The value being converted
24374      * @return {Float} The comparison value
24375      */
24376     asFloat : function(s) {
24377         var val = parseFloat(String(s).replace(/,/g, ""));
24378         if(isNaN(val)) {
24379             val = 0;
24380         }
24381         return val;
24382     },
24383     
24384     /**
24385      * Integer sorting
24386      * @param {Mixed} s The value being converted
24387      * @return {Number} The comparison value
24388      */
24389     asInt : function(s) {
24390         var val = parseInt(String(s).replace(/,/g, ""));
24391         if(isNaN(val)) {
24392             val = 0;
24393         }
24394         return val;
24395     }
24396 };/*
24397  * Based on:
24398  * Ext JS Library 1.1.1
24399  * Copyright(c) 2006-2007, Ext JS, LLC.
24400  *
24401  * Originally Released Under LGPL - original licence link has changed is not relivant.
24402  *
24403  * Fork - LGPL
24404  * <script type="text/javascript">
24405  */
24406
24407 /**
24408 * @class Roo.data.Record
24409  * Instances of this class encapsulate both record <em>definition</em> information, and record
24410  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24411  * to access Records cached in an {@link Roo.data.Store} object.<br>
24412  * <p>
24413  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24414  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24415  * objects.<br>
24416  * <p>
24417  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24418  * @constructor
24419  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24420  * {@link #create}. The parameters are the same.
24421  * @param {Array} data An associative Array of data values keyed by the field name.
24422  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24423  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24424  * not specified an integer id is generated.
24425  */
24426 Roo.data.Record = function(data, id){
24427     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24428     this.data = data;
24429 };
24430
24431 /**
24432  * Generate a constructor for a specific record layout.
24433  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24434  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24435  * Each field definition object may contain the following properties: <ul>
24436  * <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,
24437  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24438  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24439  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24440  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24441  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24442  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24443  * this may be omitted.</p></li>
24444  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24445  * <ul><li>auto (Default, implies no conversion)</li>
24446  * <li>string</li>
24447  * <li>int</li>
24448  * <li>float</li>
24449  * <li>boolean</li>
24450  * <li>date</li></ul></p></li>
24451  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24452  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24453  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24454  * by the Reader into an object that will be stored in the Record. It is passed the
24455  * following parameters:<ul>
24456  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24457  * </ul></p></li>
24458  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24459  * </ul>
24460  * <br>usage:<br><pre><code>
24461 var TopicRecord = Roo.data.Record.create(
24462     {name: 'title', mapping: 'topic_title'},
24463     {name: 'author', mapping: 'username'},
24464     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24465     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24466     {name: 'lastPoster', mapping: 'user2'},
24467     {name: 'excerpt', mapping: 'post_text'}
24468 );
24469
24470 var myNewRecord = new TopicRecord({
24471     title: 'Do my job please',
24472     author: 'noobie',
24473     totalPosts: 1,
24474     lastPost: new Date(),
24475     lastPoster: 'Animal',
24476     excerpt: 'No way dude!'
24477 });
24478 myStore.add(myNewRecord);
24479 </code></pre>
24480  * @method create
24481  * @static
24482  */
24483 Roo.data.Record.create = function(o){
24484     var f = function(){
24485         f.superclass.constructor.apply(this, arguments);
24486     };
24487     Roo.extend(f, Roo.data.Record);
24488     var p = f.prototype;
24489     p.fields = new Roo.util.MixedCollection(false, function(field){
24490         return field.name;
24491     });
24492     for(var i = 0, len = o.length; i < len; i++){
24493         p.fields.add(new Roo.data.Field(o[i]));
24494     }
24495     f.getField = function(name){
24496         return p.fields.get(name);  
24497     };
24498     return f;
24499 };
24500
24501 Roo.data.Record.AUTO_ID = 1000;
24502 Roo.data.Record.EDIT = 'edit';
24503 Roo.data.Record.REJECT = 'reject';
24504 Roo.data.Record.COMMIT = 'commit';
24505
24506 Roo.data.Record.prototype = {
24507     /**
24508      * Readonly flag - true if this record has been modified.
24509      * @type Boolean
24510      */
24511     dirty : false,
24512     editing : false,
24513     error: null,
24514     modified: null,
24515
24516     // private
24517     join : function(store){
24518         this.store = store;
24519     },
24520
24521     /**
24522      * Set the named field to the specified value.
24523      * @param {String} name The name of the field to set.
24524      * @param {Object} value The value to set the field to.
24525      */
24526     set : function(name, value){
24527         if(this.data[name] == value){
24528             return;
24529         }
24530         this.dirty = true;
24531         if(!this.modified){
24532             this.modified = {};
24533         }
24534         if(typeof this.modified[name] == 'undefined'){
24535             this.modified[name] = this.data[name];
24536         }
24537         this.data[name] = value;
24538         if(!this.editing && this.store){
24539             this.store.afterEdit(this);
24540         }       
24541     },
24542
24543     /**
24544      * Get the value of the named field.
24545      * @param {String} name The name of the field to get the value of.
24546      * @return {Object} The value of the field.
24547      */
24548     get : function(name){
24549         return this.data[name]; 
24550     },
24551
24552     // private
24553     beginEdit : function(){
24554         this.editing = true;
24555         this.modified = {}; 
24556     },
24557
24558     // private
24559     cancelEdit : function(){
24560         this.editing = false;
24561         delete this.modified;
24562     },
24563
24564     // private
24565     endEdit : function(){
24566         this.editing = false;
24567         if(this.dirty && this.store){
24568             this.store.afterEdit(this);
24569         }
24570     },
24571
24572     /**
24573      * Usually called by the {@link Roo.data.Store} which owns the Record.
24574      * Rejects all changes made to the Record since either creation, or the last commit operation.
24575      * Modified fields are reverted to their original values.
24576      * <p>
24577      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24578      * of reject operations.
24579      */
24580     reject : function(){
24581         var m = this.modified;
24582         for(var n in m){
24583             if(typeof m[n] != "function"){
24584                 this.data[n] = m[n];
24585             }
24586         }
24587         this.dirty = false;
24588         delete this.modified;
24589         this.editing = false;
24590         if(this.store){
24591             this.store.afterReject(this);
24592         }
24593     },
24594
24595     /**
24596      * Usually called by the {@link Roo.data.Store} which owns the Record.
24597      * Commits all changes made to the Record since either creation, or the last commit operation.
24598      * <p>
24599      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24600      * of commit operations.
24601      */
24602     commit : function(){
24603         this.dirty = false;
24604         delete this.modified;
24605         this.editing = false;
24606         if(this.store){
24607             this.store.afterCommit(this);
24608         }
24609     },
24610
24611     // private
24612     hasError : function(){
24613         return this.error != null;
24614     },
24615
24616     // private
24617     clearError : function(){
24618         this.error = null;
24619     },
24620
24621     /**
24622      * Creates a copy of this record.
24623      * @param {String} id (optional) A new record id if you don't want to use this record's id
24624      * @return {Record}
24625      */
24626     copy : function(newId) {
24627         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24628     }
24629 };/*
24630  * Based on:
24631  * Ext JS Library 1.1.1
24632  * Copyright(c) 2006-2007, Ext JS, LLC.
24633  *
24634  * Originally Released Under LGPL - original licence link has changed is not relivant.
24635  *
24636  * Fork - LGPL
24637  * <script type="text/javascript">
24638  */
24639
24640
24641
24642 /**
24643  * @class Roo.data.Store
24644  * @extends Roo.util.Observable
24645  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24646  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24647  * <p>
24648  * 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
24649  * has no knowledge of the format of the data returned by the Proxy.<br>
24650  * <p>
24651  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24652  * instances from the data object. These records are cached and made available through accessor functions.
24653  * @constructor
24654  * Creates a new Store.
24655  * @param {Object} config A config object containing the objects needed for the Store to access data,
24656  * and read the data into Records.
24657  */
24658 Roo.data.Store = function(config){
24659     this.data = new Roo.util.MixedCollection(false);
24660     this.data.getKey = function(o){
24661         return o.id;
24662     };
24663     this.baseParams = {};
24664     // private
24665     this.paramNames = {
24666         "start" : "start",
24667         "limit" : "limit",
24668         "sort" : "sort",
24669         "dir" : "dir",
24670         "multisort" : "_multisort"
24671     };
24672
24673     if(config && config.data){
24674         this.inlineData = config.data;
24675         delete config.data;
24676     }
24677
24678     Roo.apply(this, config);
24679     
24680     if(this.reader){ // reader passed
24681         this.reader = Roo.factory(this.reader, Roo.data);
24682         this.reader.xmodule = this.xmodule || false;
24683         if(!this.recordType){
24684             this.recordType = this.reader.recordType;
24685         }
24686         if(this.reader.onMetaChange){
24687             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24688         }
24689     }
24690
24691     if(this.recordType){
24692         this.fields = this.recordType.prototype.fields;
24693     }
24694     this.modified = [];
24695
24696     this.addEvents({
24697         /**
24698          * @event datachanged
24699          * Fires when the data cache has changed, and a widget which is using this Store
24700          * as a Record cache should refresh its view.
24701          * @param {Store} this
24702          */
24703         datachanged : true,
24704         /**
24705          * @event metachange
24706          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24707          * @param {Store} this
24708          * @param {Object} meta The JSON metadata
24709          */
24710         metachange : true,
24711         /**
24712          * @event add
24713          * Fires when Records have been added to the Store
24714          * @param {Store} this
24715          * @param {Roo.data.Record[]} records The array of Records added
24716          * @param {Number} index The index at which the record(s) were added
24717          */
24718         add : true,
24719         /**
24720          * @event remove
24721          * Fires when a Record has been removed from the Store
24722          * @param {Store} this
24723          * @param {Roo.data.Record} record The Record that was removed
24724          * @param {Number} index The index at which the record was removed
24725          */
24726         remove : true,
24727         /**
24728          * @event update
24729          * Fires when a Record has been updated
24730          * @param {Store} this
24731          * @param {Roo.data.Record} record The Record that was updated
24732          * @param {String} operation The update operation being performed.  Value may be one of:
24733          * <pre><code>
24734  Roo.data.Record.EDIT
24735  Roo.data.Record.REJECT
24736  Roo.data.Record.COMMIT
24737          * </code></pre>
24738          */
24739         update : true,
24740         /**
24741          * @event clear
24742          * Fires when the data cache has been cleared.
24743          * @param {Store} this
24744          */
24745         clear : true,
24746         /**
24747          * @event beforeload
24748          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24749          * the load action will be canceled.
24750          * @param {Store} this
24751          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24752          */
24753         beforeload : true,
24754         /**
24755          * @event beforeloadadd
24756          * Fires after a new set of Records has been loaded.
24757          * @param {Store} this
24758          * @param {Roo.data.Record[]} records The Records that were loaded
24759          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24760          */
24761         beforeloadadd : true,
24762         /**
24763          * @event load
24764          * Fires after a new set of Records has been loaded, before they are added to the store.
24765          * @param {Store} this
24766          * @param {Roo.data.Record[]} records The Records that were loaded
24767          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24768          * @params {Object} return from reader
24769          */
24770         load : true,
24771         /**
24772          * @event loadexception
24773          * Fires if an exception occurs in the Proxy during loading.
24774          * Called with the signature of the Proxy's "loadexception" event.
24775          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24776          * 
24777          * @param {Proxy} 
24778          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24779          * @param {Object} load options 
24780          * @param {Object} jsonData from your request (normally this contains the Exception)
24781          */
24782         loadexception : true
24783     });
24784     
24785     if(this.proxy){
24786         this.proxy = Roo.factory(this.proxy, Roo.data);
24787         this.proxy.xmodule = this.xmodule || false;
24788         this.relayEvents(this.proxy,  ["loadexception"]);
24789     }
24790     this.sortToggle = {};
24791     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24792
24793     Roo.data.Store.superclass.constructor.call(this);
24794
24795     if(this.inlineData){
24796         this.loadData(this.inlineData);
24797         delete this.inlineData;
24798     }
24799 };
24800
24801 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24802      /**
24803     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24804     * without a remote query - used by combo/forms at present.
24805     */
24806     
24807     /**
24808     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24809     */
24810     /**
24811     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24812     */
24813     /**
24814     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24815     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24816     */
24817     /**
24818     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24819     * on any HTTP request
24820     */
24821     /**
24822     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24823     */
24824     /**
24825     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24826     */
24827     multiSort: false,
24828     /**
24829     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24830     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24831     */
24832     remoteSort : false,
24833
24834     /**
24835     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24836      * loaded or when a record is removed. (defaults to false).
24837     */
24838     pruneModifiedRecords : false,
24839
24840     // private
24841     lastOptions : null,
24842
24843     /**
24844      * Add Records to the Store and fires the add event.
24845      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24846      */
24847     add : function(records){
24848         records = [].concat(records);
24849         for(var i = 0, len = records.length; i < len; i++){
24850             records[i].join(this);
24851         }
24852         var index = this.data.length;
24853         this.data.addAll(records);
24854         this.fireEvent("add", this, records, index);
24855     },
24856
24857     /**
24858      * Remove a Record from the Store and fires the remove event.
24859      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24860      */
24861     remove : function(record){
24862         var index = this.data.indexOf(record);
24863         this.data.removeAt(index);
24864  
24865         if(this.pruneModifiedRecords){
24866             this.modified.remove(record);
24867         }
24868         this.fireEvent("remove", this, record, index);
24869     },
24870
24871     /**
24872      * Remove all Records from the Store and fires the clear event.
24873      */
24874     removeAll : function(){
24875         this.data.clear();
24876         if(this.pruneModifiedRecords){
24877             this.modified = [];
24878         }
24879         this.fireEvent("clear", this);
24880     },
24881
24882     /**
24883      * Inserts Records to the Store at the given index and fires the add event.
24884      * @param {Number} index The start index at which to insert the passed Records.
24885      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24886      */
24887     insert : function(index, records){
24888         records = [].concat(records);
24889         for(var i = 0, len = records.length; i < len; i++){
24890             this.data.insert(index, records[i]);
24891             records[i].join(this);
24892         }
24893         this.fireEvent("add", this, records, index);
24894     },
24895
24896     /**
24897      * Get the index within the cache of the passed Record.
24898      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24899      * @return {Number} The index of the passed Record. Returns -1 if not found.
24900      */
24901     indexOf : function(record){
24902         return this.data.indexOf(record);
24903     },
24904
24905     /**
24906      * Get the index within the cache of the Record with the passed id.
24907      * @param {String} id The id of the Record to find.
24908      * @return {Number} The index of the Record. Returns -1 if not found.
24909      */
24910     indexOfId : function(id){
24911         return this.data.indexOfKey(id);
24912     },
24913
24914     /**
24915      * Get the Record with the specified id.
24916      * @param {String} id The id of the Record to find.
24917      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24918      */
24919     getById : function(id){
24920         return this.data.key(id);
24921     },
24922
24923     /**
24924      * Get the Record at the specified index.
24925      * @param {Number} index The index of the Record to find.
24926      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24927      */
24928     getAt : function(index){
24929         return this.data.itemAt(index);
24930     },
24931
24932     /**
24933      * Returns a range of Records between specified indices.
24934      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24935      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24936      * @return {Roo.data.Record[]} An array of Records
24937      */
24938     getRange : function(start, end){
24939         return this.data.getRange(start, end);
24940     },
24941
24942     // private
24943     storeOptions : function(o){
24944         o = Roo.apply({}, o);
24945         delete o.callback;
24946         delete o.scope;
24947         this.lastOptions = o;
24948     },
24949
24950     /**
24951      * Loads the Record cache from the configured Proxy using the configured Reader.
24952      * <p>
24953      * If using remote paging, then the first load call must specify the <em>start</em>
24954      * and <em>limit</em> properties in the options.params property to establish the initial
24955      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24956      * <p>
24957      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24958      * and this call will return before the new data has been loaded. Perform any post-processing
24959      * in a callback function, or in a "load" event handler.</strong>
24960      * <p>
24961      * @param {Object} options An object containing properties which control loading options:<ul>
24962      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24963      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24964      * <pre>
24965                 {
24966                     data : data,  // array of key=>value data like JsonReader
24967                     total : data.length,
24968                     success : true
24969                     
24970                 }
24971         </pre>
24972             }.</li>
24973      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24974      * passed the following arguments:<ul>
24975      * <li>r : Roo.data.Record[]</li>
24976      * <li>options: Options object from the load call</li>
24977      * <li>success: Boolean success indicator</li></ul></li>
24978      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24979      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24980      * </ul>
24981      */
24982     load : function(options){
24983         options = options || {};
24984         if(this.fireEvent("beforeload", this, options) !== false){
24985             this.storeOptions(options);
24986             var p = Roo.apply(options.params || {}, this.baseParams);
24987             // if meta was not loaded from remote source.. try requesting it.
24988             if (!this.reader.metaFromRemote) {
24989                 p._requestMeta = 1;
24990             }
24991             if(this.sortInfo && this.remoteSort){
24992                 var pn = this.paramNames;
24993                 p[pn["sort"]] = this.sortInfo.field;
24994                 p[pn["dir"]] = this.sortInfo.direction;
24995             }
24996             if (this.multiSort) {
24997                 var pn = this.paramNames;
24998                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24999             }
25000             
25001             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25002         }
25003     },
25004
25005     /**
25006      * Reloads the Record cache from the configured Proxy using the configured Reader and
25007      * the options from the last load operation performed.
25008      * @param {Object} options (optional) An object containing properties which may override the options
25009      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25010      * the most recently used options are reused).
25011      */
25012     reload : function(options){
25013         this.load(Roo.applyIf(options||{}, this.lastOptions));
25014     },
25015
25016     // private
25017     // Called as a callback by the Reader during a load operation.
25018     loadRecords : function(o, options, success){
25019          
25020         if(!o){
25021             if(success !== false){
25022                 this.fireEvent("load", this, [], options, o);
25023             }
25024             if(options.callback){
25025                 options.callback.call(options.scope || this, [], options, false);
25026             }
25027             return;
25028         }
25029         // if data returned failure - throw an exception.
25030         if (o.success === false) {
25031             // show a message if no listener is registered.
25032             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25033                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25034             }
25035             // loadmask wil be hooked into this..
25036             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25037             return;
25038         }
25039         var r = o.records, t = o.totalRecords || r.length;
25040         
25041         this.fireEvent("beforeloadadd", this, r, options, o);
25042         
25043         if(!options || options.add !== true){
25044             if(this.pruneModifiedRecords){
25045                 this.modified = [];
25046             }
25047             for(var i = 0, len = r.length; i < len; i++){
25048                 r[i].join(this);
25049             }
25050             if(this.snapshot){
25051                 this.data = this.snapshot;
25052                 delete this.snapshot;
25053             }
25054             this.data.clear();
25055             this.data.addAll(r);
25056             this.totalLength = t;
25057             this.applySort();
25058             this.fireEvent("datachanged", this);
25059         }else{
25060             this.totalLength = Math.max(t, this.data.length+r.length);
25061             this.add(r);
25062         }
25063         
25064         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25065                 
25066             var e = new Roo.data.Record({});
25067
25068             e.set(this.parent.displayField, this.parent.emptyTitle);
25069             e.set(this.parent.valueField, '');
25070
25071             this.insert(0, e);
25072         }
25073             
25074         this.fireEvent("load", this, r, options, o);
25075         if(options.callback){
25076             options.callback.call(options.scope || this, r, options, true);
25077         }
25078     },
25079
25080
25081     /**
25082      * Loads data from a passed data block. A Reader which understands the format of the data
25083      * must have been configured in the constructor.
25084      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25085      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25086      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25087      */
25088     loadData : function(o, append){
25089         var r = this.reader.readRecords(o);
25090         this.loadRecords(r, {add: append}, true);
25091     },
25092     
25093      /**
25094      * using 'cn' the nested child reader read the child array into it's child stores.
25095      * @param {Object} rec The record with a 'children array
25096      */
25097     loadDataFromChildren : function(rec)
25098     {
25099         this.loadData(this.reader.toLoadData(rec));
25100     },
25101     
25102
25103     /**
25104      * Gets the number of cached records.
25105      * <p>
25106      * <em>If using paging, this may not be the total size of the dataset. If the data object
25107      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25108      * the data set size</em>
25109      */
25110     getCount : function(){
25111         return this.data.length || 0;
25112     },
25113
25114     /**
25115      * Gets the total number of records in the dataset as returned by the server.
25116      * <p>
25117      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25118      * the dataset size</em>
25119      */
25120     getTotalCount : function(){
25121         return this.totalLength || 0;
25122     },
25123
25124     /**
25125      * Returns the sort state of the Store as an object with two properties:
25126      * <pre><code>
25127  field {String} The name of the field by which the Records are sorted
25128  direction {String} The sort order, "ASC" or "DESC"
25129      * </code></pre>
25130      */
25131     getSortState : function(){
25132         return this.sortInfo;
25133     },
25134
25135     // private
25136     applySort : function(){
25137         if(this.sortInfo && !this.remoteSort){
25138             var s = this.sortInfo, f = s.field;
25139             var st = this.fields.get(f).sortType;
25140             var fn = function(r1, r2){
25141                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25142                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25143             };
25144             this.data.sort(s.direction, fn);
25145             if(this.snapshot && this.snapshot != this.data){
25146                 this.snapshot.sort(s.direction, fn);
25147             }
25148         }
25149     },
25150
25151     /**
25152      * Sets the default sort column and order to be used by the next load operation.
25153      * @param {String} fieldName The name of the field to sort by.
25154      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25155      */
25156     setDefaultSort : function(field, dir){
25157         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25158     },
25159
25160     /**
25161      * Sort the Records.
25162      * If remote sorting is used, the sort is performed on the server, and the cache is
25163      * reloaded. If local sorting is used, the cache is sorted internally.
25164      * @param {String} fieldName The name of the field to sort by.
25165      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25166      */
25167     sort : function(fieldName, dir){
25168         var f = this.fields.get(fieldName);
25169         if(!dir){
25170             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25171             
25172             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25173                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25174             }else{
25175                 dir = f.sortDir;
25176             }
25177         }
25178         this.sortToggle[f.name] = dir;
25179         this.sortInfo = {field: f.name, direction: dir};
25180         if(!this.remoteSort){
25181             this.applySort();
25182             this.fireEvent("datachanged", this);
25183         }else{
25184             this.load(this.lastOptions);
25185         }
25186     },
25187
25188     /**
25189      * Calls the specified function for each of the Records in the cache.
25190      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25191      * Returning <em>false</em> aborts and exits the iteration.
25192      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25193      */
25194     each : function(fn, scope){
25195         this.data.each(fn, scope);
25196     },
25197
25198     /**
25199      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25200      * (e.g., during paging).
25201      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25202      */
25203     getModifiedRecords : function(){
25204         return this.modified;
25205     },
25206
25207     // private
25208     createFilterFn : function(property, value, anyMatch){
25209         if(!value.exec){ // not a regex
25210             value = String(value);
25211             if(value.length == 0){
25212                 return false;
25213             }
25214             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25215         }
25216         return function(r){
25217             return value.test(r.data[property]);
25218         };
25219     },
25220
25221     /**
25222      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25223      * @param {String} property A field on your records
25224      * @param {Number} start The record index to start at (defaults to 0)
25225      * @param {Number} end The last record index to include (defaults to length - 1)
25226      * @return {Number} The sum
25227      */
25228     sum : function(property, start, end){
25229         var rs = this.data.items, v = 0;
25230         start = start || 0;
25231         end = (end || end === 0) ? end : rs.length-1;
25232
25233         for(var i = start; i <= end; i++){
25234             v += (rs[i].data[property] || 0);
25235         }
25236         return v;
25237     },
25238
25239     /**
25240      * Filter the records by a specified property.
25241      * @param {String} field A field on your records
25242      * @param {String/RegExp} value Either a string that the field
25243      * should start with or a RegExp to test against the field
25244      * @param {Boolean} anyMatch True to match any part not just the beginning
25245      */
25246     filter : function(property, value, anyMatch){
25247         var fn = this.createFilterFn(property, value, anyMatch);
25248         return fn ? this.filterBy(fn) : this.clearFilter();
25249     },
25250
25251     /**
25252      * Filter by a function. The specified function will be called with each
25253      * record in this data source. If the function returns true the record is included,
25254      * otherwise it is filtered.
25255      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25256      * @param {Object} scope (optional) The scope of the function (defaults to this)
25257      */
25258     filterBy : function(fn, scope){
25259         this.snapshot = this.snapshot || this.data;
25260         this.data = this.queryBy(fn, scope||this);
25261         this.fireEvent("datachanged", this);
25262     },
25263
25264     /**
25265      * Query the records by a specified property.
25266      * @param {String} field A field on your records
25267      * @param {String/RegExp} value Either a string that the field
25268      * should start with or a RegExp to test against the field
25269      * @param {Boolean} anyMatch True to match any part not just the beginning
25270      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25271      */
25272     query : function(property, value, anyMatch){
25273         var fn = this.createFilterFn(property, value, anyMatch);
25274         return fn ? this.queryBy(fn) : this.data.clone();
25275     },
25276
25277     /**
25278      * Query by a function. The specified function will be called with each
25279      * record in this data source. If the function returns true the record is included
25280      * in the results.
25281      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25282      * @param {Object} scope (optional) The scope of the function (defaults to this)
25283       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25284      **/
25285     queryBy : function(fn, scope){
25286         var data = this.snapshot || this.data;
25287         return data.filterBy(fn, scope||this);
25288     },
25289
25290     /**
25291      * Collects unique values for a particular dataIndex from this store.
25292      * @param {String} dataIndex The property to collect
25293      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25294      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25295      * @return {Array} An array of the unique values
25296      **/
25297     collect : function(dataIndex, allowNull, bypassFilter){
25298         var d = (bypassFilter === true && this.snapshot) ?
25299                 this.snapshot.items : this.data.items;
25300         var v, sv, r = [], l = {};
25301         for(var i = 0, len = d.length; i < len; i++){
25302             v = d[i].data[dataIndex];
25303             sv = String(v);
25304             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25305                 l[sv] = true;
25306                 r[r.length] = v;
25307             }
25308         }
25309         return r;
25310     },
25311
25312     /**
25313      * Revert to a view of the Record cache with no filtering applied.
25314      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25315      */
25316     clearFilter : function(suppressEvent){
25317         if(this.snapshot && this.snapshot != this.data){
25318             this.data = this.snapshot;
25319             delete this.snapshot;
25320             if(suppressEvent !== true){
25321                 this.fireEvent("datachanged", this);
25322             }
25323         }
25324     },
25325
25326     // private
25327     afterEdit : function(record){
25328         if(this.modified.indexOf(record) == -1){
25329             this.modified.push(record);
25330         }
25331         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25332     },
25333     
25334     // private
25335     afterReject : function(record){
25336         this.modified.remove(record);
25337         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25338     },
25339
25340     // private
25341     afterCommit : function(record){
25342         this.modified.remove(record);
25343         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25344     },
25345
25346     /**
25347      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25348      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25349      */
25350     commitChanges : function(){
25351         var m = this.modified.slice(0);
25352         this.modified = [];
25353         for(var i = 0, len = m.length; i < len; i++){
25354             m[i].commit();
25355         }
25356     },
25357
25358     /**
25359      * Cancel outstanding changes on all changed records.
25360      */
25361     rejectChanges : function(){
25362         var m = this.modified.slice(0);
25363         this.modified = [];
25364         for(var i = 0, len = m.length; i < len; i++){
25365             m[i].reject();
25366         }
25367     },
25368
25369     onMetaChange : function(meta, rtype, o){
25370         this.recordType = rtype;
25371         this.fields = rtype.prototype.fields;
25372         delete this.snapshot;
25373         this.sortInfo = meta.sortInfo || this.sortInfo;
25374         this.modified = [];
25375         this.fireEvent('metachange', this, this.reader.meta);
25376     },
25377     
25378     moveIndex : function(data, type)
25379     {
25380         var index = this.indexOf(data);
25381         
25382         var newIndex = index + type;
25383         
25384         this.remove(data);
25385         
25386         this.insert(newIndex, data);
25387         
25388     }
25389 });/*
25390  * Based on:
25391  * Ext JS Library 1.1.1
25392  * Copyright(c) 2006-2007, Ext JS, LLC.
25393  *
25394  * Originally Released Under LGPL - original licence link has changed is not relivant.
25395  *
25396  * Fork - LGPL
25397  * <script type="text/javascript">
25398  */
25399
25400 /**
25401  * @class Roo.data.SimpleStore
25402  * @extends Roo.data.Store
25403  * Small helper class to make creating Stores from Array data easier.
25404  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25405  * @cfg {Array} fields An array of field definition objects, or field name strings.
25406  * @cfg {Object} an existing reader (eg. copied from another store)
25407  * @cfg {Array} data The multi-dimensional array of data
25408  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25409  * @cfg {Roo.data.Reader} reader  [not-required] 
25410  * @constructor
25411  * @param {Object} config
25412  */
25413 Roo.data.SimpleStore = function(config)
25414 {
25415     Roo.data.SimpleStore.superclass.constructor.call(this, {
25416         isLocal : true,
25417         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25418                 id: config.id
25419             },
25420             Roo.data.Record.create(config.fields)
25421         ),
25422         proxy : new Roo.data.MemoryProxy(config.data)
25423     });
25424     this.load();
25425 };
25426 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25427  * Based on:
25428  * Ext JS Library 1.1.1
25429  * Copyright(c) 2006-2007, Ext JS, LLC.
25430  *
25431  * Originally Released Under LGPL - original licence link has changed is not relivant.
25432  *
25433  * Fork - LGPL
25434  * <script type="text/javascript">
25435  */
25436
25437 /**
25438 /**
25439  * @extends Roo.data.Store
25440  * @class Roo.data.JsonStore
25441  * Small helper class to make creating Stores for JSON data easier. <br/>
25442 <pre><code>
25443 var store = new Roo.data.JsonStore({
25444     url: 'get-images.php',
25445     root: 'images',
25446     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25447 });
25448 </code></pre>
25449  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25450  * JsonReader and HttpProxy (unless inline data is provided).</b>
25451  * @cfg {Array} fields An array of field definition objects, or field name strings.
25452  * @constructor
25453  * @param {Object} config
25454  */
25455 Roo.data.JsonStore = function(c){
25456     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25457         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25458         reader: new Roo.data.JsonReader(c, c.fields)
25459     }));
25460 };
25461 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25462  * Based on:
25463  * Ext JS Library 1.1.1
25464  * Copyright(c) 2006-2007, Ext JS, LLC.
25465  *
25466  * Originally Released Under LGPL - original licence link has changed is not relivant.
25467  *
25468  * Fork - LGPL
25469  * <script type="text/javascript">
25470  */
25471
25472  
25473 Roo.data.Field = function(config){
25474     if(typeof config == "string"){
25475         config = {name: config};
25476     }
25477     Roo.apply(this, config);
25478     
25479     if(!this.type){
25480         this.type = "auto";
25481     }
25482     
25483     var st = Roo.data.SortTypes;
25484     // named sortTypes are supported, here we look them up
25485     if(typeof this.sortType == "string"){
25486         this.sortType = st[this.sortType];
25487     }
25488     
25489     // set default sortType for strings and dates
25490     if(!this.sortType){
25491         switch(this.type){
25492             case "string":
25493                 this.sortType = st.asUCString;
25494                 break;
25495             case "date":
25496                 this.sortType = st.asDate;
25497                 break;
25498             default:
25499                 this.sortType = st.none;
25500         }
25501     }
25502
25503     // define once
25504     var stripRe = /[\$,%]/g;
25505
25506     // prebuilt conversion function for this field, instead of
25507     // switching every time we're reading a value
25508     if(!this.convert){
25509         var cv, dateFormat = this.dateFormat;
25510         switch(this.type){
25511             case "":
25512             case "auto":
25513             case undefined:
25514                 cv = function(v){ return v; };
25515                 break;
25516             case "string":
25517                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25518                 break;
25519             case "int":
25520                 cv = function(v){
25521                     return v !== undefined && v !== null && v !== '' ?
25522                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25523                     };
25524                 break;
25525             case "float":
25526                 cv = function(v){
25527                     return v !== undefined && v !== null && v !== '' ?
25528                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25529                     };
25530                 break;
25531             case "bool":
25532             case "boolean":
25533                 cv = function(v){ return v === true || v === "true" || v == 1; };
25534                 break;
25535             case "date":
25536                 cv = function(v){
25537                     if(!v){
25538                         return '';
25539                     }
25540                     if(v instanceof Date){
25541                         return v;
25542                     }
25543                     if(dateFormat){
25544                         if(dateFormat == "timestamp"){
25545                             return new Date(v*1000);
25546                         }
25547                         return Date.parseDate(v, dateFormat);
25548                     }
25549                     var parsed = Date.parse(v);
25550                     return parsed ? new Date(parsed) : null;
25551                 };
25552              break;
25553             
25554         }
25555         this.convert = cv;
25556     }
25557 };
25558
25559 Roo.data.Field.prototype = {
25560     dateFormat: null,
25561     defaultValue: "",
25562     mapping: null,
25563     sortType : null,
25564     sortDir : "ASC"
25565 };/*
25566  * Based on:
25567  * Ext JS Library 1.1.1
25568  * Copyright(c) 2006-2007, Ext JS, LLC.
25569  *
25570  * Originally Released Under LGPL - original licence link has changed is not relivant.
25571  *
25572  * Fork - LGPL
25573  * <script type="text/javascript">
25574  */
25575  
25576 // Base class for reading structured data from a data source.  This class is intended to be
25577 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25578
25579 /**
25580  * @class Roo.data.DataReader
25581  * @abstract
25582  * Base class for reading structured data from a data source.  This class is intended to be
25583  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25584  */
25585
25586 Roo.data.DataReader = function(meta, recordType){
25587     
25588     this.meta = meta;
25589     
25590     this.recordType = recordType instanceof Array ? 
25591         Roo.data.Record.create(recordType) : recordType;
25592 };
25593
25594 Roo.data.DataReader.prototype = {
25595     
25596     
25597     readerType : 'Data',
25598      /**
25599      * Create an empty record
25600      * @param {Object} data (optional) - overlay some values
25601      * @return {Roo.data.Record} record created.
25602      */
25603     newRow :  function(d) {
25604         var da =  {};
25605         this.recordType.prototype.fields.each(function(c) {
25606             switch( c.type) {
25607                 case 'int' : da[c.name] = 0; break;
25608                 case 'date' : da[c.name] = new Date(); break;
25609                 case 'float' : da[c.name] = 0.0; break;
25610                 case 'boolean' : da[c.name] = false; break;
25611                 default : da[c.name] = ""; break;
25612             }
25613             
25614         });
25615         return new this.recordType(Roo.apply(da, d));
25616     }
25617     
25618     
25619 };/*
25620  * Based on:
25621  * Ext JS Library 1.1.1
25622  * Copyright(c) 2006-2007, Ext JS, LLC.
25623  *
25624  * Originally Released Under LGPL - original licence link has changed is not relivant.
25625  *
25626  * Fork - LGPL
25627  * <script type="text/javascript">
25628  */
25629
25630 /**
25631  * @class Roo.data.DataProxy
25632  * @extends Roo.util.Observable
25633  * @abstract
25634  * This class is an abstract base class for implementations which provide retrieval of
25635  * unformatted data objects.<br>
25636  * <p>
25637  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25638  * (of the appropriate type which knows how to parse the data object) to provide a block of
25639  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25640  * <p>
25641  * Custom implementations must implement the load method as described in
25642  * {@link Roo.data.HttpProxy#load}.
25643  */
25644 Roo.data.DataProxy = function(){
25645     this.addEvents({
25646         /**
25647          * @event beforeload
25648          * Fires before a network request is made to retrieve a data object.
25649          * @param {Object} This DataProxy object.
25650          * @param {Object} params The params parameter to the load function.
25651          */
25652         beforeload : true,
25653         /**
25654          * @event load
25655          * Fires before the load method's callback is called.
25656          * @param {Object} This DataProxy object.
25657          * @param {Object} o The data object.
25658          * @param {Object} arg The callback argument object passed to the load function.
25659          */
25660         load : true,
25661         /**
25662          * @event loadexception
25663          * Fires if an Exception occurs during data retrieval.
25664          * @param {Object} This DataProxy object.
25665          * @param {Object} o The data object.
25666          * @param {Object} arg The callback argument object passed to the load function.
25667          * @param {Object} e The Exception.
25668          */
25669         loadexception : true
25670     });
25671     Roo.data.DataProxy.superclass.constructor.call(this);
25672 };
25673
25674 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25675
25676     /**
25677      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25678      */
25679 /*
25680  * Based on:
25681  * Ext JS Library 1.1.1
25682  * Copyright(c) 2006-2007, Ext JS, LLC.
25683  *
25684  * Originally Released Under LGPL - original licence link has changed is not relivant.
25685  *
25686  * Fork - LGPL
25687  * <script type="text/javascript">
25688  */
25689 /**
25690  * @class Roo.data.MemoryProxy
25691  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25692  * to the Reader when its load method is called.
25693  * @constructor
25694  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25695  */
25696 Roo.data.MemoryProxy = function(data){
25697     if (data.data) {
25698         data = data.data;
25699     }
25700     Roo.data.MemoryProxy.superclass.constructor.call(this);
25701     this.data = data;
25702 };
25703
25704 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25705     
25706     /**
25707      * Load data from the requested source (in this case an in-memory
25708      * data object passed to the constructor), read the data object into
25709      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25710      * process that block using the passed callback.
25711      * @param {Object} params This parameter is not used by the MemoryProxy class.
25712      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25713      * object into a block of Roo.data.Records.
25714      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25715      * The function must be passed <ul>
25716      * <li>The Record block object</li>
25717      * <li>The "arg" argument from the load function</li>
25718      * <li>A boolean success indicator</li>
25719      * </ul>
25720      * @param {Object} scope The scope in which to call the callback
25721      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25722      */
25723     load : function(params, reader, callback, scope, arg){
25724         params = params || {};
25725         var result;
25726         try {
25727             result = reader.readRecords(params.data ? params.data :this.data);
25728         }catch(e){
25729             this.fireEvent("loadexception", this, arg, null, e);
25730             callback.call(scope, null, arg, false);
25731             return;
25732         }
25733         callback.call(scope, result, arg, true);
25734     },
25735     
25736     // private
25737     update : function(params, records){
25738         
25739     }
25740 });/*
25741  * Based on:
25742  * Ext JS Library 1.1.1
25743  * Copyright(c) 2006-2007, Ext JS, LLC.
25744  *
25745  * Originally Released Under LGPL - original licence link has changed is not relivant.
25746  *
25747  * Fork - LGPL
25748  * <script type="text/javascript">
25749  */
25750 /**
25751  * @class Roo.data.HttpProxy
25752  * @extends Roo.data.DataProxy
25753  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25754  * configured to reference a certain URL.<br><br>
25755  * <p>
25756  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25757  * from which the running page was served.<br><br>
25758  * <p>
25759  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25760  * <p>
25761  * Be aware that to enable the browser to parse an XML document, the server must set
25762  * the Content-Type header in the HTTP response to "text/xml".
25763  * @constructor
25764  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25765  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25766  * will be used to make the request.
25767  */
25768 Roo.data.HttpProxy = function(conn){
25769     Roo.data.HttpProxy.superclass.constructor.call(this);
25770     // is conn a conn config or a real conn?
25771     this.conn = conn;
25772     this.useAjax = !conn || !conn.events;
25773   
25774 };
25775
25776 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25777     // thse are take from connection...
25778     
25779     /**
25780      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25781      */
25782     /**
25783      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25784      * extra parameters to each request made by this object. (defaults to undefined)
25785      */
25786     /**
25787      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25788      *  to each request made by this object. (defaults to undefined)
25789      */
25790     /**
25791      * @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)
25792      */
25793     /**
25794      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25795      */
25796      /**
25797      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25798      * @type Boolean
25799      */
25800   
25801
25802     /**
25803      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25804      * @type Boolean
25805      */
25806     /**
25807      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25808      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25809      * a finer-grained basis than the DataProxy events.
25810      */
25811     getConnection : function(){
25812         return this.useAjax ? Roo.Ajax : this.conn;
25813     },
25814
25815     /**
25816      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25817      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25818      * process that block using the passed callback.
25819      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25820      * for the request to the remote server.
25821      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25822      * object into a block of Roo.data.Records.
25823      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25824      * The function must be passed <ul>
25825      * <li>The Record block object</li>
25826      * <li>The "arg" argument from the load function</li>
25827      * <li>A boolean success indicator</li>
25828      * </ul>
25829      * @param {Object} scope The scope in which to call the callback
25830      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25831      */
25832     load : function(params, reader, callback, scope, arg){
25833         if(this.fireEvent("beforeload", this, params) !== false){
25834             var  o = {
25835                 params : params || {},
25836                 request: {
25837                     callback : callback,
25838                     scope : scope,
25839                     arg : arg
25840                 },
25841                 reader: reader,
25842                 callback : this.loadResponse,
25843                 scope: this
25844             };
25845             if(this.useAjax){
25846                 Roo.applyIf(o, this.conn);
25847                 if(this.activeRequest){
25848                     Roo.Ajax.abort(this.activeRequest);
25849                 }
25850                 this.activeRequest = Roo.Ajax.request(o);
25851             }else{
25852                 this.conn.request(o);
25853             }
25854         }else{
25855             callback.call(scope||this, null, arg, false);
25856         }
25857     },
25858
25859     // private
25860     loadResponse : function(o, success, response){
25861         delete this.activeRequest;
25862         if(!success){
25863             this.fireEvent("loadexception", this, o, response);
25864             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25865             return;
25866         }
25867         var result;
25868         try {
25869             result = o.reader.read(response);
25870         }catch(e){
25871             o.success = false;
25872             o.raw = { errorMsg : response.responseText };
25873             this.fireEvent("loadexception", this, o, response, e);
25874             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25875             return;
25876         }
25877         
25878         this.fireEvent("load", this, o, o.request.arg);
25879         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25880     },
25881
25882     // private
25883     update : function(dataSet){
25884
25885     },
25886
25887     // private
25888     updateResponse : function(dataSet){
25889
25890     }
25891 });/*
25892  * Based on:
25893  * Ext JS Library 1.1.1
25894  * Copyright(c) 2006-2007, Ext JS, LLC.
25895  *
25896  * Originally Released Under LGPL - original licence link has changed is not relivant.
25897  *
25898  * Fork - LGPL
25899  * <script type="text/javascript">
25900  */
25901
25902 /**
25903  * @class Roo.data.ScriptTagProxy
25904  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25905  * other than the originating domain of the running page.<br><br>
25906  * <p>
25907  * <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
25908  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25909  * <p>
25910  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25911  * source code that is used as the source inside a &lt;script> tag.<br><br>
25912  * <p>
25913  * In order for the browser to process the returned data, the server must wrap the data object
25914  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25915  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25916  * depending on whether the callback name was passed:
25917  * <p>
25918  * <pre><code>
25919 boolean scriptTag = false;
25920 String cb = request.getParameter("callback");
25921 if (cb != null) {
25922     scriptTag = true;
25923     response.setContentType("text/javascript");
25924 } else {
25925     response.setContentType("application/x-json");
25926 }
25927 Writer out = response.getWriter();
25928 if (scriptTag) {
25929     out.write(cb + "(");
25930 }
25931 out.print(dataBlock.toJsonString());
25932 if (scriptTag) {
25933     out.write(");");
25934 }
25935 </pre></code>
25936  *
25937  * @constructor
25938  * @param {Object} config A configuration object.
25939  */
25940 Roo.data.ScriptTagProxy = function(config){
25941     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25942     Roo.apply(this, config);
25943     this.head = document.getElementsByTagName("head")[0];
25944 };
25945
25946 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25947
25948 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25949     /**
25950      * @cfg {String} url The URL from which to request the data object.
25951      */
25952     /**
25953      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25954      */
25955     timeout : 30000,
25956     /**
25957      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25958      * the server the name of the callback function set up by the load call to process the returned data object.
25959      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25960      * javascript output which calls this named function passing the data object as its only parameter.
25961      */
25962     callbackParam : "callback",
25963     /**
25964      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25965      * name to the request.
25966      */
25967     nocache : true,
25968
25969     /**
25970      * Load data from the configured URL, read the data object into
25971      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25972      * process that block using the passed callback.
25973      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25974      * for the request to the remote server.
25975      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25976      * object into a block of Roo.data.Records.
25977      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25978      * The function must be passed <ul>
25979      * <li>The Record block object</li>
25980      * <li>The "arg" argument from the load function</li>
25981      * <li>A boolean success indicator</li>
25982      * </ul>
25983      * @param {Object} scope The scope in which to call the callback
25984      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25985      */
25986     load : function(params, reader, callback, scope, arg){
25987         if(this.fireEvent("beforeload", this, params) !== false){
25988
25989             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25990
25991             var url = this.url;
25992             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25993             if(this.nocache){
25994                 url += "&_dc=" + (new Date().getTime());
25995             }
25996             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25997             var trans = {
25998                 id : transId,
25999                 cb : "stcCallback"+transId,
26000                 scriptId : "stcScript"+transId,
26001                 params : params,
26002                 arg : arg,
26003                 url : url,
26004                 callback : callback,
26005                 scope : scope,
26006                 reader : reader
26007             };
26008             var conn = this;
26009
26010             window[trans.cb] = function(o){
26011                 conn.handleResponse(o, trans);
26012             };
26013
26014             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26015
26016             if(this.autoAbort !== false){
26017                 this.abort();
26018             }
26019
26020             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26021
26022             var script = document.createElement("script");
26023             script.setAttribute("src", url);
26024             script.setAttribute("type", "text/javascript");
26025             script.setAttribute("id", trans.scriptId);
26026             this.head.appendChild(script);
26027
26028             this.trans = trans;
26029         }else{
26030             callback.call(scope||this, null, arg, false);
26031         }
26032     },
26033
26034     // private
26035     isLoading : function(){
26036         return this.trans ? true : false;
26037     },
26038
26039     /**
26040      * Abort the current server request.
26041      */
26042     abort : function(){
26043         if(this.isLoading()){
26044             this.destroyTrans(this.trans);
26045         }
26046     },
26047
26048     // private
26049     destroyTrans : function(trans, isLoaded){
26050         this.head.removeChild(document.getElementById(trans.scriptId));
26051         clearTimeout(trans.timeoutId);
26052         if(isLoaded){
26053             window[trans.cb] = undefined;
26054             try{
26055                 delete window[trans.cb];
26056             }catch(e){}
26057         }else{
26058             // if hasn't been loaded, wait for load to remove it to prevent script error
26059             window[trans.cb] = function(){
26060                 window[trans.cb] = undefined;
26061                 try{
26062                     delete window[trans.cb];
26063                 }catch(e){}
26064             };
26065         }
26066     },
26067
26068     // private
26069     handleResponse : function(o, trans){
26070         this.trans = false;
26071         this.destroyTrans(trans, true);
26072         var result;
26073         try {
26074             result = trans.reader.readRecords(o);
26075         }catch(e){
26076             this.fireEvent("loadexception", this, o, trans.arg, e);
26077             trans.callback.call(trans.scope||window, null, trans.arg, false);
26078             return;
26079         }
26080         this.fireEvent("load", this, o, trans.arg);
26081         trans.callback.call(trans.scope||window, result, trans.arg, true);
26082     },
26083
26084     // private
26085     handleFailure : function(trans){
26086         this.trans = false;
26087         this.destroyTrans(trans, false);
26088         this.fireEvent("loadexception", this, null, trans.arg);
26089         trans.callback.call(trans.scope||window, null, trans.arg, false);
26090     }
26091 });/*
26092  * Based on:
26093  * Ext JS Library 1.1.1
26094  * Copyright(c) 2006-2007, Ext JS, LLC.
26095  *
26096  * Originally Released Under LGPL - original licence link has changed is not relivant.
26097  *
26098  * Fork - LGPL
26099  * <script type="text/javascript">
26100  */
26101
26102 /**
26103  * @class Roo.data.JsonReader
26104  * @extends Roo.data.DataReader
26105  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26106  * based on mappings in a provided Roo.data.Record constructor.
26107  * 
26108  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26109  * in the reply previously. 
26110  * 
26111  * <p>
26112  * Example code:
26113  * <pre><code>
26114 var RecordDef = Roo.data.Record.create([
26115     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26116     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26117 ]);
26118 var myReader = new Roo.data.JsonReader({
26119     totalProperty: "results",    // The property which contains the total dataset size (optional)
26120     root: "rows",                // The property which contains an Array of row objects
26121     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26122 }, RecordDef);
26123 </code></pre>
26124  * <p>
26125  * This would consume a JSON file like this:
26126  * <pre><code>
26127 { 'results': 2, 'rows': [
26128     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26129     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26130 }
26131 </code></pre>
26132  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26133  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26134  * paged from the remote server.
26135  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26136  * @cfg {String} root name of the property which contains the Array of row objects.
26137  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26138  * @cfg {Array} fields Array of field definition objects
26139  * @constructor
26140  * Create a new JsonReader
26141  * @param {Object} meta Metadata configuration options
26142  * @param {Object} recordType Either an Array of field definition objects,
26143  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26144  */
26145 Roo.data.JsonReader = function(meta, recordType){
26146     
26147     meta = meta || {};
26148     // set some defaults:
26149     Roo.applyIf(meta, {
26150         totalProperty: 'total',
26151         successProperty : 'success',
26152         root : 'data',
26153         id : 'id'
26154     });
26155     
26156     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26157 };
26158 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26159     
26160     readerType : 'Json',
26161     
26162     /**
26163      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26164      * Used by Store query builder to append _requestMeta to params.
26165      * 
26166      */
26167     metaFromRemote : false,
26168     /**
26169      * This method is only used by a DataProxy which has retrieved data from a remote server.
26170      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26171      * @return {Object} data A data block which is used by an Roo.data.Store object as
26172      * a cache of Roo.data.Records.
26173      */
26174     read : function(response){
26175         var json = response.responseText;
26176        
26177         var o = /* eval:var:o */ eval("("+json+")");
26178         if(!o) {
26179             throw {message: "JsonReader.read: Json object not found"};
26180         }
26181         
26182         if(o.metaData){
26183             
26184             delete this.ef;
26185             this.metaFromRemote = true;
26186             this.meta = o.metaData;
26187             this.recordType = Roo.data.Record.create(o.metaData.fields);
26188             this.onMetaChange(this.meta, this.recordType, o);
26189         }
26190         return this.readRecords(o);
26191     },
26192
26193     // private function a store will implement
26194     onMetaChange : function(meta, recordType, o){
26195
26196     },
26197
26198     /**
26199          * @ignore
26200          */
26201     simpleAccess: function(obj, subsc) {
26202         return obj[subsc];
26203     },
26204
26205         /**
26206          * @ignore
26207          */
26208     getJsonAccessor: function(){
26209         var re = /[\[\.]/;
26210         return function(expr) {
26211             try {
26212                 return(re.test(expr))
26213                     ? new Function("obj", "return obj." + expr)
26214                     : function(obj){
26215                         return obj[expr];
26216                     };
26217             } catch(e){}
26218             return Roo.emptyFn;
26219         };
26220     }(),
26221
26222     /**
26223      * Create a data block containing Roo.data.Records from an XML document.
26224      * @param {Object} o An object which contains an Array of row objects in the property specified
26225      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26226      * which contains the total size of the dataset.
26227      * @return {Object} data A data block which is used by an Roo.data.Store object as
26228      * a cache of Roo.data.Records.
26229      */
26230     readRecords : function(o){
26231         /**
26232          * After any data loads, the raw JSON data is available for further custom processing.
26233          * @type Object
26234          */
26235         this.o = o;
26236         var s = this.meta, Record = this.recordType,
26237             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26238
26239 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26240         if (!this.ef) {
26241             if(s.totalProperty) {
26242                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26243                 }
26244                 if(s.successProperty) {
26245                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26246                 }
26247                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26248                 if (s.id) {
26249                         var g = this.getJsonAccessor(s.id);
26250                         this.getId = function(rec) {
26251                                 var r = g(rec);  
26252                                 return (r === undefined || r === "") ? null : r;
26253                         };
26254                 } else {
26255                         this.getId = function(){return null;};
26256                 }
26257             this.ef = [];
26258             for(var jj = 0; jj < fl; jj++){
26259                 f = fi[jj];
26260                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26261                 this.ef[jj] = this.getJsonAccessor(map);
26262             }
26263         }
26264
26265         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26266         if(s.totalProperty){
26267             var vt = parseInt(this.getTotal(o), 10);
26268             if(!isNaN(vt)){
26269                 totalRecords = vt;
26270             }
26271         }
26272         if(s.successProperty){
26273             var vs = this.getSuccess(o);
26274             if(vs === false || vs === 'false'){
26275                 success = false;
26276             }
26277         }
26278         var records = [];
26279         for(var i = 0; i < c; i++){
26280             var n = root[i];
26281             var values = {};
26282             var id = this.getId(n);
26283             for(var j = 0; j < fl; j++){
26284                 f = fi[j];
26285                                 var v = this.ef[j](n);
26286                                 if (!f.convert) {
26287                                         Roo.log('missing convert for ' + f.name);
26288                                         Roo.log(f);
26289                                         continue;
26290                                 }
26291                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26292             }
26293                         if (!Record) {
26294                                 return {
26295                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26296                                         success : false,
26297                                         records : [],
26298                                         totalRecords : 0
26299                                 };
26300                         }
26301             var record = new Record(values, id);
26302             record.json = n;
26303             records[i] = record;
26304         }
26305         return {
26306             raw : o,
26307             success : success,
26308             records : records,
26309             totalRecords : totalRecords
26310         };
26311     },
26312     // used when loading children.. @see loadDataFromChildren
26313     toLoadData: function(rec)
26314     {
26315         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26316         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26317         return { data : data, total : data.length };
26318         
26319     }
26320 });/*
26321  * Based on:
26322  * Ext JS Library 1.1.1
26323  * Copyright(c) 2006-2007, Ext JS, LLC.
26324  *
26325  * Originally Released Under LGPL - original licence link has changed is not relivant.
26326  *
26327  * Fork - LGPL
26328  * <script type="text/javascript">
26329  */
26330
26331 /**
26332  * @class Roo.data.XmlReader
26333  * @extends Roo.data.DataReader
26334  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26335  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26336  * <p>
26337  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26338  * header in the HTTP response must be set to "text/xml".</em>
26339  * <p>
26340  * Example code:
26341  * <pre><code>
26342 var RecordDef = Roo.data.Record.create([
26343    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26344    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26345 ]);
26346 var myReader = new Roo.data.XmlReader({
26347    totalRecords: "results", // The element which contains the total dataset size (optional)
26348    record: "row",           // The repeated element which contains row information
26349    id: "id"                 // The element within the row that provides an ID for the record (optional)
26350 }, RecordDef);
26351 </code></pre>
26352  * <p>
26353  * This would consume an XML file like this:
26354  * <pre><code>
26355 &lt;?xml?>
26356 &lt;dataset>
26357  &lt;results>2&lt;/results>
26358  &lt;row>
26359    &lt;id>1&lt;/id>
26360    &lt;name>Bill&lt;/name>
26361    &lt;occupation>Gardener&lt;/occupation>
26362  &lt;/row>
26363  &lt;row>
26364    &lt;id>2&lt;/id>
26365    &lt;name>Ben&lt;/name>
26366    &lt;occupation>Horticulturalist&lt;/occupation>
26367  &lt;/row>
26368 &lt;/dataset>
26369 </code></pre>
26370  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26371  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26372  * paged from the remote server.
26373  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26374  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26375  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26376  * a record identifier value.
26377  * @constructor
26378  * Create a new XmlReader
26379  * @param {Object} meta Metadata configuration options
26380  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26381  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26382  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26383  */
26384 Roo.data.XmlReader = function(meta, recordType){
26385     meta = meta || {};
26386     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26387 };
26388 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26389     
26390     readerType : 'Xml',
26391     
26392     /**
26393      * This method is only used by a DataProxy which has retrieved data from a remote server.
26394          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26395          * to contain a method called 'responseXML' that returns an XML document object.
26396      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26397      * a cache of Roo.data.Records.
26398      */
26399     read : function(response){
26400         var doc = response.responseXML;
26401         if(!doc) {
26402             throw {message: "XmlReader.read: XML Document not available"};
26403         }
26404         return this.readRecords(doc);
26405     },
26406
26407     /**
26408      * Create a data block containing Roo.data.Records from an XML document.
26409          * @param {Object} doc A parsed XML document.
26410      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26411      * a cache of Roo.data.Records.
26412      */
26413     readRecords : function(doc){
26414         /**
26415          * After any data loads/reads, the raw XML Document is available for further custom processing.
26416          * @type XMLDocument
26417          */
26418         this.xmlData = doc;
26419         var root = doc.documentElement || doc;
26420         var q = Roo.DomQuery;
26421         var recordType = this.recordType, fields = recordType.prototype.fields;
26422         var sid = this.meta.id;
26423         var totalRecords = 0, success = true;
26424         if(this.meta.totalRecords){
26425             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26426         }
26427         
26428         if(this.meta.success){
26429             var sv = q.selectValue(this.meta.success, root, true);
26430             success = sv !== false && sv !== 'false';
26431         }
26432         var records = [];
26433         var ns = q.select(this.meta.record, root);
26434         for(var i = 0, len = ns.length; i < len; i++) {
26435                 var n = ns[i];
26436                 var values = {};
26437                 var id = sid ? q.selectValue(sid, n) : undefined;
26438                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26439                     var f = fields.items[j];
26440                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26441                     v = f.convert(v);
26442                     values[f.name] = v;
26443                 }
26444                 var record = new recordType(values, id);
26445                 record.node = n;
26446                 records[records.length] = record;
26447             }
26448
26449             return {
26450                 success : success,
26451                 records : records,
26452                 totalRecords : totalRecords || records.length
26453             };
26454     }
26455 });/*
26456  * Based on:
26457  * Ext JS Library 1.1.1
26458  * Copyright(c) 2006-2007, Ext JS, LLC.
26459  *
26460  * Originally Released Under LGPL - original licence link has changed is not relivant.
26461  *
26462  * Fork - LGPL
26463  * <script type="text/javascript">
26464  */
26465
26466 /**
26467  * @class Roo.data.ArrayReader
26468  * @extends Roo.data.DataReader
26469  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26470  * Each element of that Array represents a row of data fields. The
26471  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26472  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26473  * <p>
26474  * Example code:.
26475  * <pre><code>
26476 var RecordDef = Roo.data.Record.create([
26477     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26478     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26479 ]);
26480 var myReader = new Roo.data.ArrayReader({
26481     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26482 }, RecordDef);
26483 </code></pre>
26484  * <p>
26485  * This would consume an Array like this:
26486  * <pre><code>
26487 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26488   </code></pre>
26489  
26490  * @constructor
26491  * Create a new JsonReader
26492  * @param {Object} meta Metadata configuration options.
26493  * @param {Object|Array} recordType Either an Array of field definition objects
26494  * 
26495  * @cfg {Array} fields Array of field definition objects
26496  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26497  * as specified to {@link Roo.data.Record#create},
26498  * or an {@link Roo.data.Record} object
26499  *
26500  * 
26501  * created using {@link Roo.data.Record#create}.
26502  */
26503 Roo.data.ArrayReader = function(meta, recordType)
26504 {    
26505     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26506 };
26507
26508 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26509     
26510       /**
26511      * Create a data block containing Roo.data.Records from an XML document.
26512      * @param {Object} o An Array of row objects which represents the dataset.
26513      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26514      * a cache of Roo.data.Records.
26515      */
26516     readRecords : function(o)
26517     {
26518         var sid = this.meta ? this.meta.id : null;
26519         var recordType = this.recordType, fields = recordType.prototype.fields;
26520         var records = [];
26521         var root = o;
26522         for(var i = 0; i < root.length; i++){
26523             var n = root[i];
26524             var values = {};
26525             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26526             for(var j = 0, jlen = fields.length; j < jlen; j++){
26527                 var f = fields.items[j];
26528                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26529                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26530                 v = f.convert(v);
26531                 values[f.name] = v;
26532             }
26533             var record = new recordType(values, id);
26534             record.json = n;
26535             records[records.length] = record;
26536         }
26537         return {
26538             records : records,
26539             totalRecords : records.length
26540         };
26541     },
26542     // used when loading children.. @see loadDataFromChildren
26543     toLoadData: function(rec)
26544     {
26545         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26546         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26547         
26548     }
26549     
26550     
26551 });/*
26552  * Based on:
26553  * Ext JS Library 1.1.1
26554  * Copyright(c) 2006-2007, Ext JS, LLC.
26555  *
26556  * Originally Released Under LGPL - original licence link has changed is not relivant.
26557  *
26558  * Fork - LGPL
26559  * <script type="text/javascript">
26560  */
26561
26562
26563 /**
26564  * @class Roo.data.Tree
26565  * @extends Roo.util.Observable
26566  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26567  * in the tree have most standard DOM functionality.
26568  * @constructor
26569  * @param {Node} root (optional) The root node
26570  */
26571 Roo.data.Tree = function(root){
26572    this.nodeHash = {};
26573    /**
26574     * The root node for this tree
26575     * @type Node
26576     */
26577    this.root = null;
26578    if(root){
26579        this.setRootNode(root);
26580    }
26581    this.addEvents({
26582        /**
26583         * @event append
26584         * Fires when a new child node is appended to a node in this tree.
26585         * @param {Tree} tree The owner tree
26586         * @param {Node} parent The parent node
26587         * @param {Node} node The newly appended node
26588         * @param {Number} index The index of the newly appended node
26589         */
26590        "append" : true,
26591        /**
26592         * @event remove
26593         * Fires when a child node is removed from a node in this tree.
26594         * @param {Tree} tree The owner tree
26595         * @param {Node} parent The parent node
26596         * @param {Node} node The child node removed
26597         */
26598        "remove" : true,
26599        /**
26600         * @event move
26601         * Fires when a node is moved to a new location in the tree
26602         * @param {Tree} tree The owner tree
26603         * @param {Node} node The node moved
26604         * @param {Node} oldParent The old parent of this node
26605         * @param {Node} newParent The new parent of this node
26606         * @param {Number} index The index it was moved to
26607         */
26608        "move" : true,
26609        /**
26610         * @event insert
26611         * Fires when a new child node is inserted in a node in this tree.
26612         * @param {Tree} tree The owner tree
26613         * @param {Node} parent The parent node
26614         * @param {Node} node The child node inserted
26615         * @param {Node} refNode The child node the node was inserted before
26616         */
26617        "insert" : true,
26618        /**
26619         * @event beforeappend
26620         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26621         * @param {Tree} tree The owner tree
26622         * @param {Node} parent The parent node
26623         * @param {Node} node The child node to be appended
26624         */
26625        "beforeappend" : true,
26626        /**
26627         * @event beforeremove
26628         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26629         * @param {Tree} tree The owner tree
26630         * @param {Node} parent The parent node
26631         * @param {Node} node The child node to be removed
26632         */
26633        "beforeremove" : true,
26634        /**
26635         * @event beforemove
26636         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26637         * @param {Tree} tree The owner tree
26638         * @param {Node} node The node being moved
26639         * @param {Node} oldParent The parent of the node
26640         * @param {Node} newParent The new parent the node is moving to
26641         * @param {Number} index The index it is being moved to
26642         */
26643        "beforemove" : true,
26644        /**
26645         * @event beforeinsert
26646         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26647         * @param {Tree} tree The owner tree
26648         * @param {Node} parent The parent node
26649         * @param {Node} node The child node to be inserted
26650         * @param {Node} refNode The child node the node is being inserted before
26651         */
26652        "beforeinsert" : true
26653    });
26654
26655     Roo.data.Tree.superclass.constructor.call(this);
26656 };
26657
26658 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26659     pathSeparator: "/",
26660
26661     proxyNodeEvent : function(){
26662         return this.fireEvent.apply(this, arguments);
26663     },
26664
26665     /**
26666      * Returns the root node for this tree.
26667      * @return {Node}
26668      */
26669     getRootNode : function(){
26670         return this.root;
26671     },
26672
26673     /**
26674      * Sets the root node for this tree.
26675      * @param {Node} node
26676      * @return {Node}
26677      */
26678     setRootNode : function(node){
26679         this.root = node;
26680         node.ownerTree = this;
26681         node.isRoot = true;
26682         this.registerNode(node);
26683         return node;
26684     },
26685
26686     /**
26687      * Gets a node in this tree by its id.
26688      * @param {String} id
26689      * @return {Node}
26690      */
26691     getNodeById : function(id){
26692         return this.nodeHash[id];
26693     },
26694
26695     registerNode : function(node){
26696         this.nodeHash[node.id] = node;
26697     },
26698
26699     unregisterNode : function(node){
26700         delete this.nodeHash[node.id];
26701     },
26702
26703     toString : function(){
26704         return "[Tree"+(this.id?" "+this.id:"")+"]";
26705     }
26706 });
26707
26708 /**
26709  * @class Roo.data.Node
26710  * @extends Roo.util.Observable
26711  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26712  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26713  * @constructor
26714  * @param {Object} attributes The attributes/config for the node
26715  */
26716 Roo.data.Node = function(attributes){
26717     /**
26718      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26719      * @type {Object}
26720      */
26721     this.attributes = attributes || {};
26722     this.leaf = this.attributes.leaf;
26723     /**
26724      * The node id. @type String
26725      */
26726     this.id = this.attributes.id;
26727     if(!this.id){
26728         this.id = Roo.id(null, "ynode-");
26729         this.attributes.id = this.id;
26730     }
26731      
26732     
26733     /**
26734      * All child nodes of this node. @type Array
26735      */
26736     this.childNodes = [];
26737     if(!this.childNodes.indexOf){ // indexOf is a must
26738         this.childNodes.indexOf = function(o){
26739             for(var i = 0, len = this.length; i < len; i++){
26740                 if(this[i] == o) {
26741                     return i;
26742                 }
26743             }
26744             return -1;
26745         };
26746     }
26747     /**
26748      * The parent node for this node. @type Node
26749      */
26750     this.parentNode = null;
26751     /**
26752      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26753      */
26754     this.firstChild = null;
26755     /**
26756      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26757      */
26758     this.lastChild = null;
26759     /**
26760      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26761      */
26762     this.previousSibling = null;
26763     /**
26764      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26765      */
26766     this.nextSibling = null;
26767
26768     this.addEvents({
26769        /**
26770         * @event append
26771         * Fires when a new child node is appended
26772         * @param {Tree} tree The owner tree
26773         * @param {Node} this This node
26774         * @param {Node} node The newly appended node
26775         * @param {Number} index The index of the newly appended node
26776         */
26777        "append" : true,
26778        /**
26779         * @event remove
26780         * Fires when a child node is removed
26781         * @param {Tree} tree The owner tree
26782         * @param {Node} this This node
26783         * @param {Node} node The removed node
26784         */
26785        "remove" : true,
26786        /**
26787         * @event move
26788         * Fires when this node is moved to a new location in the tree
26789         * @param {Tree} tree The owner tree
26790         * @param {Node} this This node
26791         * @param {Node} oldParent The old parent of this node
26792         * @param {Node} newParent The new parent of this node
26793         * @param {Number} index The index it was moved to
26794         */
26795        "move" : true,
26796        /**
26797         * @event insert
26798         * Fires when a new child node is inserted.
26799         * @param {Tree} tree The owner tree
26800         * @param {Node} this This node
26801         * @param {Node} node The child node inserted
26802         * @param {Node} refNode The child node the node was inserted before
26803         */
26804        "insert" : true,
26805        /**
26806         * @event beforeappend
26807         * Fires before a new child is appended, return false to cancel the append.
26808         * @param {Tree} tree The owner tree
26809         * @param {Node} this This node
26810         * @param {Node} node The child node to be appended
26811         */
26812        "beforeappend" : true,
26813        /**
26814         * @event beforeremove
26815         * Fires before a child is removed, return false to cancel the remove.
26816         * @param {Tree} tree The owner tree
26817         * @param {Node} this This node
26818         * @param {Node} node The child node to be removed
26819         */
26820        "beforeremove" : true,
26821        /**
26822         * @event beforemove
26823         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26824         * @param {Tree} tree The owner tree
26825         * @param {Node} this This node
26826         * @param {Node} oldParent The parent of this node
26827         * @param {Node} newParent The new parent this node is moving to
26828         * @param {Number} index The index it is being moved to
26829         */
26830        "beforemove" : true,
26831        /**
26832         * @event beforeinsert
26833         * Fires before a new child is inserted, return false to cancel the insert.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} this This node
26836         * @param {Node} node The child node to be inserted
26837         * @param {Node} refNode The child node the node is being inserted before
26838         */
26839        "beforeinsert" : true
26840    });
26841     this.listeners = this.attributes.listeners;
26842     Roo.data.Node.superclass.constructor.call(this);
26843 };
26844
26845 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26846     fireEvent : function(evtName){
26847         // first do standard event for this node
26848         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26849             return false;
26850         }
26851         // then bubble it up to the tree if the event wasn't cancelled
26852         var ot = this.getOwnerTree();
26853         if(ot){
26854             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26855                 return false;
26856             }
26857         }
26858         return true;
26859     },
26860
26861     /**
26862      * Returns true if this node is a leaf
26863      * @return {Boolean}
26864      */
26865     isLeaf : function(){
26866         return this.leaf === true;
26867     },
26868
26869     // private
26870     setFirstChild : function(node){
26871         this.firstChild = node;
26872     },
26873
26874     //private
26875     setLastChild : function(node){
26876         this.lastChild = node;
26877     },
26878
26879
26880     /**
26881      * Returns true if this node is the last child of its parent
26882      * @return {Boolean}
26883      */
26884     isLast : function(){
26885        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26886     },
26887
26888     /**
26889      * Returns true if this node is the first child of its parent
26890      * @return {Boolean}
26891      */
26892     isFirst : function(){
26893        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26894     },
26895
26896     hasChildNodes : function(){
26897         return !this.isLeaf() && this.childNodes.length > 0;
26898     },
26899
26900     /**
26901      * Insert node(s) as the last child node of this node.
26902      * @param {Node/Array} node The node or Array of nodes to append
26903      * @return {Node} The appended node if single append, or null if an array was passed
26904      */
26905     appendChild : function(node){
26906         var multi = false;
26907         if(node instanceof Array){
26908             multi = node;
26909         }else if(arguments.length > 1){
26910             multi = arguments;
26911         }
26912         
26913         // if passed an array or multiple args do them one by one
26914         if(multi){
26915             for(var i = 0, len = multi.length; i < len; i++) {
26916                 this.appendChild(multi[i]);
26917             }
26918         }else{
26919             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26920                 return false;
26921             }
26922             var index = this.childNodes.length;
26923             var oldParent = node.parentNode;
26924             // it's a move, make sure we move it cleanly
26925             if(oldParent){
26926                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26927                     return false;
26928                 }
26929                 oldParent.removeChild(node);
26930             }
26931             
26932             index = this.childNodes.length;
26933             if(index == 0){
26934                 this.setFirstChild(node);
26935             }
26936             this.childNodes.push(node);
26937             node.parentNode = this;
26938             var ps = this.childNodes[index-1];
26939             if(ps){
26940                 node.previousSibling = ps;
26941                 ps.nextSibling = node;
26942             }else{
26943                 node.previousSibling = null;
26944             }
26945             node.nextSibling = null;
26946             this.setLastChild(node);
26947             node.setOwnerTree(this.getOwnerTree());
26948             this.fireEvent("append", this.ownerTree, this, node, index);
26949             if(this.ownerTree) {
26950                 this.ownerTree.fireEvent("appendnode", this, node, index);
26951             }
26952             if(oldParent){
26953                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26954             }
26955             return node;
26956         }
26957     },
26958
26959     /**
26960      * Removes a child node from this node.
26961      * @param {Node} node The node to remove
26962      * @return {Node} The removed node
26963      */
26964     removeChild : function(node){
26965         var index = this.childNodes.indexOf(node);
26966         if(index == -1){
26967             return false;
26968         }
26969         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26970             return false;
26971         }
26972
26973         // remove it from childNodes collection
26974         this.childNodes.splice(index, 1);
26975
26976         // update siblings
26977         if(node.previousSibling){
26978             node.previousSibling.nextSibling = node.nextSibling;
26979         }
26980         if(node.nextSibling){
26981             node.nextSibling.previousSibling = node.previousSibling;
26982         }
26983
26984         // update child refs
26985         if(this.firstChild == node){
26986             this.setFirstChild(node.nextSibling);
26987         }
26988         if(this.lastChild == node){
26989             this.setLastChild(node.previousSibling);
26990         }
26991
26992         node.setOwnerTree(null);
26993         // clear any references from the node
26994         node.parentNode = null;
26995         node.previousSibling = null;
26996         node.nextSibling = null;
26997         this.fireEvent("remove", this.ownerTree, this, node);
26998         return node;
26999     },
27000
27001     /**
27002      * Inserts the first node before the second node in this nodes childNodes collection.
27003      * @param {Node} node The node to insert
27004      * @param {Node} refNode The node to insert before (if null the node is appended)
27005      * @return {Node} The inserted node
27006      */
27007     insertBefore : function(node, refNode){
27008         if(!refNode){ // like standard Dom, refNode can be null for append
27009             return this.appendChild(node);
27010         }
27011         // nothing to do
27012         if(node == refNode){
27013             return false;
27014         }
27015
27016         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27017             return false;
27018         }
27019         var index = this.childNodes.indexOf(refNode);
27020         var oldParent = node.parentNode;
27021         var refIndex = index;
27022
27023         // when moving internally, indexes will change after remove
27024         if(oldParent == this && this.childNodes.indexOf(node) < index){
27025             refIndex--;
27026         }
27027
27028         // it's a move, make sure we move it cleanly
27029         if(oldParent){
27030             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27031                 return false;
27032             }
27033             oldParent.removeChild(node);
27034         }
27035         if(refIndex == 0){
27036             this.setFirstChild(node);
27037         }
27038         this.childNodes.splice(refIndex, 0, node);
27039         node.parentNode = this;
27040         var ps = this.childNodes[refIndex-1];
27041         if(ps){
27042             node.previousSibling = ps;
27043             ps.nextSibling = node;
27044         }else{
27045             node.previousSibling = null;
27046         }
27047         node.nextSibling = refNode;
27048         refNode.previousSibling = node;
27049         node.setOwnerTree(this.getOwnerTree());
27050         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27051         if(oldParent){
27052             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27053         }
27054         return node;
27055     },
27056
27057     /**
27058      * Returns the child node at the specified index.
27059      * @param {Number} index
27060      * @return {Node}
27061      */
27062     item : function(index){
27063         return this.childNodes[index];
27064     },
27065
27066     /**
27067      * Replaces one child node in this node with another.
27068      * @param {Node} newChild The replacement node
27069      * @param {Node} oldChild The node to replace
27070      * @return {Node} The replaced node
27071      */
27072     replaceChild : function(newChild, oldChild){
27073         this.insertBefore(newChild, oldChild);
27074         this.removeChild(oldChild);
27075         return oldChild;
27076     },
27077
27078     /**
27079      * Returns the index of a child node
27080      * @param {Node} node
27081      * @return {Number} The index of the node or -1 if it was not found
27082      */
27083     indexOf : function(child){
27084         return this.childNodes.indexOf(child);
27085     },
27086
27087     /**
27088      * Returns the tree this node is in.
27089      * @return {Tree}
27090      */
27091     getOwnerTree : function(){
27092         // if it doesn't have one, look for one
27093         if(!this.ownerTree){
27094             var p = this;
27095             while(p){
27096                 if(p.ownerTree){
27097                     this.ownerTree = p.ownerTree;
27098                     break;
27099                 }
27100                 p = p.parentNode;
27101             }
27102         }
27103         return this.ownerTree;
27104     },
27105
27106     /**
27107      * Returns depth of this node (the root node has a depth of 0)
27108      * @return {Number}
27109      */
27110     getDepth : function(){
27111         var depth = 0;
27112         var p = this;
27113         while(p.parentNode){
27114             ++depth;
27115             p = p.parentNode;
27116         }
27117         return depth;
27118     },
27119
27120     // private
27121     setOwnerTree : function(tree){
27122         // if it's move, we need to update everyone
27123         if(tree != this.ownerTree){
27124             if(this.ownerTree){
27125                 this.ownerTree.unregisterNode(this);
27126             }
27127             this.ownerTree = tree;
27128             var cs = this.childNodes;
27129             for(var i = 0, len = cs.length; i < len; i++) {
27130                 cs[i].setOwnerTree(tree);
27131             }
27132             if(tree){
27133                 tree.registerNode(this);
27134             }
27135         }
27136     },
27137
27138     /**
27139      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27140      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27141      * @return {String} The path
27142      */
27143     getPath : function(attr){
27144         attr = attr || "id";
27145         var p = this.parentNode;
27146         var b = [this.attributes[attr]];
27147         while(p){
27148             b.unshift(p.attributes[attr]);
27149             p = p.parentNode;
27150         }
27151         var sep = this.getOwnerTree().pathSeparator;
27152         return sep + b.join(sep);
27153     },
27154
27155     /**
27156      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27157      * function call will be the scope provided or the current node. The arguments to the function
27158      * will be the args provided or the current node. If the function returns false at any point,
27159      * the bubble is stopped.
27160      * @param {Function} fn The function to call
27161      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27162      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27163      */
27164     bubble : function(fn, scope, args){
27165         var p = this;
27166         while(p){
27167             if(fn.call(scope || p, args || p) === false){
27168                 break;
27169             }
27170             p = p.parentNode;
27171         }
27172     },
27173
27174     /**
27175      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27176      * function call will be the scope provided or the current node. The arguments to the function
27177      * will be the args provided or the current node. If the function returns false at any point,
27178      * the cascade is stopped on that branch.
27179      * @param {Function} fn The function to call
27180      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27181      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27182      */
27183     cascade : function(fn, scope, args){
27184         if(fn.call(scope || this, args || this) !== false){
27185             var cs = this.childNodes;
27186             for(var i = 0, len = cs.length; i < len; i++) {
27187                 cs[i].cascade(fn, scope, args);
27188             }
27189         }
27190     },
27191
27192     /**
27193      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27194      * function call will be the scope provided or the current node. The arguments to the function
27195      * will be the args provided or the current node. If the function returns false at any point,
27196      * the iteration stops.
27197      * @param {Function} fn The function to call
27198      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27199      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27200      */
27201     eachChild : function(fn, scope, args){
27202         var cs = this.childNodes;
27203         for(var i = 0, len = cs.length; i < len; i++) {
27204                 if(fn.call(scope || this, args || cs[i]) === false){
27205                     break;
27206                 }
27207         }
27208     },
27209
27210     /**
27211      * Finds the first child that has the attribute with the specified value.
27212      * @param {String} attribute The attribute name
27213      * @param {Mixed} value The value to search for
27214      * @return {Node} The found child or null if none was found
27215      */
27216     findChild : function(attribute, value){
27217         var cs = this.childNodes;
27218         for(var i = 0, len = cs.length; i < len; i++) {
27219                 if(cs[i].attributes[attribute] == value){
27220                     return cs[i];
27221                 }
27222         }
27223         return null;
27224     },
27225
27226     /**
27227      * Finds the first child by a custom function. The child matches if the function passed
27228      * returns true.
27229      * @param {Function} fn
27230      * @param {Object} scope (optional)
27231      * @return {Node} The found child or null if none was found
27232      */
27233     findChildBy : function(fn, scope){
27234         var cs = this.childNodes;
27235         for(var i = 0, len = cs.length; i < len; i++) {
27236                 if(fn.call(scope||cs[i], cs[i]) === true){
27237                     return cs[i];
27238                 }
27239         }
27240         return null;
27241     },
27242
27243     /**
27244      * Sorts this nodes children using the supplied sort function
27245      * @param {Function} fn
27246      * @param {Object} scope (optional)
27247      */
27248     sort : function(fn, scope){
27249         var cs = this.childNodes;
27250         var len = cs.length;
27251         if(len > 0){
27252             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27253             cs.sort(sortFn);
27254             for(var i = 0; i < len; i++){
27255                 var n = cs[i];
27256                 n.previousSibling = cs[i-1];
27257                 n.nextSibling = cs[i+1];
27258                 if(i == 0){
27259                     this.setFirstChild(n);
27260                 }
27261                 if(i == len-1){
27262                     this.setLastChild(n);
27263                 }
27264             }
27265         }
27266     },
27267
27268     /**
27269      * Returns true if this node is an ancestor (at any point) of the passed node.
27270      * @param {Node} node
27271      * @return {Boolean}
27272      */
27273     contains : function(node){
27274         return node.isAncestor(this);
27275     },
27276
27277     /**
27278      * Returns true if the passed node is an ancestor (at any point) of this node.
27279      * @param {Node} node
27280      * @return {Boolean}
27281      */
27282     isAncestor : function(node){
27283         var p = this.parentNode;
27284         while(p){
27285             if(p == node){
27286                 return true;
27287             }
27288             p = p.parentNode;
27289         }
27290         return false;
27291     },
27292
27293     toString : function(){
27294         return "[Node"+(this.id?" "+this.id:"")+"]";
27295     }
27296 });/*
27297  * Based on:
27298  * Ext JS Library 1.1.1
27299  * Copyright(c) 2006-2007, Ext JS, LLC.
27300  *
27301  * Originally Released Under LGPL - original licence link has changed is not relivant.
27302  *
27303  * Fork - LGPL
27304  * <script type="text/javascript">
27305  */
27306
27307
27308 /**
27309  * @class Roo.Shadow
27310  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27311  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27312  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27313  * @constructor
27314  * Create a new Shadow
27315  * @param {Object} config The config object
27316  */
27317 Roo.Shadow = function(config){
27318     Roo.apply(this, config);
27319     if(typeof this.mode != "string"){
27320         this.mode = this.defaultMode;
27321     }
27322     var o = this.offset, a = {h: 0};
27323     var rad = Math.floor(this.offset/2);
27324     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27325         case "drop":
27326             a.w = 0;
27327             a.l = a.t = o;
27328             a.t -= 1;
27329             if(Roo.isIE){
27330                 a.l -= this.offset + rad;
27331                 a.t -= this.offset + rad;
27332                 a.w -= rad;
27333                 a.h -= rad;
27334                 a.t += 1;
27335             }
27336         break;
27337         case "sides":
27338             a.w = (o*2);
27339             a.l = -o;
27340             a.t = o-1;
27341             if(Roo.isIE){
27342                 a.l -= (this.offset - rad);
27343                 a.t -= this.offset + rad;
27344                 a.l += 1;
27345                 a.w -= (this.offset - rad)*2;
27346                 a.w -= rad + 1;
27347                 a.h -= 1;
27348             }
27349         break;
27350         case "frame":
27351             a.w = a.h = (o*2);
27352             a.l = a.t = -o;
27353             a.t += 1;
27354             a.h -= 2;
27355             if(Roo.isIE){
27356                 a.l -= (this.offset - rad);
27357                 a.t -= (this.offset - rad);
27358                 a.l += 1;
27359                 a.w -= (this.offset + rad + 1);
27360                 a.h -= (this.offset + rad);
27361                 a.h += 1;
27362             }
27363         break;
27364     };
27365
27366     this.adjusts = a;
27367 };
27368
27369 Roo.Shadow.prototype = {
27370     /**
27371      * @cfg {String} mode
27372      * The shadow display mode.  Supports the following options:<br />
27373      * sides: Shadow displays on both sides and bottom only<br />
27374      * frame: Shadow displays equally on all four sides<br />
27375      * drop: Traditional bottom-right drop shadow (default)
27376      */
27377     mode: false,
27378     /**
27379      * @cfg {String} offset
27380      * The number of pixels to offset the shadow from the element (defaults to 4)
27381      */
27382     offset: 4,
27383
27384     // private
27385     defaultMode: "drop",
27386
27387     /**
27388      * Displays the shadow under the target element
27389      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27390      */
27391     show : function(target){
27392         target = Roo.get(target);
27393         if(!this.el){
27394             this.el = Roo.Shadow.Pool.pull();
27395             if(this.el.dom.nextSibling != target.dom){
27396                 this.el.insertBefore(target);
27397             }
27398         }
27399         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27400         if(Roo.isIE){
27401             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27402         }
27403         this.realign(
27404             target.getLeft(true),
27405             target.getTop(true),
27406             target.getWidth(),
27407             target.getHeight()
27408         );
27409         this.el.dom.style.display = "block";
27410     },
27411
27412     /**
27413      * Returns true if the shadow is visible, else false
27414      */
27415     isVisible : function(){
27416         return this.el ? true : false;  
27417     },
27418
27419     /**
27420      * Direct alignment when values are already available. Show must be called at least once before
27421      * calling this method to ensure it is initialized.
27422      * @param {Number} left The target element left position
27423      * @param {Number} top The target element top position
27424      * @param {Number} width The target element width
27425      * @param {Number} height The target element height
27426      */
27427     realign : function(l, t, w, h){
27428         if(!this.el){
27429             return;
27430         }
27431         var a = this.adjusts, d = this.el.dom, s = d.style;
27432         var iea = 0;
27433         s.left = (l+a.l)+"px";
27434         s.top = (t+a.t)+"px";
27435         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27436  
27437         if(s.width != sws || s.height != shs){
27438             s.width = sws;
27439             s.height = shs;
27440             if(!Roo.isIE){
27441                 var cn = d.childNodes;
27442                 var sww = Math.max(0, (sw-12))+"px";
27443                 cn[0].childNodes[1].style.width = sww;
27444                 cn[1].childNodes[1].style.width = sww;
27445                 cn[2].childNodes[1].style.width = sww;
27446                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27447             }
27448         }
27449     },
27450
27451     /**
27452      * Hides this shadow
27453      */
27454     hide : function(){
27455         if(this.el){
27456             this.el.dom.style.display = "none";
27457             Roo.Shadow.Pool.push(this.el);
27458             delete this.el;
27459         }
27460     },
27461
27462     /**
27463      * Adjust the z-index of this shadow
27464      * @param {Number} zindex The new z-index
27465      */
27466     setZIndex : function(z){
27467         this.zIndex = z;
27468         if(this.el){
27469             this.el.setStyle("z-index", z);
27470         }
27471     }
27472 };
27473
27474 // Private utility class that manages the internal Shadow cache
27475 Roo.Shadow.Pool = function(){
27476     var p = [];
27477     var markup = Roo.isIE ?
27478                  '<div class="x-ie-shadow"></div>' :
27479                  '<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>';
27480     return {
27481         pull : function(){
27482             var sh = p.shift();
27483             if(!sh){
27484                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27485                 sh.autoBoxAdjust = false;
27486             }
27487             return sh;
27488         },
27489
27490         push : function(sh){
27491             p.push(sh);
27492         }
27493     };
27494 }();/*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504
27505
27506 /**
27507  * @class Roo.SplitBar
27508  * @extends Roo.util.Observable
27509  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27510  * <br><br>
27511  * Usage:
27512  * <pre><code>
27513 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27514                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27515 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27516 split.minSize = 100;
27517 split.maxSize = 600;
27518 split.animate = true;
27519 split.on('moved', splitterMoved);
27520 </code></pre>
27521  * @constructor
27522  * Create a new SplitBar
27523  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27524  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27525  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27526  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27527                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27528                         position of the SplitBar).
27529  */
27530 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27531     
27532     /** @private */
27533     this.el = Roo.get(dragElement, true);
27534     this.el.dom.unselectable = "on";
27535     /** @private */
27536     this.resizingEl = Roo.get(resizingElement, true);
27537
27538     /**
27539      * @private
27540      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27541      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27542      * @type Number
27543      */
27544     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27545     
27546     /**
27547      * The minimum size of the resizing element. (Defaults to 0)
27548      * @type Number
27549      */
27550     this.minSize = 0;
27551     
27552     /**
27553      * The maximum size of the resizing element. (Defaults to 2000)
27554      * @type Number
27555      */
27556     this.maxSize = 2000;
27557     
27558     /**
27559      * Whether to animate the transition to the new size
27560      * @type Boolean
27561      */
27562     this.animate = false;
27563     
27564     /**
27565      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27566      * @type Boolean
27567      */
27568     this.useShim = false;
27569     
27570     /** @private */
27571     this.shim = null;
27572     
27573     if(!existingProxy){
27574         /** @private */
27575         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27576     }else{
27577         this.proxy = Roo.get(existingProxy).dom;
27578     }
27579     /** @private */
27580     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27581     
27582     /** @private */
27583     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27584     
27585     /** @private */
27586     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27587     
27588     /** @private */
27589     this.dragSpecs = {};
27590     
27591     /**
27592      * @private The adapter to use to positon and resize elements
27593      */
27594     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27595     this.adapter.init(this);
27596     
27597     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27598         /** @private */
27599         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27600         this.el.addClass("x-splitbar-h");
27601     }else{
27602         /** @private */
27603         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27604         this.el.addClass("x-splitbar-v");
27605     }
27606     
27607     this.addEvents({
27608         /**
27609          * @event resize
27610          * Fires when the splitter is moved (alias for {@link #event-moved})
27611          * @param {Roo.SplitBar} this
27612          * @param {Number} newSize the new width or height
27613          */
27614         "resize" : true,
27615         /**
27616          * @event moved
27617          * Fires when the splitter is moved
27618          * @param {Roo.SplitBar} this
27619          * @param {Number} newSize the new width or height
27620          */
27621         "moved" : true,
27622         /**
27623          * @event beforeresize
27624          * Fires before the splitter is dragged
27625          * @param {Roo.SplitBar} this
27626          */
27627         "beforeresize" : true,
27628
27629         "beforeapply" : true
27630     });
27631
27632     Roo.util.Observable.call(this);
27633 };
27634
27635 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27636     onStartProxyDrag : function(x, y){
27637         this.fireEvent("beforeresize", this);
27638         if(!this.overlay){
27639             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27640             o.unselectable();
27641             o.enableDisplayMode("block");
27642             // all splitbars share the same overlay
27643             Roo.SplitBar.prototype.overlay = o;
27644         }
27645         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27646         this.overlay.show();
27647         Roo.get(this.proxy).setDisplayed("block");
27648         var size = this.adapter.getElementSize(this);
27649         this.activeMinSize = this.getMinimumSize();;
27650         this.activeMaxSize = this.getMaximumSize();;
27651         var c1 = size - this.activeMinSize;
27652         var c2 = Math.max(this.activeMaxSize - size, 0);
27653         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27654             this.dd.resetConstraints();
27655             this.dd.setXConstraint(
27656                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27657                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27658             );
27659             this.dd.setYConstraint(0, 0);
27660         }else{
27661             this.dd.resetConstraints();
27662             this.dd.setXConstraint(0, 0);
27663             this.dd.setYConstraint(
27664                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27665                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27666             );
27667          }
27668         this.dragSpecs.startSize = size;
27669         this.dragSpecs.startPoint = [x, y];
27670         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27671     },
27672     
27673     /** 
27674      * @private Called after the drag operation by the DDProxy
27675      */
27676     onEndProxyDrag : function(e){
27677         Roo.get(this.proxy).setDisplayed(false);
27678         var endPoint = Roo.lib.Event.getXY(e);
27679         if(this.overlay){
27680             this.overlay.hide();
27681         }
27682         var newSize;
27683         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27684             newSize = this.dragSpecs.startSize + 
27685                 (this.placement == Roo.SplitBar.LEFT ?
27686                     endPoint[0] - this.dragSpecs.startPoint[0] :
27687                     this.dragSpecs.startPoint[0] - endPoint[0]
27688                 );
27689         }else{
27690             newSize = this.dragSpecs.startSize + 
27691                 (this.placement == Roo.SplitBar.TOP ?
27692                     endPoint[1] - this.dragSpecs.startPoint[1] :
27693                     this.dragSpecs.startPoint[1] - endPoint[1]
27694                 );
27695         }
27696         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27697         if(newSize != this.dragSpecs.startSize){
27698             if(this.fireEvent('beforeapply', this, newSize) !== false){
27699                 this.adapter.setElementSize(this, newSize);
27700                 this.fireEvent("moved", this, newSize);
27701                 this.fireEvent("resize", this, newSize);
27702             }
27703         }
27704     },
27705     
27706     /**
27707      * Get the adapter this SplitBar uses
27708      * @return The adapter object
27709      */
27710     getAdapter : function(){
27711         return this.adapter;
27712     },
27713     
27714     /**
27715      * Set the adapter this SplitBar uses
27716      * @param {Object} adapter A SplitBar adapter object
27717      */
27718     setAdapter : function(adapter){
27719         this.adapter = adapter;
27720         this.adapter.init(this);
27721     },
27722     
27723     /**
27724      * Gets the minimum size for the resizing element
27725      * @return {Number} The minimum size
27726      */
27727     getMinimumSize : function(){
27728         return this.minSize;
27729     },
27730     
27731     /**
27732      * Sets the minimum size for the resizing element
27733      * @param {Number} minSize The minimum size
27734      */
27735     setMinimumSize : function(minSize){
27736         this.minSize = minSize;
27737     },
27738     
27739     /**
27740      * Gets the maximum size for the resizing element
27741      * @return {Number} The maximum size
27742      */
27743     getMaximumSize : function(){
27744         return this.maxSize;
27745     },
27746     
27747     /**
27748      * Sets the maximum size for the resizing element
27749      * @param {Number} maxSize The maximum size
27750      */
27751     setMaximumSize : function(maxSize){
27752         this.maxSize = maxSize;
27753     },
27754     
27755     /**
27756      * Sets the initialize size for the resizing element
27757      * @param {Number} size The initial size
27758      */
27759     setCurrentSize : function(size){
27760         var oldAnimate = this.animate;
27761         this.animate = false;
27762         this.adapter.setElementSize(this, size);
27763         this.animate = oldAnimate;
27764     },
27765     
27766     /**
27767      * Destroy this splitbar. 
27768      * @param {Boolean} removeEl True to remove the element
27769      */
27770     destroy : function(removeEl){
27771         if(this.shim){
27772             this.shim.remove();
27773         }
27774         this.dd.unreg();
27775         this.proxy.parentNode.removeChild(this.proxy);
27776         if(removeEl){
27777             this.el.remove();
27778         }
27779     }
27780 });
27781
27782 /**
27783  * @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.
27784  */
27785 Roo.SplitBar.createProxy = function(dir){
27786     var proxy = new Roo.Element(document.createElement("div"));
27787     proxy.unselectable();
27788     var cls = 'x-splitbar-proxy';
27789     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27790     document.body.appendChild(proxy.dom);
27791     return proxy.dom;
27792 };
27793
27794 /** 
27795  * @class Roo.SplitBar.BasicLayoutAdapter
27796  * Default Adapter. It assumes the splitter and resizing element are not positioned
27797  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27798  */
27799 Roo.SplitBar.BasicLayoutAdapter = function(){
27800 };
27801
27802 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27803     // do nothing for now
27804     init : function(s){
27805     
27806     },
27807     /**
27808      * Called before drag operations to get the current size of the resizing element. 
27809      * @param {Roo.SplitBar} s The SplitBar using this adapter
27810      */
27811      getElementSize : function(s){
27812         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27813             return s.resizingEl.getWidth();
27814         }else{
27815             return s.resizingEl.getHeight();
27816         }
27817     },
27818     
27819     /**
27820      * Called after drag operations to set the size of the resizing element.
27821      * @param {Roo.SplitBar} s The SplitBar using this adapter
27822      * @param {Number} newSize The new size to set
27823      * @param {Function} onComplete A function to be invoked when resizing is complete
27824      */
27825     setElementSize : function(s, newSize, onComplete){
27826         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27827             if(!s.animate){
27828                 s.resizingEl.setWidth(newSize);
27829                 if(onComplete){
27830                     onComplete(s, newSize);
27831                 }
27832             }else{
27833                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27834             }
27835         }else{
27836             
27837             if(!s.animate){
27838                 s.resizingEl.setHeight(newSize);
27839                 if(onComplete){
27840                     onComplete(s, newSize);
27841                 }
27842             }else{
27843                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27844             }
27845         }
27846     }
27847 };
27848
27849 /** 
27850  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27851  * @extends Roo.SplitBar.BasicLayoutAdapter
27852  * Adapter that  moves the splitter element to align with the resized sizing element. 
27853  * Used with an absolute positioned SplitBar.
27854  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27855  * document.body, make sure you assign an id to the body element.
27856  */
27857 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27858     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27859     this.container = Roo.get(container);
27860 };
27861
27862 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27863     init : function(s){
27864         this.basic.init(s);
27865     },
27866     
27867     getElementSize : function(s){
27868         return this.basic.getElementSize(s);
27869     },
27870     
27871     setElementSize : function(s, newSize, onComplete){
27872         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27873     },
27874     
27875     moveSplitter : function(s){
27876         var yes = Roo.SplitBar;
27877         switch(s.placement){
27878             case yes.LEFT:
27879                 s.el.setX(s.resizingEl.getRight());
27880                 break;
27881             case yes.RIGHT:
27882                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27883                 break;
27884             case yes.TOP:
27885                 s.el.setY(s.resizingEl.getBottom());
27886                 break;
27887             case yes.BOTTOM:
27888                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27889                 break;
27890         }
27891     }
27892 };
27893
27894 /**
27895  * Orientation constant - Create a vertical SplitBar
27896  * @static
27897  * @type Number
27898  */
27899 Roo.SplitBar.VERTICAL = 1;
27900
27901 /**
27902  * Orientation constant - Create a horizontal SplitBar
27903  * @static
27904  * @type Number
27905  */
27906 Roo.SplitBar.HORIZONTAL = 2;
27907
27908 /**
27909  * Placement constant - The resizing element is to the left of the splitter element
27910  * @static
27911  * @type Number
27912  */
27913 Roo.SplitBar.LEFT = 1;
27914
27915 /**
27916  * Placement constant - The resizing element is to the right of the splitter element
27917  * @static
27918  * @type Number
27919  */
27920 Roo.SplitBar.RIGHT = 2;
27921
27922 /**
27923  * Placement constant - The resizing element is positioned above the splitter element
27924  * @static
27925  * @type Number
27926  */
27927 Roo.SplitBar.TOP = 3;
27928
27929 /**
27930  * Placement constant - The resizing element is positioned under splitter element
27931  * @static
27932  * @type Number
27933  */
27934 Roo.SplitBar.BOTTOM = 4;
27935 /*
27936  * Based on:
27937  * Ext JS Library 1.1.1
27938  * Copyright(c) 2006-2007, Ext JS, LLC.
27939  *
27940  * Originally Released Under LGPL - original licence link has changed is not relivant.
27941  *
27942  * Fork - LGPL
27943  * <script type="text/javascript">
27944  */
27945
27946 /**
27947  * @class Roo.View
27948  * @extends Roo.util.Observable
27949  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27950  * This class also supports single and multi selection modes. <br>
27951  * Create a data model bound view:
27952  <pre><code>
27953  var store = new Roo.data.Store(...);
27954
27955  var view = new Roo.View({
27956     el : "my-element",
27957     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27958  
27959     singleSelect: true,
27960     selectedClass: "ydataview-selected",
27961     store: store
27962  });
27963
27964  // listen for node click?
27965  view.on("click", function(vw, index, node, e){
27966  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27967  });
27968
27969  // load XML data
27970  dataModel.load("foobar.xml");
27971  </code></pre>
27972  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27973  * <br><br>
27974  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27975  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27976  * 
27977  * Note: old style constructor is still suported (container, template, config)
27978  * 
27979  * @constructor
27980  * Create a new View
27981  * @param {Object} config The config object
27982  * 
27983  */
27984 Roo.View = function(config, depreciated_tpl, depreciated_config){
27985     
27986     this.parent = false;
27987     
27988     if (typeof(depreciated_tpl) == 'undefined') {
27989         // new way.. - universal constructor.
27990         Roo.apply(this, config);
27991         this.el  = Roo.get(this.el);
27992     } else {
27993         // old format..
27994         this.el  = Roo.get(config);
27995         this.tpl = depreciated_tpl;
27996         Roo.apply(this, depreciated_config);
27997     }
27998     this.wrapEl  = this.el.wrap().wrap();
27999     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28000     
28001     
28002     if(typeof(this.tpl) == "string"){
28003         this.tpl = new Roo.Template(this.tpl);
28004     } else {
28005         // support xtype ctors..
28006         this.tpl = new Roo.factory(this.tpl, Roo);
28007     }
28008     
28009     
28010     this.tpl.compile();
28011     
28012     /** @private */
28013     this.addEvents({
28014         /**
28015          * @event beforeclick
28016          * Fires before a click is processed. Returns false to cancel the default action.
28017          * @param {Roo.View} this
28018          * @param {Number} index The index of the target node
28019          * @param {HTMLElement} node The target node
28020          * @param {Roo.EventObject} e The raw event object
28021          */
28022             "beforeclick" : true,
28023         /**
28024          * @event click
28025          * Fires when a template node is clicked.
28026          * @param {Roo.View} this
28027          * @param {Number} index The index of the target node
28028          * @param {HTMLElement} node The target node
28029          * @param {Roo.EventObject} e The raw event object
28030          */
28031             "click" : true,
28032         /**
28033          * @event dblclick
28034          * Fires when a template node is double clicked.
28035          * @param {Roo.View} this
28036          * @param {Number} index The index of the target node
28037          * @param {HTMLElement} node The target node
28038          * @param {Roo.EventObject} e The raw event object
28039          */
28040             "dblclick" : true,
28041         /**
28042          * @event contextmenu
28043          * Fires when a template node is right clicked.
28044          * @param {Roo.View} this
28045          * @param {Number} index The index of the target node
28046          * @param {HTMLElement} node The target node
28047          * @param {Roo.EventObject} e The raw event object
28048          */
28049             "contextmenu" : true,
28050         /**
28051          * @event selectionchange
28052          * Fires when the selected nodes change.
28053          * @param {Roo.View} this
28054          * @param {Array} selections Array of the selected nodes
28055          */
28056             "selectionchange" : true,
28057     
28058         /**
28059          * @event beforeselect
28060          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28061          * @param {Roo.View} this
28062          * @param {HTMLElement} node The node to be selected
28063          * @param {Array} selections Array of currently selected nodes
28064          */
28065             "beforeselect" : true,
28066         /**
28067          * @event preparedata
28068          * Fires on every row to render, to allow you to change the data.
28069          * @param {Roo.View} this
28070          * @param {Object} data to be rendered (change this)
28071          */
28072           "preparedata" : true
28073           
28074           
28075         });
28076
28077
28078
28079     this.el.on({
28080         "click": this.onClick,
28081         "dblclick": this.onDblClick,
28082         "contextmenu": this.onContextMenu,
28083         scope:this
28084     });
28085
28086     this.selections = [];
28087     this.nodes = [];
28088     this.cmp = new Roo.CompositeElementLite([]);
28089     if(this.store){
28090         this.store = Roo.factory(this.store, Roo.data);
28091         this.setStore(this.store, true);
28092     }
28093     
28094     if ( this.footer && this.footer.xtype) {
28095            
28096          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28097         
28098         this.footer.dataSource = this.store;
28099         this.footer.container = fctr;
28100         this.footer = Roo.factory(this.footer, Roo);
28101         fctr.insertFirst(this.el);
28102         
28103         // this is a bit insane - as the paging toolbar seems to detach the el..
28104 //        dom.parentNode.parentNode.parentNode
28105          // they get detached?
28106     }
28107     
28108     
28109     Roo.View.superclass.constructor.call(this);
28110     
28111     
28112 };
28113
28114 Roo.extend(Roo.View, Roo.util.Observable, {
28115     
28116      /**
28117      * @cfg {Roo.data.Store} store Data store to load data from.
28118      */
28119     store : false,
28120     
28121     /**
28122      * @cfg {String|Roo.Element} el The container element.
28123      */
28124     el : '',
28125     
28126     /**
28127      * @cfg {String|Roo.Template} tpl The template used by this View 
28128      */
28129     tpl : false,
28130     /**
28131      * @cfg {String} dataName the named area of the template to use as the data area
28132      *                          Works with domtemplates roo-name="name"
28133      */
28134     dataName: false,
28135     /**
28136      * @cfg {String} selectedClass The css class to add to selected nodes
28137      */
28138     selectedClass : "x-view-selected",
28139      /**
28140      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28141      */
28142     emptyText : "",
28143     
28144     /**
28145      * @cfg {String} text to display on mask (default Loading)
28146      */
28147     mask : false,
28148     /**
28149      * @cfg {Boolean} multiSelect Allow multiple selection
28150      */
28151     multiSelect : false,
28152     /**
28153      * @cfg {Boolean} singleSelect Allow single selection
28154      */
28155     singleSelect:  false,
28156     
28157     /**
28158      * @cfg {Boolean} toggleSelect - selecting 
28159      */
28160     toggleSelect : false,
28161     
28162     /**
28163      * @cfg {Boolean} tickable - selecting 
28164      */
28165     tickable : false,
28166     
28167     /**
28168      * Returns the element this view is bound to.
28169      * @return {Roo.Element}
28170      */
28171     getEl : function(){
28172         return this.wrapEl;
28173     },
28174     
28175     
28176
28177     /**
28178      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28179      */
28180     refresh : function(){
28181         //Roo.log('refresh');
28182         var t = this.tpl;
28183         
28184         // if we are using something like 'domtemplate', then
28185         // the what gets used is:
28186         // t.applySubtemplate(NAME, data, wrapping data..)
28187         // the outer template then get' applied with
28188         //     the store 'extra data'
28189         // and the body get's added to the
28190         //      roo-name="data" node?
28191         //      <span class='roo-tpl-{name}'></span> ?????
28192         
28193         
28194         
28195         this.clearSelections();
28196         this.el.update("");
28197         var html = [];
28198         var records = this.store.getRange();
28199         if(records.length < 1) {
28200             
28201             // is this valid??  = should it render a template??
28202             
28203             this.el.update(this.emptyText);
28204             return;
28205         }
28206         var el = this.el;
28207         if (this.dataName) {
28208             this.el.update(t.apply(this.store.meta)); //????
28209             el = this.el.child('.roo-tpl-' + this.dataName);
28210         }
28211         
28212         for(var i = 0, len = records.length; i < len; i++){
28213             var data = this.prepareData(records[i].data, i, records[i]);
28214             this.fireEvent("preparedata", this, data, i, records[i]);
28215             
28216             var d = Roo.apply({}, data);
28217             
28218             if(this.tickable){
28219                 Roo.apply(d, {'roo-id' : Roo.id()});
28220                 
28221                 var _this = this;
28222             
28223                 Roo.each(this.parent.item, function(item){
28224                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28225                         return;
28226                     }
28227                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28228                 });
28229             }
28230             
28231             html[html.length] = Roo.util.Format.trim(
28232                 this.dataName ?
28233                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28234                     t.apply(d)
28235             );
28236         }
28237         
28238         
28239         
28240         el.update(html.join(""));
28241         this.nodes = el.dom.childNodes;
28242         this.updateIndexes(0);
28243     },
28244     
28245
28246     /**
28247      * Function to override to reformat the data that is sent to
28248      * the template for each node.
28249      * DEPRICATED - use the preparedata event handler.
28250      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28251      * a JSON object for an UpdateManager bound view).
28252      */
28253     prepareData : function(data, index, record)
28254     {
28255         this.fireEvent("preparedata", this, data, index, record);
28256         return data;
28257     },
28258
28259     onUpdate : function(ds, record){
28260         // Roo.log('on update');   
28261         this.clearSelections();
28262         var index = this.store.indexOf(record);
28263         var n = this.nodes[index];
28264         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28265         n.parentNode.removeChild(n);
28266         this.updateIndexes(index, index);
28267     },
28268
28269     
28270     
28271 // --------- FIXME     
28272     onAdd : function(ds, records, index)
28273     {
28274         //Roo.log(['on Add', ds, records, index] );        
28275         this.clearSelections();
28276         if(this.nodes.length == 0){
28277             this.refresh();
28278             return;
28279         }
28280         var n = this.nodes[index];
28281         for(var i = 0, len = records.length; i < len; i++){
28282             var d = this.prepareData(records[i].data, i, records[i]);
28283             if(n){
28284                 this.tpl.insertBefore(n, d);
28285             }else{
28286                 
28287                 this.tpl.append(this.el, d);
28288             }
28289         }
28290         this.updateIndexes(index);
28291     },
28292
28293     onRemove : function(ds, record, index){
28294        // Roo.log('onRemove');
28295         this.clearSelections();
28296         var el = this.dataName  ?
28297             this.el.child('.roo-tpl-' + this.dataName) :
28298             this.el; 
28299         
28300         el.dom.removeChild(this.nodes[index]);
28301         this.updateIndexes(index);
28302     },
28303
28304     /**
28305      * Refresh an individual node.
28306      * @param {Number} index
28307      */
28308     refreshNode : function(index){
28309         this.onUpdate(this.store, this.store.getAt(index));
28310     },
28311
28312     updateIndexes : function(startIndex, endIndex){
28313         var ns = this.nodes;
28314         startIndex = startIndex || 0;
28315         endIndex = endIndex || ns.length - 1;
28316         for(var i = startIndex; i <= endIndex; i++){
28317             ns[i].nodeIndex = i;
28318         }
28319     },
28320
28321     /**
28322      * Changes the data store this view uses and refresh the view.
28323      * @param {Store} store
28324      */
28325     setStore : function(store, initial){
28326         if(!initial && this.store){
28327             this.store.un("datachanged", this.refresh);
28328             this.store.un("add", this.onAdd);
28329             this.store.un("remove", this.onRemove);
28330             this.store.un("update", this.onUpdate);
28331             this.store.un("clear", this.refresh);
28332             this.store.un("beforeload", this.onBeforeLoad);
28333             this.store.un("load", this.onLoad);
28334             this.store.un("loadexception", this.onLoad);
28335         }
28336         if(store){
28337           
28338             store.on("datachanged", this.refresh, this);
28339             store.on("add", this.onAdd, this);
28340             store.on("remove", this.onRemove, this);
28341             store.on("update", this.onUpdate, this);
28342             store.on("clear", this.refresh, this);
28343             store.on("beforeload", this.onBeforeLoad, this);
28344             store.on("load", this.onLoad, this);
28345             store.on("loadexception", this.onLoad, this);
28346         }
28347         
28348         if(store){
28349             this.refresh();
28350         }
28351     },
28352     /**
28353      * onbeforeLoad - masks the loading area.
28354      *
28355      */
28356     onBeforeLoad : function(store,opts)
28357     {
28358          //Roo.log('onBeforeLoad');   
28359         if (!opts.add) {
28360             this.el.update("");
28361         }
28362         this.el.mask(this.mask ? this.mask : "Loading" ); 
28363     },
28364     onLoad : function ()
28365     {
28366         this.el.unmask();
28367     },
28368     
28369
28370     /**
28371      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28372      * @param {HTMLElement} node
28373      * @return {HTMLElement} The template node
28374      */
28375     findItemFromChild : function(node){
28376         var el = this.dataName  ?
28377             this.el.child('.roo-tpl-' + this.dataName,true) :
28378             this.el.dom; 
28379         
28380         if(!node || node.parentNode == el){
28381                     return node;
28382             }
28383             var p = node.parentNode;
28384             while(p && p != el){
28385             if(p.parentNode == el){
28386                 return p;
28387             }
28388             p = p.parentNode;
28389         }
28390             return null;
28391     },
28392
28393     /** @ignore */
28394     onClick : function(e){
28395         var item = this.findItemFromChild(e.getTarget());
28396         if(item){
28397             var index = this.indexOf(item);
28398             if(this.onItemClick(item, index, e) !== false){
28399                 this.fireEvent("click", this, index, item, e);
28400             }
28401         }else{
28402             this.clearSelections();
28403         }
28404     },
28405
28406     /** @ignore */
28407     onContextMenu : function(e){
28408         var item = this.findItemFromChild(e.getTarget());
28409         if(item){
28410             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28411         }
28412     },
28413
28414     /** @ignore */
28415     onDblClick : function(e){
28416         var item = this.findItemFromChild(e.getTarget());
28417         if(item){
28418             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28419         }
28420     },
28421
28422     onItemClick : function(item, index, e)
28423     {
28424         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28425             return false;
28426         }
28427         if (this.toggleSelect) {
28428             var m = this.isSelected(item) ? 'unselect' : 'select';
28429             //Roo.log(m);
28430             var _t = this;
28431             _t[m](item, true, false);
28432             return true;
28433         }
28434         if(this.multiSelect || this.singleSelect){
28435             if(this.multiSelect && e.shiftKey && this.lastSelection){
28436                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28437             }else{
28438                 this.select(item, this.multiSelect && e.ctrlKey);
28439                 this.lastSelection = item;
28440             }
28441             
28442             if(!this.tickable){
28443                 e.preventDefault();
28444             }
28445             
28446         }
28447         return true;
28448     },
28449
28450     /**
28451      * Get the number of selected nodes.
28452      * @return {Number}
28453      */
28454     getSelectionCount : function(){
28455         return this.selections.length;
28456     },
28457
28458     /**
28459      * Get the currently selected nodes.
28460      * @return {Array} An array of HTMLElements
28461      */
28462     getSelectedNodes : function(){
28463         return this.selections;
28464     },
28465
28466     /**
28467      * Get the indexes of the selected nodes.
28468      * @return {Array}
28469      */
28470     getSelectedIndexes : function(){
28471         var indexes = [], s = this.selections;
28472         for(var i = 0, len = s.length; i < len; i++){
28473             indexes.push(s[i].nodeIndex);
28474         }
28475         return indexes;
28476     },
28477
28478     /**
28479      * Clear all selections
28480      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28481      */
28482     clearSelections : function(suppressEvent){
28483         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28484             this.cmp.elements = this.selections;
28485             this.cmp.removeClass(this.selectedClass);
28486             this.selections = [];
28487             if(!suppressEvent){
28488                 this.fireEvent("selectionchange", this, this.selections);
28489             }
28490         }
28491     },
28492
28493     /**
28494      * Returns true if the passed node is selected
28495      * @param {HTMLElement/Number} node The node or node index
28496      * @return {Boolean}
28497      */
28498     isSelected : function(node){
28499         var s = this.selections;
28500         if(s.length < 1){
28501             return false;
28502         }
28503         node = this.getNode(node);
28504         return s.indexOf(node) !== -1;
28505     },
28506
28507     /**
28508      * Selects nodes.
28509      * @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
28510      * @param {Boolean} keepExisting (optional) true to keep existing selections
28511      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28512      */
28513     select : function(nodeInfo, keepExisting, suppressEvent){
28514         if(nodeInfo instanceof Array){
28515             if(!keepExisting){
28516                 this.clearSelections(true);
28517             }
28518             for(var i = 0, len = nodeInfo.length; i < len; i++){
28519                 this.select(nodeInfo[i], true, true);
28520             }
28521             return;
28522         } 
28523         var node = this.getNode(nodeInfo);
28524         if(!node || this.isSelected(node)){
28525             return; // already selected.
28526         }
28527         if(!keepExisting){
28528             this.clearSelections(true);
28529         }
28530         
28531         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28532             Roo.fly(node).addClass(this.selectedClass);
28533             this.selections.push(node);
28534             if(!suppressEvent){
28535                 this.fireEvent("selectionchange", this, this.selections);
28536             }
28537         }
28538         
28539         
28540     },
28541       /**
28542      * Unselects nodes.
28543      * @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
28544      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28545      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28546      */
28547     unselect : function(nodeInfo, keepExisting, suppressEvent)
28548     {
28549         if(nodeInfo instanceof Array){
28550             Roo.each(this.selections, function(s) {
28551                 this.unselect(s, nodeInfo);
28552             }, this);
28553             return;
28554         }
28555         var node = this.getNode(nodeInfo);
28556         if(!node || !this.isSelected(node)){
28557             //Roo.log("not selected");
28558             return; // not selected.
28559         }
28560         // fireevent???
28561         var ns = [];
28562         Roo.each(this.selections, function(s) {
28563             if (s == node ) {
28564                 Roo.fly(node).removeClass(this.selectedClass);
28565
28566                 return;
28567             }
28568             ns.push(s);
28569         },this);
28570         
28571         this.selections= ns;
28572         this.fireEvent("selectionchange", this, this.selections);
28573     },
28574
28575     /**
28576      * Gets a template node.
28577      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28578      * @return {HTMLElement} The node or null if it wasn't found
28579      */
28580     getNode : function(nodeInfo){
28581         if(typeof nodeInfo == "string"){
28582             return document.getElementById(nodeInfo);
28583         }else if(typeof nodeInfo == "number"){
28584             return this.nodes[nodeInfo];
28585         }
28586         return nodeInfo;
28587     },
28588
28589     /**
28590      * Gets a range template nodes.
28591      * @param {Number} startIndex
28592      * @param {Number} endIndex
28593      * @return {Array} An array of nodes
28594      */
28595     getNodes : function(start, end){
28596         var ns = this.nodes;
28597         start = start || 0;
28598         end = typeof end == "undefined" ? ns.length - 1 : end;
28599         var nodes = [];
28600         if(start <= end){
28601             for(var i = start; i <= end; i++){
28602                 nodes.push(ns[i]);
28603             }
28604         } else{
28605             for(var i = start; i >= end; i--){
28606                 nodes.push(ns[i]);
28607             }
28608         }
28609         return nodes;
28610     },
28611
28612     /**
28613      * Finds the index of the passed node
28614      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28615      * @return {Number} The index of the node or -1
28616      */
28617     indexOf : function(node){
28618         node = this.getNode(node);
28619         if(typeof node.nodeIndex == "number"){
28620             return node.nodeIndex;
28621         }
28622         var ns = this.nodes;
28623         for(var i = 0, len = ns.length; i < len; i++){
28624             if(ns[i] == node){
28625                 return i;
28626             }
28627         }
28628         return -1;
28629     }
28630 });
28631 /*
28632  * Based on:
28633  * Ext JS Library 1.1.1
28634  * Copyright(c) 2006-2007, Ext JS, LLC.
28635  *
28636  * Originally Released Under LGPL - original licence link has changed is not relivant.
28637  *
28638  * Fork - LGPL
28639  * <script type="text/javascript">
28640  */
28641
28642 /**
28643  * @class Roo.JsonView
28644  * @extends Roo.View
28645  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28646 <pre><code>
28647 var view = new Roo.JsonView({
28648     container: "my-element",
28649     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28650     multiSelect: true, 
28651     jsonRoot: "data" 
28652 });
28653
28654 // listen for node click?
28655 view.on("click", function(vw, index, node, e){
28656     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28657 });
28658
28659 // direct load of JSON data
28660 view.load("foobar.php");
28661
28662 // Example from my blog list
28663 var tpl = new Roo.Template(
28664     '&lt;div class="entry"&gt;' +
28665     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28666     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28667     "&lt;/div&gt;&lt;hr /&gt;"
28668 );
28669
28670 var moreView = new Roo.JsonView({
28671     container :  "entry-list", 
28672     template : tpl,
28673     jsonRoot: "posts"
28674 });
28675 moreView.on("beforerender", this.sortEntries, this);
28676 moreView.load({
28677     url: "/blog/get-posts.php",
28678     params: "allposts=true",
28679     text: "Loading Blog Entries..."
28680 });
28681 </code></pre>
28682
28683 * Note: old code is supported with arguments : (container, template, config)
28684
28685
28686  * @constructor
28687  * Create a new JsonView
28688  * 
28689  * @param {Object} config The config object
28690  * 
28691  */
28692 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28693     
28694     
28695     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28696
28697     var um = this.el.getUpdateManager();
28698     um.setRenderer(this);
28699     um.on("update", this.onLoad, this);
28700     um.on("failure", this.onLoadException, this);
28701
28702     /**
28703      * @event beforerender
28704      * Fires before rendering of the downloaded JSON data.
28705      * @param {Roo.JsonView} this
28706      * @param {Object} data The JSON data loaded
28707      */
28708     /**
28709      * @event load
28710      * Fires when data is loaded.
28711      * @param {Roo.JsonView} this
28712      * @param {Object} data The JSON data loaded
28713      * @param {Object} response The raw Connect response object
28714      */
28715     /**
28716      * @event loadexception
28717      * Fires when loading fails.
28718      * @param {Roo.JsonView} this
28719      * @param {Object} response The raw Connect response object
28720      */
28721     this.addEvents({
28722         'beforerender' : true,
28723         'load' : true,
28724         'loadexception' : true
28725     });
28726 };
28727 Roo.extend(Roo.JsonView, Roo.View, {
28728     /**
28729      * @type {String} The root property in the loaded JSON object that contains the data
28730      */
28731     jsonRoot : "",
28732
28733     /**
28734      * Refreshes the view.
28735      */
28736     refresh : function(){
28737         this.clearSelections();
28738         this.el.update("");
28739         var html = [];
28740         var o = this.jsonData;
28741         if(o && o.length > 0){
28742             for(var i = 0, len = o.length; i < len; i++){
28743                 var data = this.prepareData(o[i], i, o);
28744                 html[html.length] = this.tpl.apply(data);
28745             }
28746         }else{
28747             html.push(this.emptyText);
28748         }
28749         this.el.update(html.join(""));
28750         this.nodes = this.el.dom.childNodes;
28751         this.updateIndexes(0);
28752     },
28753
28754     /**
28755      * 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.
28756      * @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:
28757      <pre><code>
28758      view.load({
28759          url: "your-url.php",
28760          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28761          callback: yourFunction,
28762          scope: yourObject, //(optional scope)
28763          discardUrl: false,
28764          nocache: false,
28765          text: "Loading...",
28766          timeout: 30,
28767          scripts: false
28768      });
28769      </code></pre>
28770      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28771      * 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.
28772      * @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}
28773      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28774      * @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.
28775      */
28776     load : function(){
28777         var um = this.el.getUpdateManager();
28778         um.update.apply(um, arguments);
28779     },
28780
28781     // note - render is a standard framework call...
28782     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28783     render : function(el, response){
28784         
28785         this.clearSelections();
28786         this.el.update("");
28787         var o;
28788         try{
28789             if (response != '') {
28790                 o = Roo.util.JSON.decode(response.responseText);
28791                 if(this.jsonRoot){
28792                     
28793                     o = o[this.jsonRoot];
28794                 }
28795             }
28796         } catch(e){
28797         }
28798         /**
28799          * The current JSON data or null
28800          */
28801         this.jsonData = o;
28802         this.beforeRender();
28803         this.refresh();
28804     },
28805
28806 /**
28807  * Get the number of records in the current JSON dataset
28808  * @return {Number}
28809  */
28810     getCount : function(){
28811         return this.jsonData ? this.jsonData.length : 0;
28812     },
28813
28814 /**
28815  * Returns the JSON object for the specified node(s)
28816  * @param {HTMLElement/Array} node The node or an array of nodes
28817  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28818  * you get the JSON object for the node
28819  */
28820     getNodeData : function(node){
28821         if(node instanceof Array){
28822             var data = [];
28823             for(var i = 0, len = node.length; i < len; i++){
28824                 data.push(this.getNodeData(node[i]));
28825             }
28826             return data;
28827         }
28828         return this.jsonData[this.indexOf(node)] || null;
28829     },
28830
28831     beforeRender : function(){
28832         this.snapshot = this.jsonData;
28833         if(this.sortInfo){
28834             this.sort.apply(this, this.sortInfo);
28835         }
28836         this.fireEvent("beforerender", this, this.jsonData);
28837     },
28838
28839     onLoad : function(el, o){
28840         this.fireEvent("load", this, this.jsonData, o);
28841     },
28842
28843     onLoadException : function(el, o){
28844         this.fireEvent("loadexception", this, o);
28845     },
28846
28847 /**
28848  * Filter the data by a specific property.
28849  * @param {String} property A property on your JSON objects
28850  * @param {String/RegExp} value Either string that the property values
28851  * should start with, or a RegExp to test against the property
28852  */
28853     filter : function(property, value){
28854         if(this.jsonData){
28855             var data = [];
28856             var ss = this.snapshot;
28857             if(typeof value == "string"){
28858                 var vlen = value.length;
28859                 if(vlen == 0){
28860                     this.clearFilter();
28861                     return;
28862                 }
28863                 value = value.toLowerCase();
28864                 for(var i = 0, len = ss.length; i < len; i++){
28865                     var o = ss[i];
28866                     if(o[property].substr(0, vlen).toLowerCase() == value){
28867                         data.push(o);
28868                     }
28869                 }
28870             } else if(value.exec){ // regex?
28871                 for(var i = 0, len = ss.length; i < len; i++){
28872                     var o = ss[i];
28873                     if(value.test(o[property])){
28874                         data.push(o);
28875                     }
28876                 }
28877             } else{
28878                 return;
28879             }
28880             this.jsonData = data;
28881             this.refresh();
28882         }
28883     },
28884
28885 /**
28886  * Filter by a function. The passed function will be called with each
28887  * object in the current dataset. If the function returns true the value is kept,
28888  * otherwise it is filtered.
28889  * @param {Function} fn
28890  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28891  */
28892     filterBy : function(fn, scope){
28893         if(this.jsonData){
28894             var data = [];
28895             var ss = this.snapshot;
28896             for(var i = 0, len = ss.length; i < len; i++){
28897                 var o = ss[i];
28898                 if(fn.call(scope || this, o)){
28899                     data.push(o);
28900                 }
28901             }
28902             this.jsonData = data;
28903             this.refresh();
28904         }
28905     },
28906
28907 /**
28908  * Clears the current filter.
28909  */
28910     clearFilter : function(){
28911         if(this.snapshot && this.jsonData != this.snapshot){
28912             this.jsonData = this.snapshot;
28913             this.refresh();
28914         }
28915     },
28916
28917
28918 /**
28919  * Sorts the data for this view and refreshes it.
28920  * @param {String} property A property on your JSON objects to sort on
28921  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28922  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28923  */
28924     sort : function(property, dir, sortType){
28925         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28926         if(this.jsonData){
28927             var p = property;
28928             var dsc = dir && dir.toLowerCase() == "desc";
28929             var f = function(o1, o2){
28930                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28931                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28932                 ;
28933                 if(v1 < v2){
28934                     return dsc ? +1 : -1;
28935                 } else if(v1 > v2){
28936                     return dsc ? -1 : +1;
28937                 } else{
28938                     return 0;
28939                 }
28940             };
28941             this.jsonData.sort(f);
28942             this.refresh();
28943             if(this.jsonData != this.snapshot){
28944                 this.snapshot.sort(f);
28945             }
28946         }
28947     }
28948 });/*
28949  * Based on:
28950  * Ext JS Library 1.1.1
28951  * Copyright(c) 2006-2007, Ext JS, LLC.
28952  *
28953  * Originally Released Under LGPL - original licence link has changed is not relivant.
28954  *
28955  * Fork - LGPL
28956  * <script type="text/javascript">
28957  */
28958  
28959
28960 /**
28961  * @class Roo.ColorPalette
28962  * @extends Roo.Component
28963  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28964  * Here's an example of typical usage:
28965  * <pre><code>
28966 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28967 cp.render('my-div');
28968
28969 cp.on('select', function(palette, selColor){
28970     // do something with selColor
28971 });
28972 </code></pre>
28973  * @constructor
28974  * Create a new ColorPalette
28975  * @param {Object} config The config object
28976  */
28977 Roo.ColorPalette = function(config){
28978     Roo.ColorPalette.superclass.constructor.call(this, config);
28979     this.addEvents({
28980         /**
28981              * @event select
28982              * Fires when a color is selected
28983              * @param {ColorPalette} this
28984              * @param {String} color The 6-digit color hex code (without the # symbol)
28985              */
28986         select: true
28987     });
28988
28989     if(this.handler){
28990         this.on("select", this.handler, this.scope, true);
28991     }
28992 };
28993 Roo.extend(Roo.ColorPalette, Roo.Component, {
28994     /**
28995      * @cfg {String} itemCls
28996      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28997      */
28998     itemCls : "x-color-palette",
28999     /**
29000      * @cfg {String} value
29001      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29002      * the hex codes are case-sensitive.
29003      */
29004     value : null,
29005     clickEvent:'click',
29006     // private
29007     ctype: "Roo.ColorPalette",
29008
29009     /**
29010      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29011      */
29012     allowReselect : false,
29013
29014     /**
29015      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29016      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29017      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29018      * of colors with the width setting until the box is symmetrical.</p>
29019      * <p>You can override individual colors if needed:</p>
29020      * <pre><code>
29021 var cp = new Roo.ColorPalette();
29022 cp.colors[0] = "FF0000";  // change the first box to red
29023 </code></pre>
29024
29025 Or you can provide a custom array of your own for complete control:
29026 <pre><code>
29027 var cp = new Roo.ColorPalette();
29028 cp.colors = ["000000", "993300", "333300"];
29029 </code></pre>
29030      * @type Array
29031      */
29032     colors : [
29033         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29034         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29035         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29036         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29037         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29038     ],
29039
29040     // private
29041     onRender : function(container, position){
29042         var t = new Roo.MasterTemplate(
29043             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29044         );
29045         var c = this.colors;
29046         for(var i = 0, len = c.length; i < len; i++){
29047             t.add([c[i]]);
29048         }
29049         var el = document.createElement("div");
29050         el.className = this.itemCls;
29051         t.overwrite(el);
29052         container.dom.insertBefore(el, position);
29053         this.el = Roo.get(el);
29054         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29055         if(this.clickEvent != 'click'){
29056             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29057         }
29058     },
29059
29060     // private
29061     afterRender : function(){
29062         Roo.ColorPalette.superclass.afterRender.call(this);
29063         if(this.value){
29064             var s = this.value;
29065             this.value = null;
29066             this.select(s);
29067         }
29068     },
29069
29070     // private
29071     handleClick : function(e, t){
29072         e.preventDefault();
29073         if(!this.disabled){
29074             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29075             this.select(c.toUpperCase());
29076         }
29077     },
29078
29079     /**
29080      * Selects the specified color in the palette (fires the select event)
29081      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29082      */
29083     select : function(color){
29084         color = color.replace("#", "");
29085         if(color != this.value || this.allowReselect){
29086             var el = this.el;
29087             if(this.value){
29088                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29089             }
29090             el.child("a.color-"+color).addClass("x-color-palette-sel");
29091             this.value = color;
29092             this.fireEvent("select", this, color);
29093         }
29094     }
29095 });/*
29096  * Based on:
29097  * Ext JS Library 1.1.1
29098  * Copyright(c) 2006-2007, Ext JS, LLC.
29099  *
29100  * Originally Released Under LGPL - original licence link has changed is not relivant.
29101  *
29102  * Fork - LGPL
29103  * <script type="text/javascript">
29104  */
29105  
29106 /**
29107  * @class Roo.DatePicker
29108  * @extends Roo.Component
29109  * Simple date picker class.
29110  * @constructor
29111  * Create a new DatePicker
29112  * @param {Object} config The config object
29113  */
29114 Roo.DatePicker = function(config){
29115     Roo.DatePicker.superclass.constructor.call(this, config);
29116
29117     this.value = config && config.value ?
29118                  config.value.clearTime() : new Date().clearTime();
29119
29120     this.addEvents({
29121         /**
29122              * @event select
29123              * Fires when a date is selected
29124              * @param {DatePicker} this
29125              * @param {Date} date The selected date
29126              */
29127         'select': true,
29128         /**
29129              * @event monthchange
29130              * Fires when the displayed month changes 
29131              * @param {DatePicker} this
29132              * @param {Date} date The selected month
29133              */
29134         'monthchange': true
29135     });
29136
29137     if(this.handler){
29138         this.on("select", this.handler,  this.scope || this);
29139     }
29140     // build the disabledDatesRE
29141     if(!this.disabledDatesRE && this.disabledDates){
29142         var dd = this.disabledDates;
29143         var re = "(?:";
29144         for(var i = 0; i < dd.length; i++){
29145             re += dd[i];
29146             if(i != dd.length-1) {
29147                 re += "|";
29148             }
29149         }
29150         this.disabledDatesRE = new RegExp(re + ")");
29151     }
29152 };
29153
29154 Roo.extend(Roo.DatePicker, Roo.Component, {
29155     /**
29156      * @cfg {String} todayText
29157      * The text to display on the button that selects the current date (defaults to "Today")
29158      */
29159     todayText : "Today",
29160     /**
29161      * @cfg {String} okText
29162      * The text to display on the ok button
29163      */
29164     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29165     /**
29166      * @cfg {String} cancelText
29167      * The text to display on the cancel button
29168      */
29169     cancelText : "Cancel",
29170     /**
29171      * @cfg {String} todayTip
29172      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29173      */
29174     todayTip : "{0} (Spacebar)",
29175     /**
29176      * @cfg {Date} minDate
29177      * Minimum allowable date (JavaScript date object, defaults to null)
29178      */
29179     minDate : null,
29180     /**
29181      * @cfg {Date} maxDate
29182      * Maximum allowable date (JavaScript date object, defaults to null)
29183      */
29184     maxDate : null,
29185     /**
29186      * @cfg {String} minText
29187      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29188      */
29189     minText : "This date is before the minimum date",
29190     /**
29191      * @cfg {String} maxText
29192      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29193      */
29194     maxText : "This date is after the maximum date",
29195     /**
29196      * @cfg {String} format
29197      * The default date format string which can be overriden for localization support.  The format must be
29198      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29199      */
29200     format : "m/d/y",
29201     /**
29202      * @cfg {Array} disabledDays
29203      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29204      */
29205     disabledDays : null,
29206     /**
29207      * @cfg {String} disabledDaysText
29208      * The tooltip to display when the date falls on a disabled day (defaults to "")
29209      */
29210     disabledDaysText : "",
29211     /**
29212      * @cfg {RegExp} disabledDatesRE
29213      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29214      */
29215     disabledDatesRE : null,
29216     /**
29217      * @cfg {String} disabledDatesText
29218      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29219      */
29220     disabledDatesText : "",
29221     /**
29222      * @cfg {Boolean} constrainToViewport
29223      * True to constrain the date picker to the viewport (defaults to true)
29224      */
29225     constrainToViewport : true,
29226     /**
29227      * @cfg {Array} monthNames
29228      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29229      */
29230     monthNames : Date.monthNames,
29231     /**
29232      * @cfg {Array} dayNames
29233      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29234      */
29235     dayNames : Date.dayNames,
29236     /**
29237      * @cfg {String} nextText
29238      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29239      */
29240     nextText: 'Next Month (Control+Right)',
29241     /**
29242      * @cfg {String} prevText
29243      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29244      */
29245     prevText: 'Previous Month (Control+Left)',
29246     /**
29247      * @cfg {String} monthYearText
29248      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29249      */
29250     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29251     /**
29252      * @cfg {Number} startDay
29253      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29254      */
29255     startDay : 0,
29256     /**
29257      * @cfg {Bool} showClear
29258      * Show a clear button (usefull for date form elements that can be blank.)
29259      */
29260     
29261     showClear: false,
29262     
29263     /**
29264      * Sets the value of the date field
29265      * @param {Date} value The date to set
29266      */
29267     setValue : function(value){
29268         var old = this.value;
29269         
29270         if (typeof(value) == 'string') {
29271          
29272             value = Date.parseDate(value, this.format);
29273         }
29274         if (!value) {
29275             value = new Date();
29276         }
29277         
29278         this.value = value.clearTime(true);
29279         if(this.el){
29280             this.update(this.value);
29281         }
29282     },
29283
29284     /**
29285      * Gets the current selected value of the date field
29286      * @return {Date} The selected date
29287      */
29288     getValue : function(){
29289         return this.value;
29290     },
29291
29292     // private
29293     focus : function(){
29294         if(this.el){
29295             this.update(this.activeDate);
29296         }
29297     },
29298
29299     // privateval
29300     onRender : function(container, position){
29301         
29302         var m = [
29303              '<table cellspacing="0">',
29304                 '<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>',
29305                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29306         var dn = this.dayNames;
29307         for(var i = 0; i < 7; i++){
29308             var d = this.startDay+i;
29309             if(d > 6){
29310                 d = d-7;
29311             }
29312             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29313         }
29314         m[m.length] = "</tr></thead><tbody><tr>";
29315         for(var i = 0; i < 42; i++) {
29316             if(i % 7 == 0 && i != 0){
29317                 m[m.length] = "</tr><tr>";
29318             }
29319             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29320         }
29321         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29322             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29323
29324         var el = document.createElement("div");
29325         el.className = "x-date-picker";
29326         el.innerHTML = m.join("");
29327
29328         container.dom.insertBefore(el, position);
29329
29330         this.el = Roo.get(el);
29331         this.eventEl = Roo.get(el.firstChild);
29332
29333         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29334             handler: this.showPrevMonth,
29335             scope: this,
29336             preventDefault:true,
29337             stopDefault:true
29338         });
29339
29340         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29341             handler: this.showNextMonth,
29342             scope: this,
29343             preventDefault:true,
29344             stopDefault:true
29345         });
29346
29347         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29348
29349         this.monthPicker = this.el.down('div.x-date-mp');
29350         this.monthPicker.enableDisplayMode('block');
29351         
29352         var kn = new Roo.KeyNav(this.eventEl, {
29353             "left" : function(e){
29354                 e.ctrlKey ?
29355                     this.showPrevMonth() :
29356                     this.update(this.activeDate.add("d", -1));
29357             },
29358
29359             "right" : function(e){
29360                 e.ctrlKey ?
29361                     this.showNextMonth() :
29362                     this.update(this.activeDate.add("d", 1));
29363             },
29364
29365             "up" : function(e){
29366                 e.ctrlKey ?
29367                     this.showNextYear() :
29368                     this.update(this.activeDate.add("d", -7));
29369             },
29370
29371             "down" : function(e){
29372                 e.ctrlKey ?
29373                     this.showPrevYear() :
29374                     this.update(this.activeDate.add("d", 7));
29375             },
29376
29377             "pageUp" : function(e){
29378                 this.showNextMonth();
29379             },
29380
29381             "pageDown" : function(e){
29382                 this.showPrevMonth();
29383             },
29384
29385             "enter" : function(e){
29386                 e.stopPropagation();
29387                 return true;
29388             },
29389
29390             scope : this
29391         });
29392
29393         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29394
29395         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29396
29397         this.el.unselectable();
29398         
29399         this.cells = this.el.select("table.x-date-inner tbody td");
29400         this.textNodes = this.el.query("table.x-date-inner tbody span");
29401
29402         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29403             text: "&#160;",
29404             tooltip: this.monthYearText
29405         });
29406
29407         this.mbtn.on('click', this.showMonthPicker, this);
29408         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29409
29410
29411         var today = (new Date()).dateFormat(this.format);
29412         
29413         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29414         if (this.showClear) {
29415             baseTb.add( new Roo.Toolbar.Fill());
29416         }
29417         baseTb.add({
29418             text: String.format(this.todayText, today),
29419             tooltip: String.format(this.todayTip, today),
29420             handler: this.selectToday,
29421             scope: this
29422         });
29423         
29424         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29425             
29426         //});
29427         if (this.showClear) {
29428             
29429             baseTb.add( new Roo.Toolbar.Fill());
29430             baseTb.add({
29431                 text: '&#160;',
29432                 cls: 'x-btn-icon x-btn-clear',
29433                 handler: function() {
29434                     //this.value = '';
29435                     this.fireEvent("select", this, '');
29436                 },
29437                 scope: this
29438             });
29439         }
29440         
29441         
29442         if(Roo.isIE){
29443             this.el.repaint();
29444         }
29445         this.update(this.value);
29446     },
29447
29448     createMonthPicker : function(){
29449         if(!this.monthPicker.dom.firstChild){
29450             var buf = ['<table border="0" cellspacing="0">'];
29451             for(var i = 0; i < 6; i++){
29452                 buf.push(
29453                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29454                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29455                     i == 0 ?
29456                     '<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>' :
29457                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29458                 );
29459             }
29460             buf.push(
29461                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29462                     this.okText,
29463                     '</button><button type="button" class="x-date-mp-cancel">',
29464                     this.cancelText,
29465                     '</button></td></tr>',
29466                 '</table>'
29467             );
29468             this.monthPicker.update(buf.join(''));
29469             this.monthPicker.on('click', this.onMonthClick, this);
29470             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29471
29472             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29473             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29474
29475             this.mpMonths.each(function(m, a, i){
29476                 i += 1;
29477                 if((i%2) == 0){
29478                     m.dom.xmonth = 5 + Math.round(i * .5);
29479                 }else{
29480                     m.dom.xmonth = Math.round((i-1) * .5);
29481                 }
29482             });
29483         }
29484     },
29485
29486     showMonthPicker : function(){
29487         this.createMonthPicker();
29488         var size = this.el.getSize();
29489         this.monthPicker.setSize(size);
29490         this.monthPicker.child('table').setSize(size);
29491
29492         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29493         this.updateMPMonth(this.mpSelMonth);
29494         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29495         this.updateMPYear(this.mpSelYear);
29496
29497         this.monthPicker.slideIn('t', {duration:.2});
29498     },
29499
29500     updateMPYear : function(y){
29501         this.mpyear = y;
29502         var ys = this.mpYears.elements;
29503         for(var i = 1; i <= 10; i++){
29504             var td = ys[i-1], y2;
29505             if((i%2) == 0){
29506                 y2 = y + Math.round(i * .5);
29507                 td.firstChild.innerHTML = y2;
29508                 td.xyear = y2;
29509             }else{
29510                 y2 = y - (5-Math.round(i * .5));
29511                 td.firstChild.innerHTML = y2;
29512                 td.xyear = y2;
29513             }
29514             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29515         }
29516     },
29517
29518     updateMPMonth : function(sm){
29519         this.mpMonths.each(function(m, a, i){
29520             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29521         });
29522     },
29523
29524     selectMPMonth: function(m){
29525         
29526     },
29527
29528     onMonthClick : function(e, t){
29529         e.stopEvent();
29530         var el = new Roo.Element(t), pn;
29531         if(el.is('button.x-date-mp-cancel')){
29532             this.hideMonthPicker();
29533         }
29534         else if(el.is('button.x-date-mp-ok')){
29535             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29536             this.hideMonthPicker();
29537         }
29538         else if(pn = el.up('td.x-date-mp-month', 2)){
29539             this.mpMonths.removeClass('x-date-mp-sel');
29540             pn.addClass('x-date-mp-sel');
29541             this.mpSelMonth = pn.dom.xmonth;
29542         }
29543         else if(pn = el.up('td.x-date-mp-year', 2)){
29544             this.mpYears.removeClass('x-date-mp-sel');
29545             pn.addClass('x-date-mp-sel');
29546             this.mpSelYear = pn.dom.xyear;
29547         }
29548         else if(el.is('a.x-date-mp-prev')){
29549             this.updateMPYear(this.mpyear-10);
29550         }
29551         else if(el.is('a.x-date-mp-next')){
29552             this.updateMPYear(this.mpyear+10);
29553         }
29554     },
29555
29556     onMonthDblClick : function(e, t){
29557         e.stopEvent();
29558         var el = new Roo.Element(t), pn;
29559         if(pn = el.up('td.x-date-mp-month', 2)){
29560             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29561             this.hideMonthPicker();
29562         }
29563         else if(pn = el.up('td.x-date-mp-year', 2)){
29564             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29565             this.hideMonthPicker();
29566         }
29567     },
29568
29569     hideMonthPicker : function(disableAnim){
29570         if(this.monthPicker){
29571             if(disableAnim === true){
29572                 this.monthPicker.hide();
29573             }else{
29574                 this.monthPicker.slideOut('t', {duration:.2});
29575             }
29576         }
29577     },
29578
29579     // private
29580     showPrevMonth : function(e){
29581         this.update(this.activeDate.add("mo", -1));
29582     },
29583
29584     // private
29585     showNextMonth : function(e){
29586         this.update(this.activeDate.add("mo", 1));
29587     },
29588
29589     // private
29590     showPrevYear : function(){
29591         this.update(this.activeDate.add("y", -1));
29592     },
29593
29594     // private
29595     showNextYear : function(){
29596         this.update(this.activeDate.add("y", 1));
29597     },
29598
29599     // private
29600     handleMouseWheel : function(e){
29601         var delta = e.getWheelDelta();
29602         if(delta > 0){
29603             this.showPrevMonth();
29604             e.stopEvent();
29605         } else if(delta < 0){
29606             this.showNextMonth();
29607             e.stopEvent();
29608         }
29609     },
29610
29611     // private
29612     handleDateClick : function(e, t){
29613         e.stopEvent();
29614         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29615             this.setValue(new Date(t.dateValue));
29616             this.fireEvent("select", this, this.value);
29617         }
29618     },
29619
29620     // private
29621     selectToday : function(){
29622         this.setValue(new Date().clearTime());
29623         this.fireEvent("select", this, this.value);
29624     },
29625
29626     // private
29627     update : function(date)
29628     {
29629         var vd = this.activeDate;
29630         this.activeDate = date;
29631         if(vd && this.el){
29632             var t = date.getTime();
29633             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29634                 this.cells.removeClass("x-date-selected");
29635                 this.cells.each(function(c){
29636                    if(c.dom.firstChild.dateValue == t){
29637                        c.addClass("x-date-selected");
29638                        setTimeout(function(){
29639                             try{c.dom.firstChild.focus();}catch(e){}
29640                        }, 50);
29641                        return false;
29642                    }
29643                 });
29644                 return;
29645             }
29646         }
29647         
29648         var days = date.getDaysInMonth();
29649         var firstOfMonth = date.getFirstDateOfMonth();
29650         var startingPos = firstOfMonth.getDay()-this.startDay;
29651
29652         if(startingPos <= this.startDay){
29653             startingPos += 7;
29654         }
29655
29656         var pm = date.add("mo", -1);
29657         var prevStart = pm.getDaysInMonth()-startingPos;
29658
29659         var cells = this.cells.elements;
29660         var textEls = this.textNodes;
29661         days += startingPos;
29662
29663         // convert everything to numbers so it's fast
29664         var day = 86400000;
29665         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29666         var today = new Date().clearTime().getTime();
29667         var sel = date.clearTime().getTime();
29668         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29669         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29670         var ddMatch = this.disabledDatesRE;
29671         var ddText = this.disabledDatesText;
29672         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29673         var ddaysText = this.disabledDaysText;
29674         var format = this.format;
29675
29676         var setCellClass = function(cal, cell){
29677             cell.title = "";
29678             var t = d.getTime();
29679             cell.firstChild.dateValue = t;
29680             if(t == today){
29681                 cell.className += " x-date-today";
29682                 cell.title = cal.todayText;
29683             }
29684             if(t == sel){
29685                 cell.className += " x-date-selected";
29686                 setTimeout(function(){
29687                     try{cell.firstChild.focus();}catch(e){}
29688                 }, 50);
29689             }
29690             // disabling
29691             if(t < min) {
29692                 cell.className = " x-date-disabled";
29693                 cell.title = cal.minText;
29694                 return;
29695             }
29696             if(t > max) {
29697                 cell.className = " x-date-disabled";
29698                 cell.title = cal.maxText;
29699                 return;
29700             }
29701             if(ddays){
29702                 if(ddays.indexOf(d.getDay()) != -1){
29703                     cell.title = ddaysText;
29704                     cell.className = " x-date-disabled";
29705                 }
29706             }
29707             if(ddMatch && format){
29708                 var fvalue = d.dateFormat(format);
29709                 if(ddMatch.test(fvalue)){
29710                     cell.title = ddText.replace("%0", fvalue);
29711                     cell.className = " x-date-disabled";
29712                 }
29713             }
29714         };
29715
29716         var i = 0;
29717         for(; i < startingPos; i++) {
29718             textEls[i].innerHTML = (++prevStart);
29719             d.setDate(d.getDate()+1);
29720             cells[i].className = "x-date-prevday";
29721             setCellClass(this, cells[i]);
29722         }
29723         for(; i < days; i++){
29724             intDay = i - startingPos + 1;
29725             textEls[i].innerHTML = (intDay);
29726             d.setDate(d.getDate()+1);
29727             cells[i].className = "x-date-active";
29728             setCellClass(this, cells[i]);
29729         }
29730         var extraDays = 0;
29731         for(; i < 42; i++) {
29732              textEls[i].innerHTML = (++extraDays);
29733              d.setDate(d.getDate()+1);
29734              cells[i].className = "x-date-nextday";
29735              setCellClass(this, cells[i]);
29736         }
29737
29738         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29739         this.fireEvent('monthchange', this, date);
29740         
29741         if(!this.internalRender){
29742             var main = this.el.dom.firstChild;
29743             var w = main.offsetWidth;
29744             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29745             Roo.fly(main).setWidth(w);
29746             this.internalRender = true;
29747             // opera does not respect the auto grow header center column
29748             // then, after it gets a width opera refuses to recalculate
29749             // without a second pass
29750             if(Roo.isOpera && !this.secondPass){
29751                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29752                 this.secondPass = true;
29753                 this.update.defer(10, this, [date]);
29754             }
29755         }
29756         
29757         
29758     }
29759 });        /*
29760  * Based on:
29761  * Ext JS Library 1.1.1
29762  * Copyright(c) 2006-2007, Ext JS, LLC.
29763  *
29764  * Originally Released Under LGPL - original licence link has changed is not relivant.
29765  *
29766  * Fork - LGPL
29767  * <script type="text/javascript">
29768  */
29769 /**
29770  * @class Roo.TabPanel
29771  * @extends Roo.util.Observable
29772  * A lightweight tab container.
29773  * <br><br>
29774  * Usage:
29775  * <pre><code>
29776 // basic tabs 1, built from existing content
29777 var tabs = new Roo.TabPanel("tabs1");
29778 tabs.addTab("script", "View Script");
29779 tabs.addTab("markup", "View Markup");
29780 tabs.activate("script");
29781
29782 // more advanced tabs, built from javascript
29783 var jtabs = new Roo.TabPanel("jtabs");
29784 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29785
29786 // set up the UpdateManager
29787 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29788 var updater = tab2.getUpdateManager();
29789 updater.setDefaultUrl("ajax1.htm");
29790 tab2.on('activate', updater.refresh, updater, true);
29791
29792 // Use setUrl for Ajax loading
29793 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29794 tab3.setUrl("ajax2.htm", null, true);
29795
29796 // Disabled tab
29797 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29798 tab4.disable();
29799
29800 jtabs.activate("jtabs-1");
29801  * </code></pre>
29802  * @constructor
29803  * Create a new TabPanel.
29804  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29805  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29806  */
29807 Roo.TabPanel = function(container, config){
29808     /**
29809     * The container element for this TabPanel.
29810     * @type Roo.Element
29811     */
29812     this.el = Roo.get(container, true);
29813     if(config){
29814         if(typeof config == "boolean"){
29815             this.tabPosition = config ? "bottom" : "top";
29816         }else{
29817             Roo.apply(this, config);
29818         }
29819     }
29820     if(this.tabPosition == "bottom"){
29821         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29822         this.el.addClass("x-tabs-bottom");
29823     }
29824     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29825     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29826     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29827     if(Roo.isIE){
29828         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29829     }
29830     if(this.tabPosition != "bottom"){
29831         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29832          * @type Roo.Element
29833          */
29834         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29835         this.el.addClass("x-tabs-top");
29836     }
29837     this.items = [];
29838
29839     this.bodyEl.setStyle("position", "relative");
29840
29841     this.active = null;
29842     this.activateDelegate = this.activate.createDelegate(this);
29843
29844     this.addEvents({
29845         /**
29846          * @event tabchange
29847          * Fires when the active tab changes
29848          * @param {Roo.TabPanel} this
29849          * @param {Roo.TabPanelItem} activePanel The new active tab
29850          */
29851         "tabchange": true,
29852         /**
29853          * @event beforetabchange
29854          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29855          * @param {Roo.TabPanel} this
29856          * @param {Object} e Set cancel to true on this object to cancel the tab change
29857          * @param {Roo.TabPanelItem} tab The tab being changed to
29858          */
29859         "beforetabchange" : true
29860     });
29861
29862     Roo.EventManager.onWindowResize(this.onResize, this);
29863     this.cpad = this.el.getPadding("lr");
29864     this.hiddenCount = 0;
29865
29866
29867     // toolbar on the tabbar support...
29868     if (this.toolbar) {
29869         var tcfg = this.toolbar;
29870         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29871         this.toolbar = new Roo.Toolbar(tcfg);
29872         if (Roo.isSafari) {
29873             var tbl = tcfg.container.child('table', true);
29874             tbl.setAttribute('width', '100%');
29875         }
29876         
29877     }
29878    
29879
29880
29881     Roo.TabPanel.superclass.constructor.call(this);
29882 };
29883
29884 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29885     /*
29886      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29887      */
29888     tabPosition : "top",
29889     /*
29890      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29891      */
29892     currentTabWidth : 0,
29893     /*
29894      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29895      */
29896     minTabWidth : 40,
29897     /*
29898      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29899      */
29900     maxTabWidth : 250,
29901     /*
29902      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29903      */
29904     preferredTabWidth : 175,
29905     /*
29906      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29907      */
29908     resizeTabs : false,
29909     /*
29910      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29911      */
29912     monitorResize : true,
29913     /*
29914      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29915      */
29916     toolbar : false,
29917
29918     /**
29919      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29920      * @param {String} id The id of the div to use <b>or create</b>
29921      * @param {String} text The text for the tab
29922      * @param {String} content (optional) Content to put in the TabPanelItem body
29923      * @param {Boolean} closable (optional) True to create a close icon on the tab
29924      * @return {Roo.TabPanelItem} The created TabPanelItem
29925      */
29926     addTab : function(id, text, content, closable){
29927         var item = new Roo.TabPanelItem(this, id, text, closable);
29928         this.addTabItem(item);
29929         if(content){
29930             item.setContent(content);
29931         }
29932         return item;
29933     },
29934
29935     /**
29936      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29937      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29938      * @return {Roo.TabPanelItem}
29939      */
29940     getTab : function(id){
29941         return this.items[id];
29942     },
29943
29944     /**
29945      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29946      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29947      */
29948     hideTab : function(id){
29949         var t = this.items[id];
29950         if(!t.isHidden()){
29951            t.setHidden(true);
29952            this.hiddenCount++;
29953            this.autoSizeTabs();
29954         }
29955     },
29956
29957     /**
29958      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29959      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29960      */
29961     unhideTab : function(id){
29962         var t = this.items[id];
29963         if(t.isHidden()){
29964            t.setHidden(false);
29965            this.hiddenCount--;
29966            this.autoSizeTabs();
29967         }
29968     },
29969
29970     /**
29971      * Adds an existing {@link Roo.TabPanelItem}.
29972      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29973      */
29974     addTabItem : function(item){
29975         this.items[item.id] = item;
29976         this.items.push(item);
29977         if(this.resizeTabs){
29978            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29979            this.autoSizeTabs();
29980         }else{
29981             item.autoSize();
29982         }
29983     },
29984
29985     /**
29986      * Removes a {@link Roo.TabPanelItem}.
29987      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29988      */
29989     removeTab : function(id){
29990         var items = this.items;
29991         var tab = items[id];
29992         if(!tab) { return; }
29993         var index = items.indexOf(tab);
29994         if(this.active == tab && items.length > 1){
29995             var newTab = this.getNextAvailable(index);
29996             if(newTab) {
29997                 newTab.activate();
29998             }
29999         }
30000         this.stripEl.dom.removeChild(tab.pnode.dom);
30001         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30002             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30003         }
30004         items.splice(index, 1);
30005         delete this.items[tab.id];
30006         tab.fireEvent("close", tab);
30007         tab.purgeListeners();
30008         this.autoSizeTabs();
30009     },
30010
30011     getNextAvailable : function(start){
30012         var items = this.items;
30013         var index = start;
30014         // look for a next tab that will slide over to
30015         // replace the one being removed
30016         while(index < items.length){
30017             var item = items[++index];
30018             if(item && !item.isHidden()){
30019                 return item;
30020             }
30021         }
30022         // if one isn't found select the previous tab (on the left)
30023         index = start;
30024         while(index >= 0){
30025             var item = items[--index];
30026             if(item && !item.isHidden()){
30027                 return item;
30028             }
30029         }
30030         return null;
30031     },
30032
30033     /**
30034      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30035      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30036      */
30037     disableTab : function(id){
30038         var tab = this.items[id];
30039         if(tab && this.active != tab){
30040             tab.disable();
30041         }
30042     },
30043
30044     /**
30045      * Enables a {@link Roo.TabPanelItem} that is disabled.
30046      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30047      */
30048     enableTab : function(id){
30049         var tab = this.items[id];
30050         tab.enable();
30051     },
30052
30053     /**
30054      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30055      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30056      * @return {Roo.TabPanelItem} The TabPanelItem.
30057      */
30058     activate : function(id){
30059         var tab = this.items[id];
30060         if(!tab){
30061             return null;
30062         }
30063         if(tab == this.active || tab.disabled){
30064             return tab;
30065         }
30066         var e = {};
30067         this.fireEvent("beforetabchange", this, e, tab);
30068         if(e.cancel !== true && !tab.disabled){
30069             if(this.active){
30070                 this.active.hide();
30071             }
30072             this.active = this.items[id];
30073             this.active.show();
30074             this.fireEvent("tabchange", this, this.active);
30075         }
30076         return tab;
30077     },
30078
30079     /**
30080      * Gets the active {@link Roo.TabPanelItem}.
30081      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30082      */
30083     getActiveTab : function(){
30084         return this.active;
30085     },
30086
30087     /**
30088      * Updates the tab body element to fit the height of the container element
30089      * for overflow scrolling
30090      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30091      */
30092     syncHeight : function(targetHeight){
30093         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30094         var bm = this.bodyEl.getMargins();
30095         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30096         this.bodyEl.setHeight(newHeight);
30097         return newHeight;
30098     },
30099
30100     onResize : function(){
30101         if(this.monitorResize){
30102             this.autoSizeTabs();
30103         }
30104     },
30105
30106     /**
30107      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30108      */
30109     beginUpdate : function(){
30110         this.updating = true;
30111     },
30112
30113     /**
30114      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30115      */
30116     endUpdate : function(){
30117         this.updating = false;
30118         this.autoSizeTabs();
30119     },
30120
30121     /**
30122      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30123      */
30124     autoSizeTabs : function(){
30125         var count = this.items.length;
30126         var vcount = count - this.hiddenCount;
30127         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30128             return;
30129         }
30130         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30131         var availWidth = Math.floor(w / vcount);
30132         var b = this.stripBody;
30133         if(b.getWidth() > w){
30134             var tabs = this.items;
30135             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30136             if(availWidth < this.minTabWidth){
30137                 /*if(!this.sleft){    // incomplete scrolling code
30138                     this.createScrollButtons();
30139                 }
30140                 this.showScroll();
30141                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30142             }
30143         }else{
30144             if(this.currentTabWidth < this.preferredTabWidth){
30145                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30146             }
30147         }
30148     },
30149
30150     /**
30151      * Returns the number of tabs in this TabPanel.
30152      * @return {Number}
30153      */
30154      getCount : function(){
30155          return this.items.length;
30156      },
30157
30158     /**
30159      * Resizes all the tabs to the passed width
30160      * @param {Number} The new width
30161      */
30162     setTabWidth : function(width){
30163         this.currentTabWidth = width;
30164         for(var i = 0, len = this.items.length; i < len; i++) {
30165                 if(!this.items[i].isHidden()) {
30166                 this.items[i].setWidth(width);
30167             }
30168         }
30169     },
30170
30171     /**
30172      * Destroys this TabPanel
30173      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30174      */
30175     destroy : function(removeEl){
30176         Roo.EventManager.removeResizeListener(this.onResize, this);
30177         for(var i = 0, len = this.items.length; i < len; i++){
30178             this.items[i].purgeListeners();
30179         }
30180         if(removeEl === true){
30181             this.el.update("");
30182             this.el.remove();
30183         }
30184     }
30185 });
30186
30187 /**
30188  * @class Roo.TabPanelItem
30189  * @extends Roo.util.Observable
30190  * Represents an individual item (tab plus body) in a TabPanel.
30191  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30192  * @param {String} id The id of this TabPanelItem
30193  * @param {String} text The text for the tab of this TabPanelItem
30194  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30195  */
30196 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30197     /**
30198      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30199      * @type Roo.TabPanel
30200      */
30201     this.tabPanel = tabPanel;
30202     /**
30203      * The id for this TabPanelItem
30204      * @type String
30205      */
30206     this.id = id;
30207     /** @private */
30208     this.disabled = false;
30209     /** @private */
30210     this.text = text;
30211     /** @private */
30212     this.loaded = false;
30213     this.closable = closable;
30214
30215     /**
30216      * The body element for this TabPanelItem.
30217      * @type Roo.Element
30218      */
30219     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30220     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30221     this.bodyEl.setStyle("display", "block");
30222     this.bodyEl.setStyle("zoom", "1");
30223     this.hideAction();
30224
30225     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30226     /** @private */
30227     this.el = Roo.get(els.el, true);
30228     this.inner = Roo.get(els.inner, true);
30229     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30230     this.pnode = Roo.get(els.el.parentNode, true);
30231     this.el.on("mousedown", this.onTabMouseDown, this);
30232     this.el.on("click", this.onTabClick, this);
30233     /** @private */
30234     if(closable){
30235         var c = Roo.get(els.close, true);
30236         c.dom.title = this.closeText;
30237         c.addClassOnOver("close-over");
30238         c.on("click", this.closeClick, this);
30239      }
30240
30241     this.addEvents({
30242          /**
30243          * @event activate
30244          * Fires when this tab becomes the active tab.
30245          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30246          * @param {Roo.TabPanelItem} this
30247          */
30248         "activate": true,
30249         /**
30250          * @event beforeclose
30251          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30252          * @param {Roo.TabPanelItem} this
30253          * @param {Object} e Set cancel to true on this object to cancel the close.
30254          */
30255         "beforeclose": true,
30256         /**
30257          * @event close
30258          * Fires when this tab is closed.
30259          * @param {Roo.TabPanelItem} this
30260          */
30261          "close": true,
30262         /**
30263          * @event deactivate
30264          * Fires when this tab is no longer the active tab.
30265          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30266          * @param {Roo.TabPanelItem} this
30267          */
30268          "deactivate" : true
30269     });
30270     this.hidden = false;
30271
30272     Roo.TabPanelItem.superclass.constructor.call(this);
30273 };
30274
30275 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30276     purgeListeners : function(){
30277        Roo.util.Observable.prototype.purgeListeners.call(this);
30278        this.el.removeAllListeners();
30279     },
30280     /**
30281      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30282      */
30283     show : function(){
30284         this.pnode.addClass("on");
30285         this.showAction();
30286         if(Roo.isOpera){
30287             this.tabPanel.stripWrap.repaint();
30288         }
30289         this.fireEvent("activate", this.tabPanel, this);
30290     },
30291
30292     /**
30293      * Returns true if this tab is the active tab.
30294      * @return {Boolean}
30295      */
30296     isActive : function(){
30297         return this.tabPanel.getActiveTab() == this;
30298     },
30299
30300     /**
30301      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30302      */
30303     hide : function(){
30304         this.pnode.removeClass("on");
30305         this.hideAction();
30306         this.fireEvent("deactivate", this.tabPanel, this);
30307     },
30308
30309     hideAction : function(){
30310         this.bodyEl.hide();
30311         this.bodyEl.setStyle("position", "absolute");
30312         this.bodyEl.setLeft("-20000px");
30313         this.bodyEl.setTop("-20000px");
30314     },
30315
30316     showAction : function(){
30317         this.bodyEl.setStyle("position", "relative");
30318         this.bodyEl.setTop("");
30319         this.bodyEl.setLeft("");
30320         this.bodyEl.show();
30321     },
30322
30323     /**
30324      * Set the tooltip for the tab.
30325      * @param {String} tooltip The tab's tooltip
30326      */
30327     setTooltip : function(text){
30328         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30329             this.textEl.dom.qtip = text;
30330             this.textEl.dom.removeAttribute('title');
30331         }else{
30332             this.textEl.dom.title = text;
30333         }
30334     },
30335
30336     onTabClick : function(e){
30337         e.preventDefault();
30338         this.tabPanel.activate(this.id);
30339     },
30340
30341     onTabMouseDown : function(e){
30342         e.preventDefault();
30343         this.tabPanel.activate(this.id);
30344     },
30345
30346     getWidth : function(){
30347         return this.inner.getWidth();
30348     },
30349
30350     setWidth : function(width){
30351         var iwidth = width - this.pnode.getPadding("lr");
30352         this.inner.setWidth(iwidth);
30353         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30354         this.pnode.setWidth(width);
30355     },
30356
30357     /**
30358      * Show or hide the tab
30359      * @param {Boolean} hidden True to hide or false to show.
30360      */
30361     setHidden : function(hidden){
30362         this.hidden = hidden;
30363         this.pnode.setStyle("display", hidden ? "none" : "");
30364     },
30365
30366     /**
30367      * Returns true if this tab is "hidden"
30368      * @return {Boolean}
30369      */
30370     isHidden : function(){
30371         return this.hidden;
30372     },
30373
30374     /**
30375      * Returns the text for this tab
30376      * @return {String}
30377      */
30378     getText : function(){
30379         return this.text;
30380     },
30381
30382     autoSize : function(){
30383         //this.el.beginMeasure();
30384         this.textEl.setWidth(1);
30385         /*
30386          *  #2804 [new] Tabs in Roojs
30387          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30388          */
30389         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30390         //this.el.endMeasure();
30391     },
30392
30393     /**
30394      * Sets the text for the tab (Note: this also sets the tooltip text)
30395      * @param {String} text The tab's text and tooltip
30396      */
30397     setText : function(text){
30398         this.text = text;
30399         this.textEl.update(text);
30400         this.setTooltip(text);
30401         if(!this.tabPanel.resizeTabs){
30402             this.autoSize();
30403         }
30404     },
30405     /**
30406      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30407      */
30408     activate : function(){
30409         this.tabPanel.activate(this.id);
30410     },
30411
30412     /**
30413      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30414      */
30415     disable : function(){
30416         if(this.tabPanel.active != this){
30417             this.disabled = true;
30418             this.pnode.addClass("disabled");
30419         }
30420     },
30421
30422     /**
30423      * Enables this TabPanelItem if it was previously disabled.
30424      */
30425     enable : function(){
30426         this.disabled = false;
30427         this.pnode.removeClass("disabled");
30428     },
30429
30430     /**
30431      * Sets the content for this TabPanelItem.
30432      * @param {String} content The content
30433      * @param {Boolean} loadScripts true to look for and load scripts
30434      */
30435     setContent : function(content, loadScripts){
30436         this.bodyEl.update(content, loadScripts);
30437     },
30438
30439     /**
30440      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30441      * @return {Roo.UpdateManager} The UpdateManager
30442      */
30443     getUpdateManager : function(){
30444         return this.bodyEl.getUpdateManager();
30445     },
30446
30447     /**
30448      * Set a URL to be used to load the content for this TabPanelItem.
30449      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30450      * @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)
30451      * @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)
30452      * @return {Roo.UpdateManager} The UpdateManager
30453      */
30454     setUrl : function(url, params, loadOnce){
30455         if(this.refreshDelegate){
30456             this.un('activate', this.refreshDelegate);
30457         }
30458         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30459         this.on("activate", this.refreshDelegate);
30460         return this.bodyEl.getUpdateManager();
30461     },
30462
30463     /** @private */
30464     _handleRefresh : function(url, params, loadOnce){
30465         if(!loadOnce || !this.loaded){
30466             var updater = this.bodyEl.getUpdateManager();
30467             updater.update(url, params, this._setLoaded.createDelegate(this));
30468         }
30469     },
30470
30471     /**
30472      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30473      *   Will fail silently if the setUrl method has not been called.
30474      *   This does not activate the panel, just updates its content.
30475      */
30476     refresh : function(){
30477         if(this.refreshDelegate){
30478            this.loaded = false;
30479            this.refreshDelegate();
30480         }
30481     },
30482
30483     /** @private */
30484     _setLoaded : function(){
30485         this.loaded = true;
30486     },
30487
30488     /** @private */
30489     closeClick : function(e){
30490         var o = {};
30491         e.stopEvent();
30492         this.fireEvent("beforeclose", this, o);
30493         if(o.cancel !== true){
30494             this.tabPanel.removeTab(this.id);
30495         }
30496     },
30497     /**
30498      * The text displayed in the tooltip for the close icon.
30499      * @type String
30500      */
30501     closeText : "Close this tab"
30502 });
30503
30504 /** @private */
30505 Roo.TabPanel.prototype.createStrip = function(container){
30506     var strip = document.createElement("div");
30507     strip.className = "x-tabs-wrap";
30508     container.appendChild(strip);
30509     return strip;
30510 };
30511 /** @private */
30512 Roo.TabPanel.prototype.createStripList = function(strip){
30513     // div wrapper for retard IE
30514     // returns the "tr" element.
30515     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30516         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30517         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30518     return strip.firstChild.firstChild.firstChild.firstChild;
30519 };
30520 /** @private */
30521 Roo.TabPanel.prototype.createBody = function(container){
30522     var body = document.createElement("div");
30523     Roo.id(body, "tab-body");
30524     Roo.fly(body).addClass("x-tabs-body");
30525     container.appendChild(body);
30526     return body;
30527 };
30528 /** @private */
30529 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30530     var body = Roo.getDom(id);
30531     if(!body){
30532         body = document.createElement("div");
30533         body.id = id;
30534     }
30535     Roo.fly(body).addClass("x-tabs-item-body");
30536     bodyEl.insertBefore(body, bodyEl.firstChild);
30537     return body;
30538 };
30539 /** @private */
30540 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30541     var td = document.createElement("td");
30542     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30543     //stripEl.appendChild(td);
30544     if(closable){
30545         td.className = "x-tabs-closable";
30546         if(!this.closeTpl){
30547             this.closeTpl = new Roo.Template(
30548                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30549                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30550                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30551             );
30552         }
30553         var el = this.closeTpl.overwrite(td, {"text": text});
30554         var close = el.getElementsByTagName("div")[0];
30555         var inner = el.getElementsByTagName("em")[0];
30556         return {"el": el, "close": close, "inner": inner};
30557     } else {
30558         if(!this.tabTpl){
30559             this.tabTpl = new Roo.Template(
30560                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30561                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30562             );
30563         }
30564         var el = this.tabTpl.overwrite(td, {"text": text});
30565         var inner = el.getElementsByTagName("em")[0];
30566         return {"el": el, "inner": inner};
30567     }
30568 };/*
30569  * Based on:
30570  * Ext JS Library 1.1.1
30571  * Copyright(c) 2006-2007, Ext JS, LLC.
30572  *
30573  * Originally Released Under LGPL - original licence link has changed is not relivant.
30574  *
30575  * Fork - LGPL
30576  * <script type="text/javascript">
30577  */
30578
30579 /**
30580  * @class Roo.Button
30581  * @extends Roo.util.Observable
30582  * Simple Button class
30583  * @cfg {String} text The button text
30584  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30585  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30586  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30587  * @cfg {Object} scope The scope of the handler
30588  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30589  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30590  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30591  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30592  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30593  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30594    applies if enableToggle = true)
30595  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30596  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30597   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30598  * @constructor
30599  * Create a new button
30600  * @param {Object} config The config object
30601  */
30602 Roo.Button = function(renderTo, config)
30603 {
30604     if (!config) {
30605         config = renderTo;
30606         renderTo = config.renderTo || false;
30607     }
30608     
30609     Roo.apply(this, config);
30610     this.addEvents({
30611         /**
30612              * @event click
30613              * Fires when this button is clicked
30614              * @param {Button} this
30615              * @param {EventObject} e The click event
30616              */
30617             "click" : true,
30618         /**
30619              * @event toggle
30620              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30621              * @param {Button} this
30622              * @param {Boolean} pressed
30623              */
30624             "toggle" : true,
30625         /**
30626              * @event mouseover
30627              * Fires when the mouse hovers over the button
30628              * @param {Button} this
30629              * @param {Event} e The event object
30630              */
30631         'mouseover' : true,
30632         /**
30633              * @event mouseout
30634              * Fires when the mouse exits the button
30635              * @param {Button} this
30636              * @param {Event} e The event object
30637              */
30638         'mouseout': true,
30639          /**
30640              * @event render
30641              * Fires when the button is rendered
30642              * @param {Button} this
30643              */
30644         'render': true
30645     });
30646     if(this.menu){
30647         this.menu = Roo.menu.MenuMgr.get(this.menu);
30648     }
30649     // register listeners first!!  - so render can be captured..
30650     Roo.util.Observable.call(this);
30651     if(renderTo){
30652         this.render(renderTo);
30653     }
30654     
30655   
30656 };
30657
30658 Roo.extend(Roo.Button, Roo.util.Observable, {
30659     /**
30660      * 
30661      */
30662     
30663     /**
30664      * Read-only. True if this button is hidden
30665      * @type Boolean
30666      */
30667     hidden : false,
30668     /**
30669      * Read-only. True if this button is disabled
30670      * @type Boolean
30671      */
30672     disabled : false,
30673     /**
30674      * Read-only. True if this button is pressed (only if enableToggle = true)
30675      * @type Boolean
30676      */
30677     pressed : false,
30678
30679     /**
30680      * @cfg {Number} tabIndex 
30681      * The DOM tabIndex for this button (defaults to undefined)
30682      */
30683     tabIndex : undefined,
30684
30685     /**
30686      * @cfg {Boolean} enableToggle
30687      * True to enable pressed/not pressed toggling (defaults to false)
30688      */
30689     enableToggle: false,
30690     /**
30691      * @cfg {Roo.menu.Menu} menu
30692      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30693      */
30694     menu : undefined,
30695     /**
30696      * @cfg {String} menuAlign
30697      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30698      */
30699     menuAlign : "tl-bl?",
30700
30701     /**
30702      * @cfg {String} iconCls
30703      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30704      */
30705     iconCls : undefined,
30706     /**
30707      * @cfg {String} type
30708      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30709      */
30710     type : 'button',
30711
30712     // private
30713     menuClassTarget: 'tr',
30714
30715     /**
30716      * @cfg {String} clickEvent
30717      * The type of event to map to the button's event handler (defaults to 'click')
30718      */
30719     clickEvent : 'click',
30720
30721     /**
30722      * @cfg {Boolean} handleMouseEvents
30723      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30724      */
30725     handleMouseEvents : true,
30726
30727     /**
30728      * @cfg {String} tooltipType
30729      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30730      */
30731     tooltipType : 'qtip',
30732
30733     /**
30734      * @cfg {String} cls
30735      * A CSS class to apply to the button's main element.
30736      */
30737     
30738     /**
30739      * @cfg {Roo.Template} template (Optional)
30740      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30741      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30742      * require code modifications if required elements (e.g. a button) aren't present.
30743      */
30744
30745     // private
30746     render : function(renderTo){
30747         var btn;
30748         if(this.hideParent){
30749             this.parentEl = Roo.get(renderTo);
30750         }
30751         if(!this.dhconfig){
30752             if(!this.template){
30753                 if(!Roo.Button.buttonTemplate){
30754                     // hideous table template
30755                     Roo.Button.buttonTemplate = new Roo.Template(
30756                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30757                         '<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>',
30758                         "</tr></tbody></table>");
30759                 }
30760                 this.template = Roo.Button.buttonTemplate;
30761             }
30762             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30763             var btnEl = btn.child("button:first");
30764             btnEl.on('focus', this.onFocus, this);
30765             btnEl.on('blur', this.onBlur, this);
30766             if(this.cls){
30767                 btn.addClass(this.cls);
30768             }
30769             if(this.icon){
30770                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30771             }
30772             if(this.iconCls){
30773                 btnEl.addClass(this.iconCls);
30774                 if(!this.cls){
30775                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30776                 }
30777             }
30778             if(this.tabIndex !== undefined){
30779                 btnEl.dom.tabIndex = this.tabIndex;
30780             }
30781             if(this.tooltip){
30782                 if(typeof this.tooltip == 'object'){
30783                     Roo.QuickTips.tips(Roo.apply({
30784                           target: btnEl.id
30785                     }, this.tooltip));
30786                 } else {
30787                     btnEl.dom[this.tooltipType] = this.tooltip;
30788                 }
30789             }
30790         }else{
30791             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30792         }
30793         this.el = btn;
30794         if(this.id){
30795             this.el.dom.id = this.el.id = this.id;
30796         }
30797         if(this.menu){
30798             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30799             this.menu.on("show", this.onMenuShow, this);
30800             this.menu.on("hide", this.onMenuHide, this);
30801         }
30802         btn.addClass("x-btn");
30803         if(Roo.isIE && !Roo.isIE7){
30804             this.autoWidth.defer(1, this);
30805         }else{
30806             this.autoWidth();
30807         }
30808         if(this.handleMouseEvents){
30809             btn.on("mouseover", this.onMouseOver, this);
30810             btn.on("mouseout", this.onMouseOut, this);
30811             btn.on("mousedown", this.onMouseDown, this);
30812         }
30813         btn.on(this.clickEvent, this.onClick, this);
30814         //btn.on("mouseup", this.onMouseUp, this);
30815         if(this.hidden){
30816             this.hide();
30817         }
30818         if(this.disabled){
30819             this.disable();
30820         }
30821         Roo.ButtonToggleMgr.register(this);
30822         if(this.pressed){
30823             this.el.addClass("x-btn-pressed");
30824         }
30825         if(this.repeat){
30826             var repeater = new Roo.util.ClickRepeater(btn,
30827                 typeof this.repeat == "object" ? this.repeat : {}
30828             );
30829             repeater.on("click", this.onClick,  this);
30830         }
30831         
30832         this.fireEvent('render', this);
30833         
30834     },
30835     /**
30836      * Returns the button's underlying element
30837      * @return {Roo.Element} The element
30838      */
30839     getEl : function(){
30840         return this.el;  
30841     },
30842     
30843     /**
30844      * Destroys this Button and removes any listeners.
30845      */
30846     destroy : function(){
30847         Roo.ButtonToggleMgr.unregister(this);
30848         this.el.removeAllListeners();
30849         this.purgeListeners();
30850         this.el.remove();
30851     },
30852
30853     // private
30854     autoWidth : function(){
30855         if(this.el){
30856             this.el.setWidth("auto");
30857             if(Roo.isIE7 && Roo.isStrict){
30858                 var ib = this.el.child('button');
30859                 if(ib && ib.getWidth() > 20){
30860                     ib.clip();
30861                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30862                 }
30863             }
30864             if(this.minWidth){
30865                 if(this.hidden){
30866                     this.el.beginMeasure();
30867                 }
30868                 if(this.el.getWidth() < this.minWidth){
30869                     this.el.setWidth(this.minWidth);
30870                 }
30871                 if(this.hidden){
30872                     this.el.endMeasure();
30873                 }
30874             }
30875         }
30876     },
30877
30878     /**
30879      * Assigns this button's click handler
30880      * @param {Function} handler The function to call when the button is clicked
30881      * @param {Object} scope (optional) Scope for the function passed in
30882      */
30883     setHandler : function(handler, scope){
30884         this.handler = handler;
30885         this.scope = scope;  
30886     },
30887     
30888     /**
30889      * Sets this button's text
30890      * @param {String} text The button text
30891      */
30892     setText : function(text){
30893         this.text = text;
30894         if(this.el){
30895             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30896         }
30897         this.autoWidth();
30898     },
30899     
30900     /**
30901      * Gets the text for this button
30902      * @return {String} The button text
30903      */
30904     getText : function(){
30905         return this.text;  
30906     },
30907     
30908     /**
30909      * Show this button
30910      */
30911     show: function(){
30912         this.hidden = false;
30913         if(this.el){
30914             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30915         }
30916     },
30917     
30918     /**
30919      * Hide this button
30920      */
30921     hide: function(){
30922         this.hidden = true;
30923         if(this.el){
30924             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30925         }
30926     },
30927     
30928     /**
30929      * Convenience function for boolean show/hide
30930      * @param {Boolean} visible True to show, false to hide
30931      */
30932     setVisible: function(visible){
30933         if(visible) {
30934             this.show();
30935         }else{
30936             this.hide();
30937         }
30938     },
30939     
30940     /**
30941      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30942      * @param {Boolean} state (optional) Force a particular state
30943      */
30944     toggle : function(state){
30945         state = state === undefined ? !this.pressed : state;
30946         if(state != this.pressed){
30947             if(state){
30948                 this.el.addClass("x-btn-pressed");
30949                 this.pressed = true;
30950                 this.fireEvent("toggle", this, true);
30951             }else{
30952                 this.el.removeClass("x-btn-pressed");
30953                 this.pressed = false;
30954                 this.fireEvent("toggle", this, false);
30955             }
30956             if(this.toggleHandler){
30957                 this.toggleHandler.call(this.scope || this, this, state);
30958             }
30959         }
30960     },
30961     
30962     /**
30963      * Focus the button
30964      */
30965     focus : function(){
30966         this.el.child('button:first').focus();
30967     },
30968     
30969     /**
30970      * Disable this button
30971      */
30972     disable : function(){
30973         if(this.el){
30974             this.el.addClass("x-btn-disabled");
30975         }
30976         this.disabled = true;
30977     },
30978     
30979     /**
30980      * Enable this button
30981      */
30982     enable : function(){
30983         if(this.el){
30984             this.el.removeClass("x-btn-disabled");
30985         }
30986         this.disabled = false;
30987     },
30988
30989     /**
30990      * Convenience function for boolean enable/disable
30991      * @param {Boolean} enabled True to enable, false to disable
30992      */
30993     setDisabled : function(v){
30994         this[v !== true ? "enable" : "disable"]();
30995     },
30996
30997     // private
30998     onClick : function(e)
30999     {
31000         if(e){
31001             e.preventDefault();
31002         }
31003         if(e.button != 0){
31004             return;
31005         }
31006         if(!this.disabled){
31007             if(this.enableToggle){
31008                 this.toggle();
31009             }
31010             if(this.menu && !this.menu.isVisible()){
31011                 this.menu.show(this.el, this.menuAlign);
31012             }
31013             this.fireEvent("click", this, e);
31014             if(this.handler){
31015                 this.el.removeClass("x-btn-over");
31016                 this.handler.call(this.scope || this, this, e);
31017             }
31018         }
31019     },
31020     // private
31021     onMouseOver : function(e){
31022         if(!this.disabled){
31023             this.el.addClass("x-btn-over");
31024             this.fireEvent('mouseover', this, e);
31025         }
31026     },
31027     // private
31028     onMouseOut : function(e){
31029         if(!e.within(this.el,  true)){
31030             this.el.removeClass("x-btn-over");
31031             this.fireEvent('mouseout', this, e);
31032         }
31033     },
31034     // private
31035     onFocus : function(e){
31036         if(!this.disabled){
31037             this.el.addClass("x-btn-focus");
31038         }
31039     },
31040     // private
31041     onBlur : function(e){
31042         this.el.removeClass("x-btn-focus");
31043     },
31044     // private
31045     onMouseDown : function(e){
31046         if(!this.disabled && e.button == 0){
31047             this.el.addClass("x-btn-click");
31048             Roo.get(document).on('mouseup', this.onMouseUp, this);
31049         }
31050     },
31051     // private
31052     onMouseUp : function(e){
31053         if(e.button == 0){
31054             this.el.removeClass("x-btn-click");
31055             Roo.get(document).un('mouseup', this.onMouseUp, this);
31056         }
31057     },
31058     // private
31059     onMenuShow : function(e){
31060         this.el.addClass("x-btn-menu-active");
31061     },
31062     // private
31063     onMenuHide : function(e){
31064         this.el.removeClass("x-btn-menu-active");
31065     }   
31066 });
31067
31068 // Private utility class used by Button
31069 Roo.ButtonToggleMgr = function(){
31070    var groups = {};
31071    
31072    function toggleGroup(btn, state){
31073        if(state){
31074            var g = groups[btn.toggleGroup];
31075            for(var i = 0, l = g.length; i < l; i++){
31076                if(g[i] != btn){
31077                    g[i].toggle(false);
31078                }
31079            }
31080        }
31081    }
31082    
31083    return {
31084        register : function(btn){
31085            if(!btn.toggleGroup){
31086                return;
31087            }
31088            var g = groups[btn.toggleGroup];
31089            if(!g){
31090                g = groups[btn.toggleGroup] = [];
31091            }
31092            g.push(btn);
31093            btn.on("toggle", toggleGroup);
31094        },
31095        
31096        unregister : function(btn){
31097            if(!btn.toggleGroup){
31098                return;
31099            }
31100            var g = groups[btn.toggleGroup];
31101            if(g){
31102                g.remove(btn);
31103                btn.un("toggle", toggleGroup);
31104            }
31105        }
31106    };
31107 }();/*
31108  * Based on:
31109  * Ext JS Library 1.1.1
31110  * Copyright(c) 2006-2007, Ext JS, LLC.
31111  *
31112  * Originally Released Under LGPL - original licence link has changed is not relivant.
31113  *
31114  * Fork - LGPL
31115  * <script type="text/javascript">
31116  */
31117  
31118 /**
31119  * @class Roo.SplitButton
31120  * @extends Roo.Button
31121  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31122  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31123  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31124  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31125  * @cfg {String} arrowTooltip The title attribute of the arrow
31126  * @constructor
31127  * Create a new menu button
31128  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31129  * @param {Object} config The config object
31130  */
31131 Roo.SplitButton = function(renderTo, config){
31132     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31133     /**
31134      * @event arrowclick
31135      * Fires when this button's arrow is clicked
31136      * @param {SplitButton} this
31137      * @param {EventObject} e The click event
31138      */
31139     this.addEvents({"arrowclick":true});
31140 };
31141
31142 Roo.extend(Roo.SplitButton, Roo.Button, {
31143     render : function(renderTo){
31144         // this is one sweet looking template!
31145         var tpl = new Roo.Template(
31146             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31147             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31148             '<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>',
31149             "</tbody></table></td><td>",
31150             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31151             '<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>',
31152             "</tbody></table></td></tr></table>"
31153         );
31154         var btn = tpl.append(renderTo, [this.text, this.type], true);
31155         var btnEl = btn.child("button");
31156         if(this.cls){
31157             btn.addClass(this.cls);
31158         }
31159         if(this.icon){
31160             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31161         }
31162         if(this.iconCls){
31163             btnEl.addClass(this.iconCls);
31164             if(!this.cls){
31165                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31166             }
31167         }
31168         this.el = btn;
31169         if(this.handleMouseEvents){
31170             btn.on("mouseover", this.onMouseOver, this);
31171             btn.on("mouseout", this.onMouseOut, this);
31172             btn.on("mousedown", this.onMouseDown, this);
31173             btn.on("mouseup", this.onMouseUp, this);
31174         }
31175         btn.on(this.clickEvent, this.onClick, this);
31176         if(this.tooltip){
31177             if(typeof this.tooltip == 'object'){
31178                 Roo.QuickTips.tips(Roo.apply({
31179                       target: btnEl.id
31180                 }, this.tooltip));
31181             } else {
31182                 btnEl.dom[this.tooltipType] = this.tooltip;
31183             }
31184         }
31185         if(this.arrowTooltip){
31186             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31187         }
31188         if(this.hidden){
31189             this.hide();
31190         }
31191         if(this.disabled){
31192             this.disable();
31193         }
31194         if(this.pressed){
31195             this.el.addClass("x-btn-pressed");
31196         }
31197         if(Roo.isIE && !Roo.isIE7){
31198             this.autoWidth.defer(1, this);
31199         }else{
31200             this.autoWidth();
31201         }
31202         if(this.menu){
31203             this.menu.on("show", this.onMenuShow, this);
31204             this.menu.on("hide", this.onMenuHide, this);
31205         }
31206         this.fireEvent('render', this);
31207     },
31208
31209     // private
31210     autoWidth : function(){
31211         if(this.el){
31212             var tbl = this.el.child("table:first");
31213             var tbl2 = this.el.child("table:last");
31214             this.el.setWidth("auto");
31215             tbl.setWidth("auto");
31216             if(Roo.isIE7 && Roo.isStrict){
31217                 var ib = this.el.child('button:first');
31218                 if(ib && ib.getWidth() > 20){
31219                     ib.clip();
31220                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31221                 }
31222             }
31223             if(this.minWidth){
31224                 if(this.hidden){
31225                     this.el.beginMeasure();
31226                 }
31227                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31228                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31229                 }
31230                 if(this.hidden){
31231                     this.el.endMeasure();
31232                 }
31233             }
31234             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31235         } 
31236     },
31237     /**
31238      * Sets this button's click handler
31239      * @param {Function} handler The function to call when the button is clicked
31240      * @param {Object} scope (optional) Scope for the function passed above
31241      */
31242     setHandler : function(handler, scope){
31243         this.handler = handler;
31244         this.scope = scope;  
31245     },
31246     
31247     /**
31248      * Sets this button's arrow click handler
31249      * @param {Function} handler The function to call when the arrow is clicked
31250      * @param {Object} scope (optional) Scope for the function passed above
31251      */
31252     setArrowHandler : function(handler, scope){
31253         this.arrowHandler = handler;
31254         this.scope = scope;  
31255     },
31256     
31257     /**
31258      * Focus the button
31259      */
31260     focus : function(){
31261         if(this.el){
31262             this.el.child("button:first").focus();
31263         }
31264     },
31265
31266     // private
31267     onClick : function(e){
31268         e.preventDefault();
31269         if(!this.disabled){
31270             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31271                 if(this.menu && !this.menu.isVisible()){
31272                     this.menu.show(this.el, this.menuAlign);
31273                 }
31274                 this.fireEvent("arrowclick", this, e);
31275                 if(this.arrowHandler){
31276                     this.arrowHandler.call(this.scope || this, this, e);
31277                 }
31278             }else{
31279                 this.fireEvent("click", this, e);
31280                 if(this.handler){
31281                     this.handler.call(this.scope || this, this, e);
31282                 }
31283             }
31284         }
31285     },
31286     // private
31287     onMouseDown : function(e){
31288         if(!this.disabled){
31289             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31290         }
31291     },
31292     // private
31293     onMouseUp : function(e){
31294         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31295     }   
31296 });
31297
31298
31299 // backwards compat
31300 Roo.MenuButton = Roo.SplitButton;/*
31301  * Based on:
31302  * Ext JS Library 1.1.1
31303  * Copyright(c) 2006-2007, Ext JS, LLC.
31304  *
31305  * Originally Released Under LGPL - original licence link has changed is not relivant.
31306  *
31307  * Fork - LGPL
31308  * <script type="text/javascript">
31309  */
31310
31311 /**
31312  * @class Roo.Toolbar
31313  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31314  * Basic Toolbar class.
31315  * @constructor
31316  * Creates a new Toolbar
31317  * @param {Object} container The config object
31318  */ 
31319 Roo.Toolbar = function(container, buttons, config)
31320 {
31321     /// old consturctor format still supported..
31322     if(container instanceof Array){ // omit the container for later rendering
31323         buttons = container;
31324         config = buttons;
31325         container = null;
31326     }
31327     if (typeof(container) == 'object' && container.xtype) {
31328         config = container;
31329         container = config.container;
31330         buttons = config.buttons || []; // not really - use items!!
31331     }
31332     var xitems = [];
31333     if (config && config.items) {
31334         xitems = config.items;
31335         delete config.items;
31336     }
31337     Roo.apply(this, config);
31338     this.buttons = buttons;
31339     
31340     if(container){
31341         this.render(container);
31342     }
31343     this.xitems = xitems;
31344     Roo.each(xitems, function(b) {
31345         this.add(b);
31346     }, this);
31347     
31348 };
31349
31350 Roo.Toolbar.prototype = {
31351     /**
31352      * @cfg {Array} items
31353      * array of button configs or elements to add (will be converted to a MixedCollection)
31354      */
31355     items: false,
31356     /**
31357      * @cfg {String/HTMLElement/Element} container
31358      * The id or element that will contain the toolbar
31359      */
31360     // private
31361     render : function(ct){
31362         this.el = Roo.get(ct);
31363         if(this.cls){
31364             this.el.addClass(this.cls);
31365         }
31366         // using a table allows for vertical alignment
31367         // 100% width is needed by Safari...
31368         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31369         this.tr = this.el.child("tr", true);
31370         var autoId = 0;
31371         this.items = new Roo.util.MixedCollection(false, function(o){
31372             return o.id || ("item" + (++autoId));
31373         });
31374         if(this.buttons){
31375             this.add.apply(this, this.buttons);
31376             delete this.buttons;
31377         }
31378     },
31379
31380     /**
31381      * Adds element(s) to the toolbar -- this function takes a variable number of 
31382      * arguments of mixed type and adds them to the toolbar.
31383      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31384      * <ul>
31385      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31386      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31387      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31388      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31389      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31390      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31391      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31392      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31393      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31394      * </ul>
31395      * @param {Mixed} arg2
31396      * @param {Mixed} etc.
31397      */
31398     add : function(){
31399         var a = arguments, l = a.length;
31400         for(var i = 0; i < l; i++){
31401             this._add(a[i]);
31402         }
31403     },
31404     // private..
31405     _add : function(el) {
31406         
31407         if (el.xtype) {
31408             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31409         }
31410         
31411         if (el.applyTo){ // some kind of form field
31412             return this.addField(el);
31413         } 
31414         if (el.render){ // some kind of Toolbar.Item
31415             return this.addItem(el);
31416         }
31417         if (typeof el == "string"){ // string
31418             if(el == "separator" || el == "-"){
31419                 return this.addSeparator();
31420             }
31421             if (el == " "){
31422                 return this.addSpacer();
31423             }
31424             if(el == "->"){
31425                 return this.addFill();
31426             }
31427             return this.addText(el);
31428             
31429         }
31430         if(el.tagName){ // element
31431             return this.addElement(el);
31432         }
31433         if(typeof el == "object"){ // must be button config?
31434             return this.addButton(el);
31435         }
31436         // and now what?!?!
31437         return false;
31438         
31439     },
31440     
31441     /**
31442      * Add an Xtype element
31443      * @param {Object} xtype Xtype Object
31444      * @return {Object} created Object
31445      */
31446     addxtype : function(e){
31447         return this.add(e);  
31448     },
31449     
31450     /**
31451      * Returns the Element for this toolbar.
31452      * @return {Roo.Element}
31453      */
31454     getEl : function(){
31455         return this.el;  
31456     },
31457     
31458     /**
31459      * Adds a separator
31460      * @return {Roo.Toolbar.Item} The separator item
31461      */
31462     addSeparator : function(){
31463         return this.addItem(new Roo.Toolbar.Separator());
31464     },
31465
31466     /**
31467      * Adds a spacer element
31468      * @return {Roo.Toolbar.Spacer} The spacer item
31469      */
31470     addSpacer : function(){
31471         return this.addItem(new Roo.Toolbar.Spacer());
31472     },
31473
31474     /**
31475      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31476      * @return {Roo.Toolbar.Fill} The fill item
31477      */
31478     addFill : function(){
31479         return this.addItem(new Roo.Toolbar.Fill());
31480     },
31481
31482     /**
31483      * Adds any standard HTML element to the toolbar
31484      * @param {String/HTMLElement/Element} el The element or id of the element to add
31485      * @return {Roo.Toolbar.Item} The element's item
31486      */
31487     addElement : function(el){
31488         return this.addItem(new Roo.Toolbar.Item(el));
31489     },
31490     /**
31491      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31492      * @type Roo.util.MixedCollection  
31493      */
31494     items : false,
31495      
31496     /**
31497      * Adds any Toolbar.Item or subclass
31498      * @param {Roo.Toolbar.Item} item
31499      * @return {Roo.Toolbar.Item} The item
31500      */
31501     addItem : function(item){
31502         var td = this.nextBlock();
31503         item.render(td);
31504         this.items.add(item);
31505         return item;
31506     },
31507     
31508     /**
31509      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31510      * @param {Object/Array} config A button config or array of configs
31511      * @return {Roo.Toolbar.Button/Array}
31512      */
31513     addButton : function(config){
31514         if(config instanceof Array){
31515             var buttons = [];
31516             for(var i = 0, len = config.length; i < len; i++) {
31517                 buttons.push(this.addButton(config[i]));
31518             }
31519             return buttons;
31520         }
31521         var b = config;
31522         if(!(config instanceof Roo.Toolbar.Button)){
31523             b = config.split ?
31524                 new Roo.Toolbar.SplitButton(config) :
31525                 new Roo.Toolbar.Button(config);
31526         }
31527         var td = this.nextBlock();
31528         b.render(td);
31529         this.items.add(b);
31530         return b;
31531     },
31532     
31533     /**
31534      * Adds text to the toolbar
31535      * @param {String} text The text to add
31536      * @return {Roo.Toolbar.Item} The element's item
31537      */
31538     addText : function(text){
31539         return this.addItem(new Roo.Toolbar.TextItem(text));
31540     },
31541     
31542     /**
31543      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31544      * @param {Number} index The index where the item is to be inserted
31545      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31546      * @return {Roo.Toolbar.Button/Item}
31547      */
31548     insertButton : function(index, item){
31549         if(item instanceof Array){
31550             var buttons = [];
31551             for(var i = 0, len = item.length; i < len; i++) {
31552                buttons.push(this.insertButton(index + i, item[i]));
31553             }
31554             return buttons;
31555         }
31556         if (!(item instanceof Roo.Toolbar.Button)){
31557            item = new Roo.Toolbar.Button(item);
31558         }
31559         var td = document.createElement("td");
31560         this.tr.insertBefore(td, this.tr.childNodes[index]);
31561         item.render(td);
31562         this.items.insert(index, item);
31563         return item;
31564     },
31565     
31566     /**
31567      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31568      * @param {Object} config
31569      * @return {Roo.Toolbar.Item} The element's item
31570      */
31571     addDom : function(config, returnEl){
31572         var td = this.nextBlock();
31573         Roo.DomHelper.overwrite(td, config);
31574         var ti = new Roo.Toolbar.Item(td.firstChild);
31575         ti.render(td);
31576         this.items.add(ti);
31577         return ti;
31578     },
31579
31580     /**
31581      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31582      * @type Roo.util.MixedCollection  
31583      */
31584     fields : false,
31585     
31586     /**
31587      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31588      * Note: the field should not have been rendered yet. For a field that has already been
31589      * rendered, use {@link #addElement}.
31590      * @param {Roo.form.Field} field
31591      * @return {Roo.ToolbarItem}
31592      */
31593      
31594       
31595     addField : function(field) {
31596         if (!this.fields) {
31597             var autoId = 0;
31598             this.fields = new Roo.util.MixedCollection(false, function(o){
31599                 return o.id || ("item" + (++autoId));
31600             });
31601
31602         }
31603         
31604         var td = this.nextBlock();
31605         field.render(td);
31606         var ti = new Roo.Toolbar.Item(td.firstChild);
31607         ti.render(td);
31608         this.items.add(ti);
31609         this.fields.add(field);
31610         return ti;
31611     },
31612     /**
31613      * Hide the toolbar
31614      * @method hide
31615      */
31616      
31617       
31618     hide : function()
31619     {
31620         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31621         this.el.child('div').hide();
31622     },
31623     /**
31624      * Show the toolbar
31625      * @method show
31626      */
31627     show : function()
31628     {
31629         this.el.child('div').show();
31630     },
31631       
31632     // private
31633     nextBlock : function(){
31634         var td = document.createElement("td");
31635         this.tr.appendChild(td);
31636         return td;
31637     },
31638
31639     // private
31640     destroy : function(){
31641         if(this.items){ // rendered?
31642             Roo.destroy.apply(Roo, this.items.items);
31643         }
31644         if(this.fields){ // rendered?
31645             Roo.destroy.apply(Roo, this.fields.items);
31646         }
31647         Roo.Element.uncache(this.el, this.tr);
31648     }
31649 };
31650
31651 /**
31652  * @class Roo.Toolbar.Item
31653  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31654  * @constructor
31655  * Creates a new Item
31656  * @param {HTMLElement} el 
31657  */
31658 Roo.Toolbar.Item = function(el){
31659     var cfg = {};
31660     if (typeof (el.xtype) != 'undefined') {
31661         cfg = el;
31662         el = cfg.el;
31663     }
31664     
31665     this.el = Roo.getDom(el);
31666     this.id = Roo.id(this.el);
31667     this.hidden = false;
31668     
31669     this.addEvents({
31670          /**
31671              * @event render
31672              * Fires when the button is rendered
31673              * @param {Button} this
31674              */
31675         'render': true
31676     });
31677     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31678 };
31679 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31680 //Roo.Toolbar.Item.prototype = {
31681     
31682     /**
31683      * Get this item's HTML Element
31684      * @return {HTMLElement}
31685      */
31686     getEl : function(){
31687        return this.el;  
31688     },
31689
31690     // private
31691     render : function(td){
31692         
31693          this.td = td;
31694         td.appendChild(this.el);
31695         
31696         this.fireEvent('render', this);
31697     },
31698     
31699     /**
31700      * Removes and destroys this item.
31701      */
31702     destroy : function(){
31703         this.td.parentNode.removeChild(this.td);
31704     },
31705     
31706     /**
31707      * Shows this item.
31708      */
31709     show: function(){
31710         this.hidden = false;
31711         this.td.style.display = "";
31712     },
31713     
31714     /**
31715      * Hides this item.
31716      */
31717     hide: function(){
31718         this.hidden = true;
31719         this.td.style.display = "none";
31720     },
31721     
31722     /**
31723      * Convenience function for boolean show/hide.
31724      * @param {Boolean} visible true to show/false to hide
31725      */
31726     setVisible: function(visible){
31727         if(visible) {
31728             this.show();
31729         }else{
31730             this.hide();
31731         }
31732     },
31733     
31734     /**
31735      * Try to focus this item.
31736      */
31737     focus : function(){
31738         Roo.fly(this.el).focus();
31739     },
31740     
31741     /**
31742      * Disables this item.
31743      */
31744     disable : function(){
31745         Roo.fly(this.td).addClass("x-item-disabled");
31746         this.disabled = true;
31747         this.el.disabled = true;
31748     },
31749     
31750     /**
31751      * Enables this item.
31752      */
31753     enable : function(){
31754         Roo.fly(this.td).removeClass("x-item-disabled");
31755         this.disabled = false;
31756         this.el.disabled = false;
31757     }
31758 });
31759
31760
31761 /**
31762  * @class Roo.Toolbar.Separator
31763  * @extends Roo.Toolbar.Item
31764  * A simple toolbar separator class
31765  * @constructor
31766  * Creates a new Separator
31767  */
31768 Roo.Toolbar.Separator = function(cfg){
31769     
31770     var s = document.createElement("span");
31771     s.className = "ytb-sep";
31772     if (cfg) {
31773         cfg.el = s;
31774     }
31775     
31776     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31777 };
31778 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31779     enable:Roo.emptyFn,
31780     disable:Roo.emptyFn,
31781     focus:Roo.emptyFn
31782 });
31783
31784 /**
31785  * @class Roo.Toolbar.Spacer
31786  * @extends Roo.Toolbar.Item
31787  * A simple element that adds extra horizontal space to a toolbar.
31788  * @constructor
31789  * Creates a new Spacer
31790  */
31791 Roo.Toolbar.Spacer = function(cfg){
31792     var s = document.createElement("div");
31793     s.className = "ytb-spacer";
31794     if (cfg) {
31795         cfg.el = s;
31796     }
31797     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31798 };
31799 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31800     enable:Roo.emptyFn,
31801     disable:Roo.emptyFn,
31802     focus:Roo.emptyFn
31803 });
31804
31805 /**
31806  * @class Roo.Toolbar.Fill
31807  * @extends Roo.Toolbar.Spacer
31808  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31809  * @constructor
31810  * Creates a new Spacer
31811  */
31812 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31813     // private
31814     render : function(td){
31815         td.style.width = '100%';
31816         Roo.Toolbar.Fill.superclass.render.call(this, td);
31817     }
31818 });
31819
31820 /**
31821  * @class Roo.Toolbar.TextItem
31822  * @extends Roo.Toolbar.Item
31823  * A simple class that renders text directly into a toolbar.
31824  * @constructor
31825  * Creates a new TextItem
31826  * @cfg {string} text 
31827  */
31828 Roo.Toolbar.TextItem = function(cfg){
31829     var  text = cfg || "";
31830     if (typeof(cfg) == 'object') {
31831         text = cfg.text || "";
31832     }  else {
31833         cfg = null;
31834     }
31835     var s = document.createElement("span");
31836     s.className = "ytb-text";
31837     s.innerHTML = text;
31838     if (cfg) {
31839         cfg.el  = s;
31840     }
31841     
31842     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31843 };
31844 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31845     
31846      
31847     enable:Roo.emptyFn,
31848     disable:Roo.emptyFn,
31849     focus:Roo.emptyFn,
31850      /**
31851      * Shows this button
31852      */
31853     show: function(){
31854         this.hidden = false;
31855         this.el.style.display = "";
31856     },
31857     
31858     /**
31859      * Hides this button
31860      */
31861     hide: function(){
31862         this.hidden = true;
31863         this.el.style.display = "none";
31864     }
31865     
31866 });
31867
31868 /**
31869  * @class Roo.Toolbar.Button
31870  * @extends Roo.Button
31871  * A button that renders into a toolbar.
31872  * @constructor
31873  * Creates a new Button
31874  * @param {Object} config A standard {@link Roo.Button} config object
31875  */
31876 Roo.Toolbar.Button = function(config){
31877     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31878 };
31879 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31880 {
31881     
31882     
31883     render : function(td){
31884         this.td = td;
31885         Roo.Toolbar.Button.superclass.render.call(this, td);
31886     },
31887     
31888     /**
31889      * Removes and destroys this button
31890      */
31891     destroy : function(){
31892         Roo.Toolbar.Button.superclass.destroy.call(this);
31893         this.td.parentNode.removeChild(this.td);
31894     },
31895     
31896     /**
31897      * Shows this button
31898      */
31899     show: function(){
31900         this.hidden = false;
31901         this.td.style.display = "";
31902     },
31903     
31904     /**
31905      * Hides this button
31906      */
31907     hide: function(){
31908         this.hidden = true;
31909         this.td.style.display = "none";
31910     },
31911
31912     /**
31913      * Disables this item
31914      */
31915     disable : function(){
31916         Roo.fly(this.td).addClass("x-item-disabled");
31917         this.disabled = true;
31918     },
31919
31920     /**
31921      * Enables this item
31922      */
31923     enable : function(){
31924         Roo.fly(this.td).removeClass("x-item-disabled");
31925         this.disabled = false;
31926     }
31927 });
31928 // backwards compat
31929 Roo.ToolbarButton = Roo.Toolbar.Button;
31930
31931 /**
31932  * @class Roo.Toolbar.SplitButton
31933  * @extends Roo.SplitButton
31934  * A menu button that renders into a toolbar.
31935  * @constructor
31936  * Creates a new SplitButton
31937  * @param {Object} config A standard {@link Roo.SplitButton} config object
31938  */
31939 Roo.Toolbar.SplitButton = function(config){
31940     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31941 };
31942 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31943     render : function(td){
31944         this.td = td;
31945         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31946     },
31947     
31948     /**
31949      * Removes and destroys this button
31950      */
31951     destroy : function(){
31952         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31953         this.td.parentNode.removeChild(this.td);
31954     },
31955     
31956     /**
31957      * Shows this button
31958      */
31959     show: function(){
31960         this.hidden = false;
31961         this.td.style.display = "";
31962     },
31963     
31964     /**
31965      * Hides this button
31966      */
31967     hide: function(){
31968         this.hidden = true;
31969         this.td.style.display = "none";
31970     }
31971 });
31972
31973 // backwards compat
31974 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31975  * Based on:
31976  * Ext JS Library 1.1.1
31977  * Copyright(c) 2006-2007, Ext JS, LLC.
31978  *
31979  * Originally Released Under LGPL - original licence link has changed is not relivant.
31980  *
31981  * Fork - LGPL
31982  * <script type="text/javascript">
31983  */
31984  
31985 /**
31986  * @class Roo.PagingToolbar
31987  * @extends Roo.Toolbar
31988  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31989  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31990  * @constructor
31991  * Create a new PagingToolbar
31992  * @param {Object} config The config object
31993  */
31994 Roo.PagingToolbar = function(el, ds, config)
31995 {
31996     // old args format still supported... - xtype is prefered..
31997     if (typeof(el) == 'object' && el.xtype) {
31998         // created from xtype...
31999         config = el;
32000         ds = el.dataSource;
32001         el = config.container;
32002     }
32003     var items = [];
32004     if (config.items) {
32005         items = config.items;
32006         config.items = [];
32007     }
32008     
32009     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32010     this.ds = ds;
32011     this.cursor = 0;
32012     this.renderButtons(this.el);
32013     this.bind(ds);
32014     
32015     // supprot items array.
32016    
32017     Roo.each(items, function(e) {
32018         this.add(Roo.factory(e));
32019     },this);
32020     
32021 };
32022
32023 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32024    
32025     /**
32026      * @cfg {String/HTMLElement/Element} container
32027      * container The id or element that will contain the toolbar
32028      */
32029     /**
32030      * @cfg {Boolean} displayInfo
32031      * True to display the displayMsg (defaults to false)
32032      */
32033     
32034     
32035     /**
32036      * @cfg {Number} pageSize
32037      * The number of records to display per page (defaults to 20)
32038      */
32039     pageSize: 20,
32040     /**
32041      * @cfg {String} displayMsg
32042      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32043      */
32044     displayMsg : 'Displaying {0} - {1} of {2}',
32045     /**
32046      * @cfg {String} emptyMsg
32047      * The message to display when no records are found (defaults to "No data to display")
32048      */
32049     emptyMsg : 'No data to display',
32050     /**
32051      * Customizable piece of the default paging text (defaults to "Page")
32052      * @type String
32053      */
32054     beforePageText : "Page",
32055     /**
32056      * Customizable piece of the default paging text (defaults to "of %0")
32057      * @type String
32058      */
32059     afterPageText : "of {0}",
32060     /**
32061      * Customizable piece of the default paging text (defaults to "First Page")
32062      * @type String
32063      */
32064     firstText : "First Page",
32065     /**
32066      * Customizable piece of the default paging text (defaults to "Previous Page")
32067      * @type String
32068      */
32069     prevText : "Previous Page",
32070     /**
32071      * Customizable piece of the default paging text (defaults to "Next Page")
32072      * @type String
32073      */
32074     nextText : "Next Page",
32075     /**
32076      * Customizable piece of the default paging text (defaults to "Last Page")
32077      * @type String
32078      */
32079     lastText : "Last Page",
32080     /**
32081      * Customizable piece of the default paging text (defaults to "Refresh")
32082      * @type String
32083      */
32084     refreshText : "Refresh",
32085
32086     // private
32087     renderButtons : function(el){
32088         Roo.PagingToolbar.superclass.render.call(this, el);
32089         this.first = this.addButton({
32090             tooltip: this.firstText,
32091             cls: "x-btn-icon x-grid-page-first",
32092             disabled: true,
32093             handler: this.onClick.createDelegate(this, ["first"])
32094         });
32095         this.prev = this.addButton({
32096             tooltip: this.prevText,
32097             cls: "x-btn-icon x-grid-page-prev",
32098             disabled: true,
32099             handler: this.onClick.createDelegate(this, ["prev"])
32100         });
32101         //this.addSeparator();
32102         this.add(this.beforePageText);
32103         this.field = Roo.get(this.addDom({
32104            tag: "input",
32105            type: "text",
32106            size: "3",
32107            value: "1",
32108            cls: "x-grid-page-number"
32109         }).el);
32110         this.field.on("keydown", this.onPagingKeydown, this);
32111         this.field.on("focus", function(){this.dom.select();});
32112         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32113         this.field.setHeight(18);
32114         //this.addSeparator();
32115         this.next = this.addButton({
32116             tooltip: this.nextText,
32117             cls: "x-btn-icon x-grid-page-next",
32118             disabled: true,
32119             handler: this.onClick.createDelegate(this, ["next"])
32120         });
32121         this.last = this.addButton({
32122             tooltip: this.lastText,
32123             cls: "x-btn-icon x-grid-page-last",
32124             disabled: true,
32125             handler: this.onClick.createDelegate(this, ["last"])
32126         });
32127         //this.addSeparator();
32128         this.loading = this.addButton({
32129             tooltip: this.refreshText,
32130             cls: "x-btn-icon x-grid-loading",
32131             handler: this.onClick.createDelegate(this, ["refresh"])
32132         });
32133
32134         if(this.displayInfo){
32135             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32136         }
32137     },
32138
32139     // private
32140     updateInfo : function(){
32141         if(this.displayEl){
32142             var count = this.ds.getCount();
32143             var msg = count == 0 ?
32144                 this.emptyMsg :
32145                 String.format(
32146                     this.displayMsg,
32147                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32148                 );
32149             this.displayEl.update(msg);
32150         }
32151     },
32152
32153     // private
32154     onLoad : function(ds, r, o){
32155        this.cursor = o.params ? o.params.start : 0;
32156        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32157
32158        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32159        this.field.dom.value = ap;
32160        this.first.setDisabled(ap == 1);
32161        this.prev.setDisabled(ap == 1);
32162        this.next.setDisabled(ap == ps);
32163        this.last.setDisabled(ap == ps);
32164        this.loading.enable();
32165        this.updateInfo();
32166     },
32167
32168     // private
32169     getPageData : function(){
32170         var total = this.ds.getTotalCount();
32171         return {
32172             total : total,
32173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32175         };
32176     },
32177
32178     // private
32179     onLoadError : function(){
32180         this.loading.enable();
32181     },
32182
32183     // private
32184     onPagingKeydown : function(e){
32185         var k = e.getKey();
32186         var d = this.getPageData();
32187         if(k == e.RETURN){
32188             var v = this.field.dom.value, pageNum;
32189             if(!v || isNaN(pageNum = parseInt(v, 10))){
32190                 this.field.dom.value = d.activePage;
32191                 return;
32192             }
32193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32195             e.stopEvent();
32196         }
32197         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))
32198         {
32199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32200           this.field.dom.value = pageNum;
32201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32202           e.stopEvent();
32203         }
32204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32205         {
32206           var v = this.field.dom.value, pageNum; 
32207           var increment = (e.shiftKey) ? 10 : 1;
32208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32209             increment *= -1;
32210           }
32211           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32212             this.field.dom.value = d.activePage;
32213             return;
32214           }
32215           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32216           {
32217             this.field.dom.value = parseInt(v, 10) + increment;
32218             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32219             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32220           }
32221           e.stopEvent();
32222         }
32223     },
32224
32225     // private
32226     beforeLoad : function(){
32227         if(this.loading){
32228             this.loading.disable();
32229         }
32230     },
32231
32232     // private
32233     onClick : function(which){
32234         var ds = this.ds;
32235         switch(which){
32236             case "first":
32237                 ds.load({params:{start: 0, limit: this.pageSize}});
32238             break;
32239             case "prev":
32240                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32241             break;
32242             case "next":
32243                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32244             break;
32245             case "last":
32246                 var total = ds.getTotalCount();
32247                 var extra = total % this.pageSize;
32248                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32249                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32250             break;
32251             case "refresh":
32252                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32253             break;
32254         }
32255     },
32256
32257     /**
32258      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32259      * @param {Roo.data.Store} store The data store to unbind
32260      */
32261     unbind : function(ds){
32262         ds.un("beforeload", this.beforeLoad, this);
32263         ds.un("load", this.onLoad, this);
32264         ds.un("loadexception", this.onLoadError, this);
32265         ds.un("remove", this.updateInfo, this);
32266         ds.un("add", this.updateInfo, this);
32267         this.ds = undefined;
32268     },
32269
32270     /**
32271      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32272      * @param {Roo.data.Store} store The data store to bind
32273      */
32274     bind : function(ds){
32275         ds.on("beforeload", this.beforeLoad, this);
32276         ds.on("load", this.onLoad, this);
32277         ds.on("loadexception", this.onLoadError, this);
32278         ds.on("remove", this.updateInfo, this);
32279         ds.on("add", this.updateInfo, this);
32280         this.ds = ds;
32281     }
32282 });/*
32283  * Based on:
32284  * Ext JS Library 1.1.1
32285  * Copyright(c) 2006-2007, Ext JS, LLC.
32286  *
32287  * Originally Released Under LGPL - original licence link has changed is not relivant.
32288  *
32289  * Fork - LGPL
32290  * <script type="text/javascript">
32291  */
32292
32293 /**
32294  * @class Roo.Resizable
32295  * @extends Roo.util.Observable
32296  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32297  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32298  * 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
32299  * the element will be wrapped for you automatically.</p>
32300  * <p>Here is the list of valid resize handles:</p>
32301  * <pre>
32302 Value   Description
32303 ------  -------------------
32304  'n'     north
32305  's'     south
32306  'e'     east
32307  'w'     west
32308  'nw'    northwest
32309  'sw'    southwest
32310  'se'    southeast
32311  'ne'    northeast
32312  'hd'    horizontal drag
32313  'all'   all
32314 </pre>
32315  * <p>Here's an example showing the creation of a typical Resizable:</p>
32316  * <pre><code>
32317 var resizer = new Roo.Resizable("element-id", {
32318     handles: 'all',
32319     minWidth: 200,
32320     minHeight: 100,
32321     maxWidth: 500,
32322     maxHeight: 400,
32323     pinned: true
32324 });
32325 resizer.on("resize", myHandler);
32326 </code></pre>
32327  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32328  * resizer.east.setDisplayed(false);</p>
32329  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32330  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32331  * resize operation's new size (defaults to [0, 0])
32332  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32333  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32334  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32335  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32336  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32337  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32338  * @cfg {Number} width The width of the element in pixels (defaults to null)
32339  * @cfg {Number} height The height of the element in pixels (defaults to null)
32340  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32341  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32342  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32343  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32344  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32345  * in favor of the handles config option (defaults to false)
32346  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32347  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32348  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32349  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32350  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32351  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32352  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32353  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32354  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32355  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32356  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32357  * @constructor
32358  * Create a new resizable component
32359  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32360  * @param {Object} config configuration options
32361   */
32362 Roo.Resizable = function(el, config)
32363 {
32364     this.el = Roo.get(el);
32365
32366     if(config && config.wrap){
32367         config.resizeChild = this.el;
32368         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32369         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32370         this.el.setStyle("overflow", "hidden");
32371         this.el.setPositioning(config.resizeChild.getPositioning());
32372         config.resizeChild.clearPositioning();
32373         if(!config.width || !config.height){
32374             var csize = config.resizeChild.getSize();
32375             this.el.setSize(csize.width, csize.height);
32376         }
32377         if(config.pinned && !config.adjustments){
32378             config.adjustments = "auto";
32379         }
32380     }
32381
32382     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32383     this.proxy.unselectable();
32384     this.proxy.enableDisplayMode('block');
32385
32386     Roo.apply(this, config);
32387
32388     if(this.pinned){
32389         this.disableTrackOver = true;
32390         this.el.addClass("x-resizable-pinned");
32391     }
32392     // if the element isn't positioned, make it relative
32393     var position = this.el.getStyle("position");
32394     if(position != "absolute" && position != "fixed"){
32395         this.el.setStyle("position", "relative");
32396     }
32397     if(!this.handles){ // no handles passed, must be legacy style
32398         this.handles = 's,e,se';
32399         if(this.multiDirectional){
32400             this.handles += ',n,w';
32401         }
32402     }
32403     if(this.handles == "all"){
32404         this.handles = "n s e w ne nw se sw";
32405     }
32406     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32407     var ps = Roo.Resizable.positions;
32408     for(var i = 0, len = hs.length; i < len; i++){
32409         if(hs[i] && ps[hs[i]]){
32410             var pos = ps[hs[i]];
32411             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32412         }
32413     }
32414     // legacy
32415     this.corner = this.southeast;
32416     
32417     // updateBox = the box can move..
32418     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32419         this.updateBox = true;
32420     }
32421
32422     this.activeHandle = null;
32423
32424     if(this.resizeChild){
32425         if(typeof this.resizeChild == "boolean"){
32426             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32427         }else{
32428             this.resizeChild = Roo.get(this.resizeChild, true);
32429         }
32430     }
32431     
32432     if(this.adjustments == "auto"){
32433         var rc = this.resizeChild;
32434         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32435         if(rc && (hw || hn)){
32436             rc.position("relative");
32437             rc.setLeft(hw ? hw.el.getWidth() : 0);
32438             rc.setTop(hn ? hn.el.getHeight() : 0);
32439         }
32440         this.adjustments = [
32441             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32442             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32443         ];
32444     }
32445
32446     if(this.draggable){
32447         this.dd = this.dynamic ?
32448             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32449         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32450     }
32451
32452     // public events
32453     this.addEvents({
32454         /**
32455          * @event beforeresize
32456          * Fired before resize is allowed. Set enabled to false to cancel resize.
32457          * @param {Roo.Resizable} this
32458          * @param {Roo.EventObject} e The mousedown event
32459          */
32460         "beforeresize" : true,
32461         /**
32462          * @event resizing
32463          * Fired a resizing.
32464          * @param {Roo.Resizable} this
32465          * @param {Number} x The new x position
32466          * @param {Number} y The new y position
32467          * @param {Number} w The new w width
32468          * @param {Number} h The new h hight
32469          * @param {Roo.EventObject} e The mouseup event
32470          */
32471         "resizing" : true,
32472         /**
32473          * @event resize
32474          * Fired after a resize.
32475          * @param {Roo.Resizable} this
32476          * @param {Number} width The new width
32477          * @param {Number} height The new height
32478          * @param {Roo.EventObject} e The mouseup event
32479          */
32480         "resize" : true
32481     });
32482
32483     if(this.width !== null && this.height !== null){
32484         this.resizeTo(this.width, this.height);
32485     }else{
32486         this.updateChildSize();
32487     }
32488     if(Roo.isIE){
32489         this.el.dom.style.zoom = 1;
32490     }
32491     Roo.Resizable.superclass.constructor.call(this);
32492 };
32493
32494 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32495         resizeChild : false,
32496         adjustments : [0, 0],
32497         minWidth : 5,
32498         minHeight : 5,
32499         maxWidth : 10000,
32500         maxHeight : 10000,
32501         enabled : true,
32502         animate : false,
32503         duration : .35,
32504         dynamic : false,
32505         handles : false,
32506         multiDirectional : false,
32507         disableTrackOver : false,
32508         easing : 'easeOutStrong',
32509         widthIncrement : 0,
32510         heightIncrement : 0,
32511         pinned : false,
32512         width : null,
32513         height : null,
32514         preserveRatio : false,
32515         transparent: false,
32516         minX: 0,
32517         minY: 0,
32518         draggable: false,
32519
32520         /**
32521          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32522          */
32523         constrainTo: undefined,
32524         /**
32525          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32526          */
32527         resizeRegion: undefined,
32528
32529
32530     /**
32531      * Perform a manual resize
32532      * @param {Number} width
32533      * @param {Number} height
32534      */
32535     resizeTo : function(width, height){
32536         this.el.setSize(width, height);
32537         this.updateChildSize();
32538         this.fireEvent("resize", this, width, height, null);
32539     },
32540
32541     // private
32542     startSizing : function(e, handle){
32543         this.fireEvent("beforeresize", this, e);
32544         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32545
32546             if(!this.overlay){
32547                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32548                 this.overlay.unselectable();
32549                 this.overlay.enableDisplayMode("block");
32550                 this.overlay.on("mousemove", this.onMouseMove, this);
32551                 this.overlay.on("mouseup", this.onMouseUp, this);
32552             }
32553             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32554
32555             this.resizing = true;
32556             this.startBox = this.el.getBox();
32557             this.startPoint = e.getXY();
32558             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32559                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32560
32561             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32562             this.overlay.show();
32563
32564             if(this.constrainTo) {
32565                 var ct = Roo.get(this.constrainTo);
32566                 this.resizeRegion = ct.getRegion().adjust(
32567                     ct.getFrameWidth('t'),
32568                     ct.getFrameWidth('l'),
32569                     -ct.getFrameWidth('b'),
32570                     -ct.getFrameWidth('r')
32571                 );
32572             }
32573
32574             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32575             this.proxy.show();
32576             this.proxy.setBox(this.startBox);
32577             if(!this.dynamic){
32578                 this.proxy.setStyle('visibility', 'visible');
32579             }
32580         }
32581     },
32582
32583     // private
32584     onMouseDown : function(handle, e){
32585         if(this.enabled){
32586             e.stopEvent();
32587             this.activeHandle = handle;
32588             this.startSizing(e, handle);
32589         }
32590     },
32591
32592     // private
32593     onMouseUp : function(e){
32594         var size = this.resizeElement();
32595         this.resizing = false;
32596         this.handleOut();
32597         this.overlay.hide();
32598         this.proxy.hide();
32599         this.fireEvent("resize", this, size.width, size.height, e);
32600     },
32601
32602     // private
32603     updateChildSize : function(){
32604         
32605         if(this.resizeChild){
32606             var el = this.el;
32607             var child = this.resizeChild;
32608             var adj = this.adjustments;
32609             if(el.dom.offsetWidth){
32610                 var b = el.getSize(true);
32611                 child.setSize(b.width+adj[0], b.height+adj[1]);
32612             }
32613             // Second call here for IE
32614             // The first call enables instant resizing and
32615             // the second call corrects scroll bars if they
32616             // exist
32617             if(Roo.isIE){
32618                 setTimeout(function(){
32619                     if(el.dom.offsetWidth){
32620                         var b = el.getSize(true);
32621                         child.setSize(b.width+adj[0], b.height+adj[1]);
32622                     }
32623                 }, 10);
32624             }
32625         }
32626     },
32627
32628     // private
32629     snap : function(value, inc, min){
32630         if(!inc || !value) {
32631             return value;
32632         }
32633         var newValue = value;
32634         var m = value % inc;
32635         if(m > 0){
32636             if(m > (inc/2)){
32637                 newValue = value + (inc-m);
32638             }else{
32639                 newValue = value - m;
32640             }
32641         }
32642         return Math.max(min, newValue);
32643     },
32644
32645     // private
32646     resizeElement : function(){
32647         var box = this.proxy.getBox();
32648         if(this.updateBox){
32649             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32650         }else{
32651             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32652         }
32653         this.updateChildSize();
32654         if(!this.dynamic){
32655             this.proxy.hide();
32656         }
32657         return box;
32658     },
32659
32660     // private
32661     constrain : function(v, diff, m, mx){
32662         if(v - diff < m){
32663             diff = v - m;
32664         }else if(v - diff > mx){
32665             diff = mx - v;
32666         }
32667         return diff;
32668     },
32669
32670     // private
32671     onMouseMove : function(e){
32672         
32673         if(this.enabled){
32674             try{// try catch so if something goes wrong the user doesn't get hung
32675
32676             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32677                 return;
32678             }
32679
32680             //var curXY = this.startPoint;
32681             var curSize = this.curSize || this.startBox;
32682             var x = this.startBox.x, y = this.startBox.y;
32683             var ox = x, oy = y;
32684             var w = curSize.width, h = curSize.height;
32685             var ow = w, oh = h;
32686             var mw = this.minWidth, mh = this.minHeight;
32687             var mxw = this.maxWidth, mxh = this.maxHeight;
32688             var wi = this.widthIncrement;
32689             var hi = this.heightIncrement;
32690
32691             var eventXY = e.getXY();
32692             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32693             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32694
32695             var pos = this.activeHandle.position;
32696
32697             switch(pos){
32698                 case "east":
32699                     w += diffX;
32700                     w = Math.min(Math.max(mw, w), mxw);
32701                     break;
32702              
32703                 case "south":
32704                     h += diffY;
32705                     h = Math.min(Math.max(mh, h), mxh);
32706                     break;
32707                 case "southeast":
32708                     w += diffX;
32709                     h += diffY;
32710                     w = Math.min(Math.max(mw, w), mxw);
32711                     h = Math.min(Math.max(mh, h), mxh);
32712                     break;
32713                 case "north":
32714                     diffY = this.constrain(h, diffY, mh, mxh);
32715                     y += diffY;
32716                     h -= diffY;
32717                     break;
32718                 case "hdrag":
32719                     
32720                     if (wi) {
32721                         var adiffX = Math.abs(diffX);
32722                         var sub = (adiffX % wi); // how much 
32723                         if (sub > (wi/2)) { // far enough to snap
32724                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32725                         } else {
32726                             // remove difference.. 
32727                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32728                         }
32729                     }
32730                     x += diffX;
32731                     x = Math.max(this.minX, x);
32732                     break;
32733                 case "west":
32734                     diffX = this.constrain(w, diffX, mw, mxw);
32735                     x += diffX;
32736                     w -= diffX;
32737                     break;
32738                 case "northeast":
32739                     w += diffX;
32740                     w = Math.min(Math.max(mw, w), mxw);
32741                     diffY = this.constrain(h, diffY, mh, mxh);
32742                     y += diffY;
32743                     h -= diffY;
32744                     break;
32745                 case "northwest":
32746                     diffX = this.constrain(w, diffX, mw, mxw);
32747                     diffY = this.constrain(h, diffY, mh, mxh);
32748                     y += diffY;
32749                     h -= diffY;
32750                     x += diffX;
32751                     w -= diffX;
32752                     break;
32753                case "southwest":
32754                     diffX = this.constrain(w, diffX, mw, mxw);
32755                     h += diffY;
32756                     h = Math.min(Math.max(mh, h), mxh);
32757                     x += diffX;
32758                     w -= diffX;
32759                     break;
32760             }
32761
32762             var sw = this.snap(w, wi, mw);
32763             var sh = this.snap(h, hi, mh);
32764             if(sw != w || sh != h){
32765                 switch(pos){
32766                     case "northeast":
32767                         y -= sh - h;
32768                     break;
32769                     case "north":
32770                         y -= sh - h;
32771                         break;
32772                     case "southwest":
32773                         x -= sw - w;
32774                     break;
32775                     case "west":
32776                         x -= sw - w;
32777                         break;
32778                     case "northwest":
32779                         x -= sw - w;
32780                         y -= sh - h;
32781                     break;
32782                 }
32783                 w = sw;
32784                 h = sh;
32785             }
32786
32787             if(this.preserveRatio){
32788                 switch(pos){
32789                     case "southeast":
32790                     case "east":
32791                         h = oh * (w/ow);
32792                         h = Math.min(Math.max(mh, h), mxh);
32793                         w = ow * (h/oh);
32794                        break;
32795                     case "south":
32796                         w = ow * (h/oh);
32797                         w = Math.min(Math.max(mw, w), mxw);
32798                         h = oh * (w/ow);
32799                         break;
32800                     case "northeast":
32801                         w = ow * (h/oh);
32802                         w = Math.min(Math.max(mw, w), mxw);
32803                         h = oh * (w/ow);
32804                     break;
32805                     case "north":
32806                         var tw = w;
32807                         w = ow * (h/oh);
32808                         w = Math.min(Math.max(mw, w), mxw);
32809                         h = oh * (w/ow);
32810                         x += (tw - w) / 2;
32811                         break;
32812                     case "southwest":
32813                         h = oh * (w/ow);
32814                         h = Math.min(Math.max(mh, h), mxh);
32815                         var tw = w;
32816                         w = ow * (h/oh);
32817                         x += tw - w;
32818                         break;
32819                     case "west":
32820                         var th = h;
32821                         h = oh * (w/ow);
32822                         h = Math.min(Math.max(mh, h), mxh);
32823                         y += (th - h) / 2;
32824                         var tw = w;
32825                         w = ow * (h/oh);
32826                         x += tw - w;
32827                        break;
32828                     case "northwest":
32829                         var tw = w;
32830                         var th = h;
32831                         h = oh * (w/ow);
32832                         h = Math.min(Math.max(mh, h), mxh);
32833                         w = ow * (h/oh);
32834                         y += th - h;
32835                         x += tw - w;
32836                        break;
32837
32838                 }
32839             }
32840             if (pos == 'hdrag') {
32841                 w = ow;
32842             }
32843             this.proxy.setBounds(x, y, w, h);
32844             if(this.dynamic){
32845                 this.resizeElement();
32846             }
32847             }catch(e){}
32848         }
32849         this.fireEvent("resizing", this, x, y, w, h, e);
32850     },
32851
32852     // private
32853     handleOver : function(){
32854         if(this.enabled){
32855             this.el.addClass("x-resizable-over");
32856         }
32857     },
32858
32859     // private
32860     handleOut : function(){
32861         if(!this.resizing){
32862             this.el.removeClass("x-resizable-over");
32863         }
32864     },
32865
32866     /**
32867      * Returns the element this component is bound to.
32868      * @return {Roo.Element}
32869      */
32870     getEl : function(){
32871         return this.el;
32872     },
32873
32874     /**
32875      * Returns the resizeChild element (or null).
32876      * @return {Roo.Element}
32877      */
32878     getResizeChild : function(){
32879         return this.resizeChild;
32880     },
32881     groupHandler : function()
32882     {
32883         
32884     },
32885     /**
32886      * Destroys this resizable. If the element was wrapped and
32887      * removeEl is not true then the element remains.
32888      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32889      */
32890     destroy : function(removeEl){
32891         this.proxy.remove();
32892         if(this.overlay){
32893             this.overlay.removeAllListeners();
32894             this.overlay.remove();
32895         }
32896         var ps = Roo.Resizable.positions;
32897         for(var k in ps){
32898             if(typeof ps[k] != "function" && this[ps[k]]){
32899                 var h = this[ps[k]];
32900                 h.el.removeAllListeners();
32901                 h.el.remove();
32902             }
32903         }
32904         if(removeEl){
32905             this.el.update("");
32906             this.el.remove();
32907         }
32908     }
32909 });
32910
32911 // private
32912 // hash to map config positions to true positions
32913 Roo.Resizable.positions = {
32914     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32915     hd: "hdrag"
32916 };
32917
32918 // private
32919 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32920     if(!this.tpl){
32921         // only initialize the template if resizable is used
32922         var tpl = Roo.DomHelper.createTemplate(
32923             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32924         );
32925         tpl.compile();
32926         Roo.Resizable.Handle.prototype.tpl = tpl;
32927     }
32928     this.position = pos;
32929     this.rz = rz;
32930     // show north drag fro topdra
32931     var handlepos = pos == 'hdrag' ? 'north' : pos;
32932     
32933     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32934     if (pos == 'hdrag') {
32935         this.el.setStyle('cursor', 'pointer');
32936     }
32937     this.el.unselectable();
32938     if(transparent){
32939         this.el.setOpacity(0);
32940     }
32941     this.el.on("mousedown", this.onMouseDown, this);
32942     if(!disableTrackOver){
32943         this.el.on("mouseover", this.onMouseOver, this);
32944         this.el.on("mouseout", this.onMouseOut, this);
32945     }
32946 };
32947
32948 // private
32949 Roo.Resizable.Handle.prototype = {
32950     afterResize : function(rz){
32951         Roo.log('after?');
32952         // do nothing
32953     },
32954     // private
32955     onMouseDown : function(e){
32956         this.rz.onMouseDown(this, e);
32957     },
32958     // private
32959     onMouseOver : function(e){
32960         this.rz.handleOver(this, e);
32961     },
32962     // private
32963     onMouseOut : function(e){
32964         this.rz.handleOut(this, e);
32965     }
32966 };/*
32967  * Based on:
32968  * Ext JS Library 1.1.1
32969  * Copyright(c) 2006-2007, Ext JS, LLC.
32970  *
32971  * Originally Released Under LGPL - original licence link has changed is not relivant.
32972  *
32973  * Fork - LGPL
32974  * <script type="text/javascript">
32975  */
32976
32977 /**
32978  * @class Roo.Editor
32979  * @extends Roo.Component
32980  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32981  * @constructor
32982  * Create a new Editor
32983  * @param {Roo.form.Field} field The Field object (or descendant)
32984  * @param {Object} config The config object
32985  */
32986 Roo.Editor = function(field, config){
32987     Roo.Editor.superclass.constructor.call(this, config);
32988     this.field = field;
32989     this.addEvents({
32990         /**
32991              * @event beforestartedit
32992              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32993              * false from the handler of this event.
32994              * @param {Editor} this
32995              * @param {Roo.Element} boundEl The underlying element bound to this editor
32996              * @param {Mixed} value The field value being set
32997              */
32998         "beforestartedit" : true,
32999         /**
33000              * @event startedit
33001              * Fires when this editor is displayed
33002              * @param {Roo.Element} boundEl The underlying element bound to this editor
33003              * @param {Mixed} value The starting field value
33004              */
33005         "startedit" : true,
33006         /**
33007              * @event beforecomplete
33008              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33009              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33010              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33011              * event will not fire since no edit actually occurred.
33012              * @param {Editor} this
33013              * @param {Mixed} value The current field value
33014              * @param {Mixed} startValue The original field value
33015              */
33016         "beforecomplete" : true,
33017         /**
33018              * @event complete
33019              * Fires after editing is complete and any changed value has been written to the underlying field.
33020              * @param {Editor} this
33021              * @param {Mixed} value The current field value
33022              * @param {Mixed} startValue The original field value
33023              */
33024         "complete" : true,
33025         /**
33026          * @event specialkey
33027          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33028          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33029          * @param {Roo.form.Field} this
33030          * @param {Roo.EventObject} e The event object
33031          */
33032         "specialkey" : true
33033     });
33034 };
33035
33036 Roo.extend(Roo.Editor, Roo.Component, {
33037     /**
33038      * @cfg {Boolean/String} autosize
33039      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33040      * or "height" to adopt the height only (defaults to false)
33041      */
33042     /**
33043      * @cfg {Boolean} revertInvalid
33044      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33045      * validation fails (defaults to true)
33046      */
33047     /**
33048      * @cfg {Boolean} ignoreNoChange
33049      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33050      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33051      * will never be ignored.
33052      */
33053     /**
33054      * @cfg {Boolean} hideEl
33055      * False to keep the bound element visible while the editor is displayed (defaults to true)
33056      */
33057     /**
33058      * @cfg {Mixed} value
33059      * The data value of the underlying field (defaults to "")
33060      */
33061     value : "",
33062     /**
33063      * @cfg {String} alignment
33064      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33065      */
33066     alignment: "c-c?",
33067     /**
33068      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33069      * for bottom-right shadow (defaults to "frame")
33070      */
33071     shadow : "frame",
33072     /**
33073      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33074      */
33075     constrain : false,
33076     /**
33077      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33078      */
33079     completeOnEnter : false,
33080     /**
33081      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33082      */
33083     cancelOnEsc : false,
33084     /**
33085      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33086      */
33087     updateEl : false,
33088
33089     // private
33090     onRender : function(ct, position){
33091         this.el = new Roo.Layer({
33092             shadow: this.shadow,
33093             cls: "x-editor",
33094             parentEl : ct,
33095             shim : this.shim,
33096             shadowOffset:4,
33097             id: this.id,
33098             constrain: this.constrain
33099         });
33100         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33101         if(this.field.msgTarget != 'title'){
33102             this.field.msgTarget = 'qtip';
33103         }
33104         this.field.render(this.el);
33105         if(Roo.isGecko){
33106             this.field.el.dom.setAttribute('autocomplete', 'off');
33107         }
33108         this.field.on("specialkey", this.onSpecialKey, this);
33109         if(this.swallowKeys){
33110             this.field.el.swallowEvent(['keydown','keypress']);
33111         }
33112         this.field.show();
33113         this.field.on("blur", this.onBlur, this);
33114         if(this.field.grow){
33115             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33116         }
33117     },
33118
33119     onSpecialKey : function(field, e)
33120     {
33121         //Roo.log('editor onSpecialKey');
33122         if(this.completeOnEnter && e.getKey() == e.ENTER){
33123             e.stopEvent();
33124             this.completeEdit();
33125             return;
33126         }
33127         // do not fire special key otherwise it might hide close the editor...
33128         if(e.getKey() == e.ENTER){    
33129             return;
33130         }
33131         if(this.cancelOnEsc && e.getKey() == e.ESC){
33132             this.cancelEdit();
33133             return;
33134         } 
33135         this.fireEvent('specialkey', field, e);
33136     
33137     },
33138
33139     /**
33140      * Starts the editing process and shows the editor.
33141      * @param {String/HTMLElement/Element} el The element to edit
33142      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33143       * to the innerHTML of el.
33144      */
33145     startEdit : function(el, value){
33146         if(this.editing){
33147             this.completeEdit();
33148         }
33149         this.boundEl = Roo.get(el);
33150         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33151         if(!this.rendered){
33152             this.render(this.parentEl || document.body);
33153         }
33154         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33155             return;
33156         }
33157         this.startValue = v;
33158         this.field.setValue(v);
33159         if(this.autoSize){
33160             var sz = this.boundEl.getSize();
33161             switch(this.autoSize){
33162                 case "width":
33163                 this.setSize(sz.width,  "");
33164                 break;
33165                 case "height":
33166                 this.setSize("",  sz.height);
33167                 break;
33168                 default:
33169                 this.setSize(sz.width,  sz.height);
33170             }
33171         }
33172         this.el.alignTo(this.boundEl, this.alignment);
33173         this.editing = true;
33174         if(Roo.QuickTips){
33175             Roo.QuickTips.disable();
33176         }
33177         this.show();
33178     },
33179
33180     /**
33181      * Sets the height and width of this editor.
33182      * @param {Number} width The new width
33183      * @param {Number} height The new height
33184      */
33185     setSize : function(w, h){
33186         this.field.setSize(w, h);
33187         if(this.el){
33188             this.el.sync();
33189         }
33190     },
33191
33192     /**
33193      * Realigns the editor to the bound field based on the current alignment config value.
33194      */
33195     realign : function(){
33196         this.el.alignTo(this.boundEl, this.alignment);
33197     },
33198
33199     /**
33200      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33201      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33202      */
33203     completeEdit : function(remainVisible){
33204         if(!this.editing){
33205             return;
33206         }
33207         var v = this.getValue();
33208         if(this.revertInvalid !== false && !this.field.isValid()){
33209             v = this.startValue;
33210             this.cancelEdit(true);
33211         }
33212         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33213             this.editing = false;
33214             this.hide();
33215             return;
33216         }
33217         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33218             this.editing = false;
33219             if(this.updateEl && this.boundEl){
33220                 this.boundEl.update(v);
33221             }
33222             if(remainVisible !== true){
33223                 this.hide();
33224             }
33225             this.fireEvent("complete", this, v, this.startValue);
33226         }
33227     },
33228
33229     // private
33230     onShow : function(){
33231         this.el.show();
33232         if(this.hideEl !== false){
33233             this.boundEl.hide();
33234         }
33235         this.field.show();
33236         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33237             this.fixIEFocus = true;
33238             this.deferredFocus.defer(50, this);
33239         }else{
33240             this.field.focus();
33241         }
33242         this.fireEvent("startedit", this.boundEl, this.startValue);
33243     },
33244
33245     deferredFocus : function(){
33246         if(this.editing){
33247             this.field.focus();
33248         }
33249     },
33250
33251     /**
33252      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33253      * reverted to the original starting value.
33254      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33255      * cancel (defaults to false)
33256      */
33257     cancelEdit : function(remainVisible){
33258         if(this.editing){
33259             this.setValue(this.startValue);
33260             if(remainVisible !== true){
33261                 this.hide();
33262             }
33263         }
33264     },
33265
33266     // private
33267     onBlur : function(){
33268         if(this.allowBlur !== true && this.editing){
33269             this.completeEdit();
33270         }
33271     },
33272
33273     // private
33274     onHide : function(){
33275         if(this.editing){
33276             this.completeEdit();
33277             return;
33278         }
33279         this.field.blur();
33280         if(this.field.collapse){
33281             this.field.collapse();
33282         }
33283         this.el.hide();
33284         if(this.hideEl !== false){
33285             this.boundEl.show();
33286         }
33287         if(Roo.QuickTips){
33288             Roo.QuickTips.enable();
33289         }
33290     },
33291
33292     /**
33293      * Sets the data value of the editor
33294      * @param {Mixed} value Any valid value supported by the underlying field
33295      */
33296     setValue : function(v){
33297         this.field.setValue(v);
33298     },
33299
33300     /**
33301      * Gets the data value of the editor
33302      * @return {Mixed} The data value
33303      */
33304     getValue : function(){
33305         return this.field.getValue();
33306     }
33307 });/*
33308  * Based on:
33309  * Ext JS Library 1.1.1
33310  * Copyright(c) 2006-2007, Ext JS, LLC.
33311  *
33312  * Originally Released Under LGPL - original licence link has changed is not relivant.
33313  *
33314  * Fork - LGPL
33315  * <script type="text/javascript">
33316  */
33317  
33318 /**
33319  * @class Roo.BasicDialog
33320  * @extends Roo.util.Observable
33321  * @parent none builder
33322  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33323  * <pre><code>
33324 var dlg = new Roo.BasicDialog("my-dlg", {
33325     height: 200,
33326     width: 300,
33327     minHeight: 100,
33328     minWidth: 150,
33329     modal: true,
33330     proxyDrag: true,
33331     shadow: true
33332 });
33333 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33334 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33335 dlg.addButton('Cancel', dlg.hide, dlg);
33336 dlg.show();
33337 </code></pre>
33338   <b>A Dialog should always be a direct child of the body element.</b>
33339  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33340  * @cfg {String} title Default text to display in the title bar (defaults to null)
33341  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33342  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33343  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33344  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33345  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33346  * (defaults to null with no animation)
33347  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33348  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33349  * property for valid values (defaults to 'all')
33350  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33351  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33352  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33353  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33354  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33355  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33356  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33357  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33358  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33359  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33360  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33361  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33362  * draggable = true (defaults to false)
33363  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33364  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33365  * shadow (defaults to false)
33366  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33367  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33368  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33369  * @cfg {Array} buttons Array of buttons
33370  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33371  * @constructor
33372  * Create a new BasicDialog.
33373  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33374  * @param {Object} config Configuration options
33375  */
33376 Roo.BasicDialog = function(el, config){
33377     this.el = Roo.get(el);
33378     var dh = Roo.DomHelper;
33379     if(!this.el && config && config.autoCreate){
33380         if(typeof config.autoCreate == "object"){
33381             if(!config.autoCreate.id){
33382                 config.autoCreate.id = el;
33383             }
33384             this.el = dh.append(document.body,
33385                         config.autoCreate, true);
33386         }else{
33387             this.el = dh.append(document.body,
33388                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33389         }
33390     }
33391     el = this.el;
33392     el.setDisplayed(true);
33393     el.hide = this.hideAction;
33394     this.id = el.id;
33395     el.addClass("x-dlg");
33396
33397     Roo.apply(this, config);
33398
33399     this.proxy = el.createProxy("x-dlg-proxy");
33400     this.proxy.hide = this.hideAction;
33401     this.proxy.setOpacity(.5);
33402     this.proxy.hide();
33403
33404     if(config.width){
33405         el.setWidth(config.width);
33406     }
33407     if(config.height){
33408         el.setHeight(config.height);
33409     }
33410     this.size = el.getSize();
33411     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33412         this.xy = [config.x,config.y];
33413     }else{
33414         this.xy = el.getCenterXY(true);
33415     }
33416     /** The header element @type Roo.Element */
33417     this.header = el.child("> .x-dlg-hd");
33418     /** The body element @type Roo.Element */
33419     this.body = el.child("> .x-dlg-bd");
33420     /** The footer element @type Roo.Element */
33421     this.footer = el.child("> .x-dlg-ft");
33422
33423     if(!this.header){
33424         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33425     }
33426     if(!this.body){
33427         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33428     }
33429
33430     this.header.unselectable();
33431     if(this.title){
33432         this.header.update(this.title);
33433     }
33434     // this element allows the dialog to be focused for keyboard event
33435     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33436     this.focusEl.swallowEvent("click", true);
33437
33438     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33439
33440     // wrap the body and footer for special rendering
33441     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33442     if(this.footer){
33443         this.bwrap.dom.appendChild(this.footer.dom);
33444     }
33445
33446     this.bg = this.el.createChild({
33447         tag: "div", cls:"x-dlg-bg",
33448         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33449     });
33450     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33451
33452
33453     if(this.autoScroll !== false && !this.autoTabs){
33454         this.body.setStyle("overflow", "auto");
33455     }
33456
33457     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33458
33459     if(this.closable !== false){
33460         this.el.addClass("x-dlg-closable");
33461         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33462         this.close.on("click", this.closeClick, this);
33463         this.close.addClassOnOver("x-dlg-close-over");
33464     }
33465     if(this.collapsible !== false){
33466         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33467         this.collapseBtn.on("click", this.collapseClick, this);
33468         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33469         this.header.on("dblclick", this.collapseClick, this);
33470     }
33471     if(this.resizable !== false){
33472         this.el.addClass("x-dlg-resizable");
33473         this.resizer = new Roo.Resizable(el, {
33474             minWidth: this.minWidth || 80,
33475             minHeight:this.minHeight || 80,
33476             handles: this.resizeHandles || "all",
33477             pinned: true
33478         });
33479         this.resizer.on("beforeresize", this.beforeResize, this);
33480         this.resizer.on("resize", this.onResize, this);
33481     }
33482     if(this.draggable !== false){
33483         el.addClass("x-dlg-draggable");
33484         if (!this.proxyDrag) {
33485             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33486         }
33487         else {
33488             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33489         }
33490         dd.setHandleElId(this.header.id);
33491         dd.endDrag = this.endMove.createDelegate(this);
33492         dd.startDrag = this.startMove.createDelegate(this);
33493         dd.onDrag = this.onDrag.createDelegate(this);
33494         dd.scroll = false;
33495         this.dd = dd;
33496     }
33497     if(this.modal){
33498         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33499         this.mask.enableDisplayMode("block");
33500         this.mask.hide();
33501         this.el.addClass("x-dlg-modal");
33502     }
33503     if(this.shadow){
33504         this.shadow = new Roo.Shadow({
33505             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33506             offset : this.shadowOffset
33507         });
33508     }else{
33509         this.shadowOffset = 0;
33510     }
33511     if(Roo.useShims && this.shim !== false){
33512         this.shim = this.el.createShim();
33513         this.shim.hide = this.hideAction;
33514         this.shim.hide();
33515     }else{
33516         this.shim = false;
33517     }
33518     if(this.autoTabs){
33519         this.initTabs();
33520     }
33521     if (this.buttons) { 
33522         var bts= this.buttons;
33523         this.buttons = [];
33524         Roo.each(bts, function(b) {
33525             this.addButton(b);
33526         }, this);
33527     }
33528     
33529     
33530     this.addEvents({
33531         /**
33532          * @event keydown
33533          * Fires when a key is pressed
33534          * @param {Roo.BasicDialog} this
33535          * @param {Roo.EventObject} e
33536          */
33537         "keydown" : true,
33538         /**
33539          * @event move
33540          * Fires when this dialog is moved by the user.
33541          * @param {Roo.BasicDialog} this
33542          * @param {Number} x The new page X
33543          * @param {Number} y The new page Y
33544          */
33545         "move" : true,
33546         /**
33547          * @event resize
33548          * Fires when this dialog is resized by the user.
33549          * @param {Roo.BasicDialog} this
33550          * @param {Number} width The new width
33551          * @param {Number} height The new height
33552          */
33553         "resize" : true,
33554         /**
33555          * @event beforehide
33556          * Fires before this dialog is hidden.
33557          * @param {Roo.BasicDialog} this
33558          */
33559         "beforehide" : true,
33560         /**
33561          * @event hide
33562          * Fires when this dialog is hidden.
33563          * @param {Roo.BasicDialog} this
33564          */
33565         "hide" : true,
33566         /**
33567          * @event beforeshow
33568          * Fires before this dialog is shown.
33569          * @param {Roo.BasicDialog} this
33570          */
33571         "beforeshow" : true,
33572         /**
33573          * @event show
33574          * Fires when this dialog is shown.
33575          * @param {Roo.BasicDialog} this
33576          */
33577         "show" : true
33578     });
33579     el.on("keydown", this.onKeyDown, this);
33580     el.on("mousedown", this.toFront, this);
33581     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33582     this.el.hide();
33583     Roo.DialogManager.register(this);
33584     Roo.BasicDialog.superclass.constructor.call(this);
33585 };
33586
33587 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33588     shadowOffset: Roo.isIE ? 6 : 5,
33589     minHeight: 80,
33590     minWidth: 200,
33591     minButtonWidth: 75,
33592     defaultButton: null,
33593     buttonAlign: "right",
33594     tabTag: 'div',
33595     firstShow: true,
33596
33597     /**
33598      * Sets the dialog title text
33599      * @param {String} text The title text to display
33600      * @return {Roo.BasicDialog} this
33601      */
33602     setTitle : function(text){
33603         this.header.update(text);
33604         return this;
33605     },
33606
33607     // private
33608     closeClick : function(){
33609         this.hide();
33610     },
33611
33612     // private
33613     collapseClick : function(){
33614         this[this.collapsed ? "expand" : "collapse"]();
33615     },
33616
33617     /**
33618      * Collapses the dialog to its minimized state (only the title bar is visible).
33619      * Equivalent to the user clicking the collapse dialog button.
33620      */
33621     collapse : function(){
33622         if(!this.collapsed){
33623             this.collapsed = true;
33624             this.el.addClass("x-dlg-collapsed");
33625             this.restoreHeight = this.el.getHeight();
33626             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33627         }
33628     },
33629
33630     /**
33631      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33632      * clicking the expand dialog button.
33633      */
33634     expand : function(){
33635         if(this.collapsed){
33636             this.collapsed = false;
33637             this.el.removeClass("x-dlg-collapsed");
33638             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33639         }
33640     },
33641
33642     /**
33643      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33644      * @return {Roo.TabPanel} The tabs component
33645      */
33646     initTabs : function(){
33647         var tabs = this.getTabs();
33648         while(tabs.getTab(0)){
33649             tabs.removeTab(0);
33650         }
33651         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33652             var dom = el.dom;
33653             tabs.addTab(Roo.id(dom), dom.title);
33654             dom.title = "";
33655         });
33656         tabs.activate(0);
33657         return tabs;
33658     },
33659
33660     // private
33661     beforeResize : function(){
33662         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33663     },
33664
33665     // private
33666     onResize : function(){
33667         this.refreshSize();
33668         this.syncBodyHeight();
33669         this.adjustAssets();
33670         this.focus();
33671         this.fireEvent("resize", this, this.size.width, this.size.height);
33672     },
33673
33674     // private
33675     onKeyDown : function(e){
33676         if(this.isVisible()){
33677             this.fireEvent("keydown", this, e);
33678         }
33679     },
33680
33681     /**
33682      * Resizes the dialog.
33683      * @param {Number} width
33684      * @param {Number} height
33685      * @return {Roo.BasicDialog} this
33686      */
33687     resizeTo : function(width, height){
33688         this.el.setSize(width, height);
33689         this.size = {width: width, height: height};
33690         this.syncBodyHeight();
33691         if(this.fixedcenter){
33692             this.center();
33693         }
33694         if(this.isVisible()){
33695             this.constrainXY();
33696             this.adjustAssets();
33697         }
33698         this.fireEvent("resize", this, width, height);
33699         return this;
33700     },
33701
33702
33703     /**
33704      * Resizes the dialog to fit the specified content size.
33705      * @param {Number} width
33706      * @param {Number} height
33707      * @return {Roo.BasicDialog} this
33708      */
33709     setContentSize : function(w, h){
33710         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33711         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33712         //if(!this.el.isBorderBox()){
33713             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33714             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33715         //}
33716         if(this.tabs){
33717             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33718             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33719         }
33720         this.resizeTo(w, h);
33721         return this;
33722     },
33723
33724     /**
33725      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33726      * executed in response to a particular key being pressed while the dialog is active.
33727      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33728      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33729      * @param {Function} fn The function to call
33730      * @param {Object} scope (optional) The scope of the function
33731      * @return {Roo.BasicDialog} this
33732      */
33733     addKeyListener : function(key, fn, scope){
33734         var keyCode, shift, ctrl, alt;
33735         if(typeof key == "object" && !(key instanceof Array)){
33736             keyCode = key["key"];
33737             shift = key["shift"];
33738             ctrl = key["ctrl"];
33739             alt = key["alt"];
33740         }else{
33741             keyCode = key;
33742         }
33743         var handler = function(dlg, e){
33744             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33745                 var k = e.getKey();
33746                 if(keyCode instanceof Array){
33747                     for(var i = 0, len = keyCode.length; i < len; i++){
33748                         if(keyCode[i] == k){
33749                           fn.call(scope || window, dlg, k, e);
33750                           return;
33751                         }
33752                     }
33753                 }else{
33754                     if(k == keyCode){
33755                         fn.call(scope || window, dlg, k, e);
33756                     }
33757                 }
33758             }
33759         };
33760         this.on("keydown", handler);
33761         return this;
33762     },
33763
33764     /**
33765      * Returns the TabPanel component (creates it if it doesn't exist).
33766      * Note: If you wish to simply check for the existence of tabs without creating them,
33767      * check for a null 'tabs' property.
33768      * @return {Roo.TabPanel} The tabs component
33769      */
33770     getTabs : function(){
33771         if(!this.tabs){
33772             this.el.addClass("x-dlg-auto-tabs");
33773             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33774             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33775         }
33776         return this.tabs;
33777     },
33778
33779     /**
33780      * Adds a button to the footer section of the dialog.
33781      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33782      * object or a valid Roo.DomHelper element config
33783      * @param {Function} handler The function called when the button is clicked
33784      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33785      * @return {Roo.Button} The new button
33786      */
33787     addButton : function(config, handler, scope){
33788         var dh = Roo.DomHelper;
33789         if(!this.footer){
33790             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33791         }
33792         if(!this.btnContainer){
33793             var tb = this.footer.createChild({
33794
33795                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33796                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33797             }, null, true);
33798             this.btnContainer = tb.firstChild.firstChild.firstChild;
33799         }
33800         var bconfig = {
33801             handler: handler,
33802             scope: scope,
33803             minWidth: this.minButtonWidth,
33804             hideParent:true
33805         };
33806         if(typeof config == "string"){
33807             bconfig.text = config;
33808         }else{
33809             if(config.tag){
33810                 bconfig.dhconfig = config;
33811             }else{
33812                 Roo.apply(bconfig, config);
33813             }
33814         }
33815         var fc = false;
33816         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33817             bconfig.position = Math.max(0, bconfig.position);
33818             fc = this.btnContainer.childNodes[bconfig.position];
33819         }
33820          
33821         var btn = new Roo.Button(
33822             fc ? 
33823                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33824                 : this.btnContainer.appendChild(document.createElement("td")),
33825             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33826             bconfig
33827         );
33828         this.syncBodyHeight();
33829         if(!this.buttons){
33830             /**
33831              * Array of all the buttons that have been added to this dialog via addButton
33832              * @type Array
33833              */
33834             this.buttons = [];
33835         }
33836         this.buttons.push(btn);
33837         return btn;
33838     },
33839
33840     /**
33841      * Sets the default button to be focused when the dialog is displayed.
33842      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33843      * @return {Roo.BasicDialog} this
33844      */
33845     setDefaultButton : function(btn){
33846         this.defaultButton = btn;
33847         return this;
33848     },
33849
33850     // private
33851     getHeaderFooterHeight : function(safe){
33852         var height = 0;
33853         if(this.header){
33854            height += this.header.getHeight();
33855         }
33856         if(this.footer){
33857            var fm = this.footer.getMargins();
33858             height += (this.footer.getHeight()+fm.top+fm.bottom);
33859         }
33860         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33861         height += this.centerBg.getPadding("tb");
33862         return height;
33863     },
33864
33865     // private
33866     syncBodyHeight : function()
33867     {
33868         var bd = this.body, // the text
33869             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33870             bw = this.bwrap;
33871         var height = this.size.height - this.getHeaderFooterHeight(false);
33872         bd.setHeight(height-bd.getMargins("tb"));
33873         var hh = this.header.getHeight();
33874         var h = this.size.height-hh;
33875         cb.setHeight(h);
33876         
33877         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33878         bw.setHeight(h-cb.getPadding("tb"));
33879         
33880         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33881         bd.setWidth(bw.getWidth(true));
33882         if(this.tabs){
33883             this.tabs.syncHeight();
33884             if(Roo.isIE){
33885                 this.tabs.el.repaint();
33886             }
33887         }
33888     },
33889
33890     /**
33891      * Restores the previous state of the dialog if Roo.state is configured.
33892      * @return {Roo.BasicDialog} this
33893      */
33894     restoreState : function(){
33895         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33896         if(box && box.width){
33897             this.xy = [box.x, box.y];
33898             this.resizeTo(box.width, box.height);
33899         }
33900         return this;
33901     },
33902
33903     // private
33904     beforeShow : function(){
33905         this.expand();
33906         if(this.fixedcenter){
33907             this.xy = this.el.getCenterXY(true);
33908         }
33909         if(this.modal){
33910             Roo.get(document.body).addClass("x-body-masked");
33911             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33912             this.mask.show();
33913         }
33914         this.constrainXY();
33915     },
33916
33917     // private
33918     animShow : function(){
33919         var b = Roo.get(this.animateTarget).getBox();
33920         this.proxy.setSize(b.width, b.height);
33921         this.proxy.setLocation(b.x, b.y);
33922         this.proxy.show();
33923         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33924                     true, .35, this.showEl.createDelegate(this));
33925     },
33926
33927     /**
33928      * Shows the dialog.
33929      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33930      * @return {Roo.BasicDialog} this
33931      */
33932     show : function(animateTarget){
33933         if (this.fireEvent("beforeshow", this) === false){
33934             return;
33935         }
33936         if(this.syncHeightBeforeShow){
33937             this.syncBodyHeight();
33938         }else if(this.firstShow){
33939             this.firstShow = false;
33940             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33941         }
33942         this.animateTarget = animateTarget || this.animateTarget;
33943         if(!this.el.isVisible()){
33944             this.beforeShow();
33945             if(this.animateTarget && Roo.get(this.animateTarget)){
33946                 this.animShow();
33947             }else{
33948                 this.showEl();
33949             }
33950         }
33951         return this;
33952     },
33953
33954     // private
33955     showEl : function(){
33956         this.proxy.hide();
33957         this.el.setXY(this.xy);
33958         this.el.show();
33959         this.adjustAssets(true);
33960         this.toFront();
33961         this.focus();
33962         // IE peekaboo bug - fix found by Dave Fenwick
33963         if(Roo.isIE){
33964             this.el.repaint();
33965         }
33966         this.fireEvent("show", this);
33967     },
33968
33969     /**
33970      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33971      * dialog itself will receive focus.
33972      */
33973     focus : function(){
33974         if(this.defaultButton){
33975             this.defaultButton.focus();
33976         }else{
33977             this.focusEl.focus();
33978         }
33979     },
33980
33981     // private
33982     constrainXY : function(){
33983         if(this.constraintoviewport !== false){
33984             if(!this.viewSize){
33985                 if(this.container){
33986                     var s = this.container.getSize();
33987                     this.viewSize = [s.width, s.height];
33988                 }else{
33989                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33990                 }
33991             }
33992             var s = Roo.get(this.container||document).getScroll();
33993
33994             var x = this.xy[0], y = this.xy[1];
33995             var w = this.size.width, h = this.size.height;
33996             var vw = this.viewSize[0], vh = this.viewSize[1];
33997             // only move it if it needs it
33998             var moved = false;
33999             // first validate right/bottom
34000             if(x + w > vw+s.left){
34001                 x = vw - w;
34002                 moved = true;
34003             }
34004             if(y + h > vh+s.top){
34005                 y = vh - h;
34006                 moved = true;
34007             }
34008             // then make sure top/left isn't negative
34009             if(x < s.left){
34010                 x = s.left;
34011                 moved = true;
34012             }
34013             if(y < s.top){
34014                 y = s.top;
34015                 moved = true;
34016             }
34017             if(moved){
34018                 // cache xy
34019                 this.xy = [x, y];
34020                 if(this.isVisible()){
34021                     this.el.setLocation(x, y);
34022                     this.adjustAssets();
34023                 }
34024             }
34025         }
34026     },
34027
34028     // private
34029     onDrag : function(){
34030         if(!this.proxyDrag){
34031             this.xy = this.el.getXY();
34032             this.adjustAssets();
34033         }
34034     },
34035
34036     // private
34037     adjustAssets : function(doShow){
34038         var x = this.xy[0], y = this.xy[1];
34039         var w = this.size.width, h = this.size.height;
34040         if(doShow === true){
34041             if(this.shadow){
34042                 this.shadow.show(this.el);
34043             }
34044             if(this.shim){
34045                 this.shim.show();
34046             }
34047         }
34048         if(this.shadow && this.shadow.isVisible()){
34049             this.shadow.show(this.el);
34050         }
34051         if(this.shim && this.shim.isVisible()){
34052             this.shim.setBounds(x, y, w, h);
34053         }
34054     },
34055
34056     // private
34057     adjustViewport : function(w, h){
34058         if(!w || !h){
34059             w = Roo.lib.Dom.getViewWidth();
34060             h = Roo.lib.Dom.getViewHeight();
34061         }
34062         // cache the size
34063         this.viewSize = [w, h];
34064         if(this.modal && this.mask.isVisible()){
34065             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34066             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34067         }
34068         if(this.isVisible()){
34069             this.constrainXY();
34070         }
34071     },
34072
34073     /**
34074      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34075      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34076      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34077      */
34078     destroy : function(removeEl){
34079         if(this.isVisible()){
34080             this.animateTarget = null;
34081             this.hide();
34082         }
34083         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34084         if(this.tabs){
34085             this.tabs.destroy(removeEl);
34086         }
34087         Roo.destroy(
34088              this.shim,
34089              this.proxy,
34090              this.resizer,
34091              this.close,
34092              this.mask
34093         );
34094         if(this.dd){
34095             this.dd.unreg();
34096         }
34097         if(this.buttons){
34098            for(var i = 0, len = this.buttons.length; i < len; i++){
34099                this.buttons[i].destroy();
34100            }
34101         }
34102         this.el.removeAllListeners();
34103         if(removeEl === true){
34104             this.el.update("");
34105             this.el.remove();
34106         }
34107         Roo.DialogManager.unregister(this);
34108     },
34109
34110     // private
34111     startMove : function(){
34112         if(this.proxyDrag){
34113             this.proxy.show();
34114         }
34115         if(this.constraintoviewport !== false){
34116             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34117         }
34118     },
34119
34120     // private
34121     endMove : function(){
34122         if(!this.proxyDrag){
34123             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34124         }else{
34125             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34126             this.proxy.hide();
34127         }
34128         this.refreshSize();
34129         this.adjustAssets();
34130         this.focus();
34131         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34132     },
34133
34134     /**
34135      * Brings this dialog to the front of any other visible dialogs
34136      * @return {Roo.BasicDialog} this
34137      */
34138     toFront : function(){
34139         Roo.DialogManager.bringToFront(this);
34140         return this;
34141     },
34142
34143     /**
34144      * Sends this dialog to the back (under) of any other visible dialogs
34145      * @return {Roo.BasicDialog} this
34146      */
34147     toBack : function(){
34148         Roo.DialogManager.sendToBack(this);
34149         return this;
34150     },
34151
34152     /**
34153      * Centers this dialog in the viewport
34154      * @return {Roo.BasicDialog} this
34155      */
34156     center : function(){
34157         var xy = this.el.getCenterXY(true);
34158         this.moveTo(xy[0], xy[1]);
34159         return this;
34160     },
34161
34162     /**
34163      * Moves the dialog's top-left corner to the specified point
34164      * @param {Number} x
34165      * @param {Number} y
34166      * @return {Roo.BasicDialog} this
34167      */
34168     moveTo : function(x, y){
34169         this.xy = [x,y];
34170         if(this.isVisible()){
34171             this.el.setXY(this.xy);
34172             this.adjustAssets();
34173         }
34174         return this;
34175     },
34176
34177     /**
34178      * Aligns the dialog to the specified element
34179      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34180      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34181      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34182      * @return {Roo.BasicDialog} this
34183      */
34184     alignTo : function(element, position, offsets){
34185         this.xy = this.el.getAlignToXY(element, position, offsets);
34186         if(this.isVisible()){
34187             this.el.setXY(this.xy);
34188             this.adjustAssets();
34189         }
34190         return this;
34191     },
34192
34193     /**
34194      * Anchors an element to another element and realigns it when the window is resized.
34195      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34196      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34197      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34198      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34199      * is a number, it is used as the buffer delay (defaults to 50ms).
34200      * @return {Roo.BasicDialog} this
34201      */
34202     anchorTo : function(el, alignment, offsets, monitorScroll){
34203         var action = function(){
34204             this.alignTo(el, alignment, offsets);
34205         };
34206         Roo.EventManager.onWindowResize(action, this);
34207         var tm = typeof monitorScroll;
34208         if(tm != 'undefined'){
34209             Roo.EventManager.on(window, 'scroll', action, this,
34210                 {buffer: tm == 'number' ? monitorScroll : 50});
34211         }
34212         action.call(this);
34213         return this;
34214     },
34215
34216     /**
34217      * Returns true if the dialog is visible
34218      * @return {Boolean}
34219      */
34220     isVisible : function(){
34221         return this.el.isVisible();
34222     },
34223
34224     // private
34225     animHide : function(callback){
34226         var b = Roo.get(this.animateTarget).getBox();
34227         this.proxy.show();
34228         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34229         this.el.hide();
34230         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34231                     this.hideEl.createDelegate(this, [callback]));
34232     },
34233
34234     /**
34235      * Hides the dialog.
34236      * @param {Function} callback (optional) Function to call when the dialog is hidden
34237      * @return {Roo.BasicDialog} this
34238      */
34239     hide : function(callback){
34240         if (this.fireEvent("beforehide", this) === false){
34241             return;
34242         }
34243         if(this.shadow){
34244             this.shadow.hide();
34245         }
34246         if(this.shim) {
34247           this.shim.hide();
34248         }
34249         // sometimes animateTarget seems to get set.. causing problems...
34250         // this just double checks..
34251         if(this.animateTarget && Roo.get(this.animateTarget)) {
34252            this.animHide(callback);
34253         }else{
34254             this.el.hide();
34255             this.hideEl(callback);
34256         }
34257         return this;
34258     },
34259
34260     // private
34261     hideEl : function(callback){
34262         this.proxy.hide();
34263         if(this.modal){
34264             this.mask.hide();
34265             Roo.get(document.body).removeClass("x-body-masked");
34266         }
34267         this.fireEvent("hide", this);
34268         if(typeof callback == "function"){
34269             callback();
34270         }
34271     },
34272
34273     // private
34274     hideAction : function(){
34275         this.setLeft("-10000px");
34276         this.setTop("-10000px");
34277         this.setStyle("visibility", "hidden");
34278     },
34279
34280     // private
34281     refreshSize : function(){
34282         this.size = this.el.getSize();
34283         this.xy = this.el.getXY();
34284         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34285     },
34286
34287     // private
34288     // z-index is managed by the DialogManager and may be overwritten at any time
34289     setZIndex : function(index){
34290         if(this.modal){
34291             this.mask.setStyle("z-index", index);
34292         }
34293         if(this.shim){
34294             this.shim.setStyle("z-index", ++index);
34295         }
34296         if(this.shadow){
34297             this.shadow.setZIndex(++index);
34298         }
34299         this.el.setStyle("z-index", ++index);
34300         if(this.proxy){
34301             this.proxy.setStyle("z-index", ++index);
34302         }
34303         if(this.resizer){
34304             this.resizer.proxy.setStyle("z-index", ++index);
34305         }
34306
34307         this.lastZIndex = index;
34308     },
34309
34310     /**
34311      * Returns the element for this dialog
34312      * @return {Roo.Element} The underlying dialog Element
34313      */
34314     getEl : function(){
34315         return this.el;
34316     }
34317 });
34318
34319 /**
34320  * @class Roo.DialogManager
34321  * Provides global access to BasicDialogs that have been created and
34322  * support for z-indexing (layering) multiple open dialogs.
34323  */
34324 Roo.DialogManager = function(){
34325     var list = {};
34326     var accessList = [];
34327     var front = null;
34328
34329     // private
34330     var sortDialogs = function(d1, d2){
34331         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34332     };
34333
34334     // private
34335     var orderDialogs = function(){
34336         accessList.sort(sortDialogs);
34337         var seed = Roo.DialogManager.zseed;
34338         for(var i = 0, len = accessList.length; i < len; i++){
34339             var dlg = accessList[i];
34340             if(dlg){
34341                 dlg.setZIndex(seed + (i*10));
34342             }
34343         }
34344     };
34345
34346     return {
34347         /**
34348          * The starting z-index for BasicDialogs (defaults to 9000)
34349          * @type Number The z-index value
34350          */
34351         zseed : 9000,
34352
34353         // private
34354         register : function(dlg){
34355             list[dlg.id] = dlg;
34356             accessList.push(dlg);
34357         },
34358
34359         // private
34360         unregister : function(dlg){
34361             delete list[dlg.id];
34362             var i=0;
34363             var len=0;
34364             if(!accessList.indexOf){
34365                 for(  i = 0, len = accessList.length; i < len; i++){
34366                     if(accessList[i] == dlg){
34367                         accessList.splice(i, 1);
34368                         return;
34369                     }
34370                 }
34371             }else{
34372                  i = accessList.indexOf(dlg);
34373                 if(i != -1){
34374                     accessList.splice(i, 1);
34375                 }
34376             }
34377         },
34378
34379         /**
34380          * Gets a registered dialog by id
34381          * @param {String/Object} id The id of the dialog or a dialog
34382          * @return {Roo.BasicDialog} this
34383          */
34384         get : function(id){
34385             return typeof id == "object" ? id : list[id];
34386         },
34387
34388         /**
34389          * Brings the specified dialog to the front
34390          * @param {String/Object} dlg The id of the dialog or a dialog
34391          * @return {Roo.BasicDialog} this
34392          */
34393         bringToFront : function(dlg){
34394             dlg = this.get(dlg);
34395             if(dlg != front){
34396                 front = dlg;
34397                 dlg._lastAccess = new Date().getTime();
34398                 orderDialogs();
34399             }
34400             return dlg;
34401         },
34402
34403         /**
34404          * Sends the specified dialog to the back
34405          * @param {String/Object} dlg The id of the dialog or a dialog
34406          * @return {Roo.BasicDialog} this
34407          */
34408         sendToBack : function(dlg){
34409             dlg = this.get(dlg);
34410             dlg._lastAccess = -(new Date().getTime());
34411             orderDialogs();
34412             return dlg;
34413         },
34414
34415         /**
34416          * Hides all dialogs
34417          */
34418         hideAll : function(){
34419             for(var id in list){
34420                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34421                     list[id].hide();
34422                 }
34423             }
34424         }
34425     };
34426 }();
34427
34428 /**
34429  * @class Roo.LayoutDialog
34430  * @extends Roo.BasicDialog
34431  * @children Roo.ContentPanel
34432  * @parent builder none
34433  * Dialog which provides adjustments for working with a layout in a Dialog.
34434  * Add your necessary layout config options to the dialog's config.<br>
34435  * Example usage (including a nested layout):
34436  * <pre><code>
34437 if(!dialog){
34438     dialog = new Roo.LayoutDialog("download-dlg", {
34439         modal: true,
34440         width:600,
34441         height:450,
34442         shadow:true,
34443         minWidth:500,
34444         minHeight:350,
34445         autoTabs:true,
34446         proxyDrag:true,
34447         // layout config merges with the dialog config
34448         center:{
34449             tabPosition: "top",
34450             alwaysShowTabs: true
34451         }
34452     });
34453     dialog.addKeyListener(27, dialog.hide, dialog);
34454     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34455     dialog.addButton("Build It!", this.getDownload, this);
34456
34457     // we can even add nested layouts
34458     var innerLayout = new Roo.BorderLayout("dl-inner", {
34459         east: {
34460             initialSize: 200,
34461             autoScroll:true,
34462             split:true
34463         },
34464         center: {
34465             autoScroll:true
34466         }
34467     });
34468     innerLayout.beginUpdate();
34469     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34470     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34471     innerLayout.endUpdate(true);
34472
34473     var layout = dialog.getLayout();
34474     layout.beginUpdate();
34475     layout.add("center", new Roo.ContentPanel("standard-panel",
34476                         {title: "Download the Source", fitToFrame:true}));
34477     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34478                {title: "Build your own roo.js"}));
34479     layout.getRegion("center").showPanel(sp);
34480     layout.endUpdate();
34481 }
34482 </code></pre>
34483     * @constructor
34484     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34485     * @param {Object} config configuration options
34486   */
34487 Roo.LayoutDialog = function(el, cfg){
34488     
34489     var config=  cfg;
34490     if (typeof(cfg) == 'undefined') {
34491         config = Roo.apply({}, el);
34492         // not sure why we use documentElement here.. - it should always be body.
34493         // IE7 borks horribly if we use documentElement.
34494         // webkit also does not like documentElement - it creates a body element...
34495         el = Roo.get( document.body || document.documentElement ).createChild();
34496         //config.autoCreate = true;
34497     }
34498     
34499     
34500     config.autoTabs = false;
34501     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34502     this.body.setStyle({overflow:"hidden", position:"relative"});
34503     this.layout = new Roo.BorderLayout(this.body.dom, config);
34504     this.layout.monitorWindowResize = false;
34505     this.el.addClass("x-dlg-auto-layout");
34506     // fix case when center region overwrites center function
34507     this.center = Roo.BasicDialog.prototype.center;
34508     this.on("show", this.layout.layout, this.layout, true);
34509     if (config.items) {
34510         var xitems = config.items;
34511         delete config.items;
34512         Roo.each(xitems, this.addxtype, this);
34513     }
34514     
34515     
34516 };
34517 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34518     
34519     
34520     /**
34521      * @cfg {Roo.LayoutRegion} east  
34522      */
34523     /**
34524      * @cfg {Roo.LayoutRegion} west
34525      */
34526     /**
34527      * @cfg {Roo.LayoutRegion} south
34528      */
34529     /**
34530      * @cfg {Roo.LayoutRegion} north
34531      */
34532     /**
34533      * @cfg {Roo.LayoutRegion} center
34534      */
34535     /**
34536      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34537      */
34538     
34539     
34540     /**
34541      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34542      * @deprecated
34543      */
34544     endUpdate : function(){
34545         this.layout.endUpdate();
34546     },
34547
34548     /**
34549      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34550      *  @deprecated
34551      */
34552     beginUpdate : function(){
34553         this.layout.beginUpdate();
34554     },
34555
34556     /**
34557      * Get the BorderLayout for this dialog
34558      * @return {Roo.BorderLayout}
34559      */
34560     getLayout : function(){
34561         return this.layout;
34562     },
34563
34564     showEl : function(){
34565         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34566         if(Roo.isIE7){
34567             this.layout.layout();
34568         }
34569     },
34570
34571     // private
34572     // Use the syncHeightBeforeShow config option to control this automatically
34573     syncBodyHeight : function(){
34574         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34575         if(this.layout){this.layout.layout();}
34576     },
34577     
34578       /**
34579      * Add an xtype element (actually adds to the layout.)
34580      * @return {Object} xdata xtype object data.
34581      */
34582     
34583     addxtype : function(c) {
34584         return this.layout.addxtype(c);
34585     }
34586 });/*
34587  * Based on:
34588  * Ext JS Library 1.1.1
34589  * Copyright(c) 2006-2007, Ext JS, LLC.
34590  *
34591  * Originally Released Under LGPL - original licence link has changed is not relivant.
34592  *
34593  * Fork - LGPL
34594  * <script type="text/javascript">
34595  */
34596  
34597 /**
34598  * @class Roo.MessageBox
34599  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34600  * Example usage:
34601  *<pre><code>
34602 // Basic alert:
34603 Roo.Msg.alert('Status', 'Changes saved successfully.');
34604
34605 // Prompt for user data:
34606 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34607     if (btn == 'ok'){
34608         // process text value...
34609     }
34610 });
34611
34612 // Show a dialog using config options:
34613 Roo.Msg.show({
34614    title:'Save Changes?',
34615    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34616    buttons: Roo.Msg.YESNOCANCEL,
34617    fn: processResult,
34618    animEl: 'elId'
34619 });
34620 </code></pre>
34621  * @static
34622  */
34623 Roo.MessageBox = function(){
34624     var dlg, opt, mask, waitTimer;
34625     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34626     var buttons, activeTextEl, bwidth;
34627
34628     // private
34629     var handleButton = function(button){
34630         dlg.hide();
34631         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34632     };
34633
34634     // private
34635     var handleHide = function(){
34636         if(opt && opt.cls){
34637             dlg.el.removeClass(opt.cls);
34638         }
34639         if(waitTimer){
34640             Roo.TaskMgr.stop(waitTimer);
34641             waitTimer = null;
34642         }
34643     };
34644
34645     // private
34646     var updateButtons = function(b){
34647         var width = 0;
34648         if(!b){
34649             buttons["ok"].hide();
34650             buttons["cancel"].hide();
34651             buttons["yes"].hide();
34652             buttons["no"].hide();
34653             dlg.footer.dom.style.display = 'none';
34654             return width;
34655         }
34656         dlg.footer.dom.style.display = '';
34657         for(var k in buttons){
34658             if(typeof buttons[k] != "function"){
34659                 if(b[k]){
34660                     buttons[k].show();
34661                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34662                     width += buttons[k].el.getWidth()+15;
34663                 }else{
34664                     buttons[k].hide();
34665                 }
34666             }
34667         }
34668         return width;
34669     };
34670
34671     // private
34672     var handleEsc = function(d, k, e){
34673         if(opt && opt.closable !== false){
34674             dlg.hide();
34675         }
34676         if(e){
34677             e.stopEvent();
34678         }
34679     };
34680
34681     return {
34682         /**
34683          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34684          * @return {Roo.BasicDialog} The BasicDialog element
34685          */
34686         getDialog : function(){
34687            if(!dlg){
34688                 dlg = new Roo.BasicDialog("x-msg-box", {
34689                     autoCreate : true,
34690                     shadow: true,
34691                     draggable: true,
34692                     resizable:false,
34693                     constraintoviewport:false,
34694                     fixedcenter:true,
34695                     collapsible : false,
34696                     shim:true,
34697                     modal: true,
34698                     width:400, height:100,
34699                     buttonAlign:"center",
34700                     closeClick : function(){
34701                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34702                             handleButton("no");
34703                         }else{
34704                             handleButton("cancel");
34705                         }
34706                     }
34707                 });
34708                 dlg.on("hide", handleHide);
34709                 mask = dlg.mask;
34710                 dlg.addKeyListener(27, handleEsc);
34711                 buttons = {};
34712                 var bt = this.buttonText;
34713                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34714                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34715                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34716                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34717                 bodyEl = dlg.body.createChild({
34718
34719                     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>'
34720                 });
34721                 msgEl = bodyEl.dom.firstChild;
34722                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34723                 textboxEl.enableDisplayMode();
34724                 textboxEl.addKeyListener([10,13], function(){
34725                     if(dlg.isVisible() && opt && opt.buttons){
34726                         if(opt.buttons.ok){
34727                             handleButton("ok");
34728                         }else if(opt.buttons.yes){
34729                             handleButton("yes");
34730                         }
34731                     }
34732                 });
34733                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34734                 textareaEl.enableDisplayMode();
34735                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34736                 progressEl.enableDisplayMode();
34737                 var pf = progressEl.dom.firstChild;
34738                 if (pf) {
34739                     pp = Roo.get(pf.firstChild);
34740                     pp.setHeight(pf.offsetHeight);
34741                 }
34742                 
34743             }
34744             return dlg;
34745         },
34746
34747         /**
34748          * Updates the message box body text
34749          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34750          * the XHTML-compliant non-breaking space character '&amp;#160;')
34751          * @return {Roo.MessageBox} This message box
34752          */
34753         updateText : function(text){
34754             if(!dlg.isVisible() && !opt.width){
34755                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34756             }
34757             msgEl.innerHTML = text || '&#160;';
34758       
34759             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34760             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34761             var w = Math.max(
34762                     Math.min(opt.width || cw , this.maxWidth), 
34763                     Math.max(opt.minWidth || this.minWidth, bwidth)
34764             );
34765             if(opt.prompt){
34766                 activeTextEl.setWidth(w);
34767             }
34768             if(dlg.isVisible()){
34769                 dlg.fixedcenter = false;
34770             }
34771             // to big, make it scroll. = But as usual stupid IE does not support
34772             // !important..
34773             
34774             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34775                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34776                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34777             } else {
34778                 bodyEl.dom.style.height = '';
34779                 bodyEl.dom.style.overflowY = '';
34780             }
34781             if (cw > w) {
34782                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34783             } else {
34784                 bodyEl.dom.style.overflowX = '';
34785             }
34786             
34787             dlg.setContentSize(w, bodyEl.getHeight());
34788             if(dlg.isVisible()){
34789                 dlg.fixedcenter = true;
34790             }
34791             return this;
34792         },
34793
34794         /**
34795          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34796          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34797          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34798          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34799          * @return {Roo.MessageBox} This message box
34800          */
34801         updateProgress : function(value, text){
34802             if(text){
34803                 this.updateText(text);
34804             }
34805             if (pp) { // weird bug on my firefox - for some reason this is not defined
34806                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34807             }
34808             return this;
34809         },        
34810
34811         /**
34812          * Returns true if the message box is currently displayed
34813          * @return {Boolean} True if the message box is visible, else false
34814          */
34815         isVisible : function(){
34816             return dlg && dlg.isVisible();  
34817         },
34818
34819         /**
34820          * Hides the message box if it is displayed
34821          */
34822         hide : function(){
34823             if(this.isVisible()){
34824                 dlg.hide();
34825             }  
34826         },
34827
34828         /**
34829          * Displays a new message box, or reinitializes an existing message box, based on the config options
34830          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34831          * The following config object properties are supported:
34832          * <pre>
34833 Property    Type             Description
34834 ----------  ---------------  ------------------------------------------------------------------------------------
34835 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34836                                    closes (defaults to undefined)
34837 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34838                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34839 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34840                                    progress and wait dialogs will ignore this property and always hide the
34841                                    close button as they can only be closed programmatically.
34842 cls               String           A custom CSS class to apply to the message box element
34843 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34844                                    displayed (defaults to 75)
34845 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34846                                    function will be btn (the name of the button that was clicked, if applicable,
34847                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34848                                    Progress and wait dialogs will ignore this option since they do not respond to
34849                                    user actions and can only be closed programmatically, so any required function
34850                                    should be called by the same code after it closes the dialog.
34851 icon              String           A CSS class that provides a background image to be used as an icon for
34852                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34853 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34854 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34855 modal             Boolean          False to allow user interaction with the page while the message box is
34856                                    displayed (defaults to true)
34857 msg               String           A string that will replace the existing message box body text (defaults
34858                                    to the XHTML-compliant non-breaking space character '&#160;')
34859 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34860 progress          Boolean          True to display a progress bar (defaults to false)
34861 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34862 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34863 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34864 title             String           The title text
34865 value             String           The string value to set into the active textbox element if displayed
34866 wait              Boolean          True to display a progress bar (defaults to false)
34867 width             Number           The width of the dialog in pixels
34868 </pre>
34869          *
34870          * Example usage:
34871          * <pre><code>
34872 Roo.Msg.show({
34873    title: 'Address',
34874    msg: 'Please enter your address:',
34875    width: 300,
34876    buttons: Roo.MessageBox.OKCANCEL,
34877    multiline: true,
34878    fn: saveAddress,
34879    animEl: 'addAddressBtn'
34880 });
34881 </code></pre>
34882          * @param {Object} config Configuration options
34883          * @return {Roo.MessageBox} This message box
34884          */
34885         show : function(options)
34886         {
34887             
34888             // this causes nightmares if you show one dialog after another
34889             // especially on callbacks..
34890              
34891             if(this.isVisible()){
34892                 
34893                 this.hide();
34894                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34895                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34896                 Roo.log("New Dialog Message:" +  options.msg )
34897                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34898                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34899                 
34900             }
34901             var d = this.getDialog();
34902             opt = options;
34903             d.setTitle(opt.title || "&#160;");
34904             d.close.setDisplayed(opt.closable !== false);
34905             activeTextEl = textboxEl;
34906             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34907             if(opt.prompt){
34908                 if(opt.multiline){
34909                     textboxEl.hide();
34910                     textareaEl.show();
34911                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34912                         opt.multiline : this.defaultTextHeight);
34913                     activeTextEl = textareaEl;
34914                 }else{
34915                     textboxEl.show();
34916                     textareaEl.hide();
34917                 }
34918             }else{
34919                 textboxEl.hide();
34920                 textareaEl.hide();
34921             }
34922             progressEl.setDisplayed(opt.progress === true);
34923             this.updateProgress(0);
34924             activeTextEl.dom.value = opt.value || "";
34925             if(opt.prompt){
34926                 dlg.setDefaultButton(activeTextEl);
34927             }else{
34928                 var bs = opt.buttons;
34929                 var db = null;
34930                 if(bs && bs.ok){
34931                     db = buttons["ok"];
34932                 }else if(bs && bs.yes){
34933                     db = buttons["yes"];
34934                 }
34935                 dlg.setDefaultButton(db);
34936             }
34937             bwidth = updateButtons(opt.buttons);
34938             this.updateText(opt.msg);
34939             if(opt.cls){
34940                 d.el.addClass(opt.cls);
34941             }
34942             d.proxyDrag = opt.proxyDrag === true;
34943             d.modal = opt.modal !== false;
34944             d.mask = opt.modal !== false ? mask : false;
34945             if(!d.isVisible()){
34946                 // force it to the end of the z-index stack so it gets a cursor in FF
34947                 document.body.appendChild(dlg.el.dom);
34948                 d.animateTarget = null;
34949                 d.show(options.animEl);
34950             }
34951             return this;
34952         },
34953
34954         /**
34955          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34956          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34957          * and closing the message box when the process is complete.
34958          * @param {String} title The title bar text
34959          * @param {String} msg The message box body text
34960          * @return {Roo.MessageBox} This message box
34961          */
34962         progress : function(title, msg){
34963             this.show({
34964                 title : title,
34965                 msg : msg,
34966                 buttons: false,
34967                 progress:true,
34968                 closable:false,
34969                 minWidth: this.minProgressWidth,
34970                 modal : true
34971             });
34972             return this;
34973         },
34974
34975         /**
34976          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34977          * If a callback function is passed it will be called after the user clicks the button, and the
34978          * id of the button that was clicked will be passed as the only parameter to the callback
34979          * (could also be the top-right close button).
34980          * @param {String} title The title bar text
34981          * @param {String} msg The message box body text
34982          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34983          * @param {Object} scope (optional) The scope of the callback function
34984          * @return {Roo.MessageBox} This message box
34985          */
34986         alert : function(title, msg, fn, scope){
34987             this.show({
34988                 title : title,
34989                 msg : msg,
34990                 buttons: this.OK,
34991                 fn: fn,
34992                 scope : scope,
34993                 modal : true
34994             });
34995             return this;
34996         },
34997
34998         /**
34999          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35000          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35001          * You are responsible for closing the message box when the process is complete.
35002          * @param {String} msg The message box body text
35003          * @param {String} title (optional) The title bar text
35004          * @return {Roo.MessageBox} This message box
35005          */
35006         wait : function(msg, title){
35007             this.show({
35008                 title : title,
35009                 msg : msg,
35010                 buttons: false,
35011                 closable:false,
35012                 progress:true,
35013                 modal:true,
35014                 width:300,
35015                 wait:true
35016             });
35017             waitTimer = Roo.TaskMgr.start({
35018                 run: function(i){
35019                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35020                 },
35021                 interval: 1000
35022             });
35023             return this;
35024         },
35025
35026         /**
35027          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35028          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35029          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35030          * @param {String} title The title bar text
35031          * @param {String} msg The message box body text
35032          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35033          * @param {Object} scope (optional) The scope of the callback function
35034          * @return {Roo.MessageBox} This message box
35035          */
35036         confirm : function(title, msg, fn, scope){
35037             this.show({
35038                 title : title,
35039                 msg : msg,
35040                 buttons: this.YESNO,
35041                 fn: fn,
35042                 scope : scope,
35043                 modal : true
35044             });
35045             return this;
35046         },
35047
35048         /**
35049          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35050          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35051          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35052          * (could also be the top-right close button) and the text that was entered will be passed as the two
35053          * parameters to the callback.
35054          * @param {String} title The title bar text
35055          * @param {String} msg The message box body text
35056          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35057          * @param {Object} scope (optional) The scope of the callback function
35058          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35059          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35060          * @return {Roo.MessageBox} This message box
35061          */
35062         prompt : function(title, msg, fn, scope, multiline){
35063             this.show({
35064                 title : title,
35065                 msg : msg,
35066                 buttons: this.OKCANCEL,
35067                 fn: fn,
35068                 minWidth:250,
35069                 scope : scope,
35070                 prompt:true,
35071                 multiline: multiline,
35072                 modal : true
35073             });
35074             return this;
35075         },
35076
35077         /**
35078          * Button config that displays a single OK button
35079          * @type Object
35080          */
35081         OK : {ok:true},
35082         /**
35083          * Button config that displays Yes and No buttons
35084          * @type Object
35085          */
35086         YESNO : {yes:true, no:true},
35087         /**
35088          * Button config that displays OK and Cancel buttons
35089          * @type Object
35090          */
35091         OKCANCEL : {ok:true, cancel:true},
35092         /**
35093          * Button config that displays Yes, No and Cancel buttons
35094          * @type Object
35095          */
35096         YESNOCANCEL : {yes:true, no:true, cancel:true},
35097
35098         /**
35099          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35100          * @type Number
35101          */
35102         defaultTextHeight : 75,
35103         /**
35104          * The maximum width in pixels of the message box (defaults to 600)
35105          * @type Number
35106          */
35107         maxWidth : 600,
35108         /**
35109          * The minimum width in pixels of the message box (defaults to 100)
35110          * @type Number
35111          */
35112         minWidth : 100,
35113         /**
35114          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35115          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35116          * @type Number
35117          */
35118         minProgressWidth : 250,
35119         /**
35120          * An object containing the default button text strings that can be overriden for localized language support.
35121          * Supported properties are: ok, cancel, yes and no.
35122          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35123          * @type Object
35124          */
35125         buttonText : {
35126             ok : "OK",
35127             cancel : "Cancel",
35128             yes : "Yes",
35129             no : "No"
35130         }
35131     };
35132 }();
35133
35134 /**
35135  * Shorthand for {@link Roo.MessageBox}
35136  */
35137 Roo.Msg = Roo.MessageBox;/*
35138  * Based on:
35139  * Ext JS Library 1.1.1
35140  * Copyright(c) 2006-2007, Ext JS, LLC.
35141  *
35142  * Originally Released Under LGPL - original licence link has changed is not relivant.
35143  *
35144  * Fork - LGPL
35145  * <script type="text/javascript">
35146  */
35147 /**
35148  * @class Roo.QuickTips
35149  * Provides attractive and customizable tooltips for any element.
35150  * @static
35151  */
35152 Roo.QuickTips = function(){
35153     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35154     var ce, bd, xy, dd;
35155     var visible = false, disabled = true, inited = false;
35156     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35157     
35158     var onOver = function(e){
35159         if(disabled){
35160             return;
35161         }
35162         var t = e.getTarget();
35163         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35164             return;
35165         }
35166         if(ce && t == ce.el){
35167             clearTimeout(hideProc);
35168             return;
35169         }
35170         if(t && tagEls[t.id]){
35171             tagEls[t.id].el = t;
35172             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35173             return;
35174         }
35175         var ttp, et = Roo.fly(t);
35176         var ns = cfg.namespace;
35177         if(tm.interceptTitles && t.title){
35178             ttp = t.title;
35179             t.qtip = ttp;
35180             t.removeAttribute("title");
35181             e.preventDefault();
35182         }else{
35183             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35184         }
35185         if(ttp){
35186             showProc = show.defer(tm.showDelay, tm, [{
35187                 el: t, 
35188                 text: ttp.replace(/\\n/g,'<br/>'),
35189                 width: et.getAttributeNS(ns, cfg.width),
35190                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35191                 title: et.getAttributeNS(ns, cfg.title),
35192                     cls: et.getAttributeNS(ns, cfg.cls)
35193             }]);
35194         }
35195     };
35196     
35197     var onOut = function(e){
35198         clearTimeout(showProc);
35199         var t = e.getTarget();
35200         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35201             hideProc = setTimeout(hide, tm.hideDelay);
35202         }
35203     };
35204     
35205     var onMove = function(e){
35206         if(disabled){
35207             return;
35208         }
35209         xy = e.getXY();
35210         xy[1] += 18;
35211         if(tm.trackMouse && ce){
35212             el.setXY(xy);
35213         }
35214     };
35215     
35216     var onDown = function(e){
35217         clearTimeout(showProc);
35218         clearTimeout(hideProc);
35219         if(!e.within(el)){
35220             if(tm.hideOnClick){
35221                 hide();
35222                 tm.disable();
35223                 tm.enable.defer(100, tm);
35224             }
35225         }
35226     };
35227     
35228     var getPad = function(){
35229         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35230     };
35231
35232     var show = function(o){
35233         if(disabled){
35234             return;
35235         }
35236         clearTimeout(dismissProc);
35237         ce = o;
35238         if(removeCls){ // in case manually hidden
35239             el.removeClass(removeCls);
35240             removeCls = null;
35241         }
35242         if(ce.cls){
35243             el.addClass(ce.cls);
35244             removeCls = ce.cls;
35245         }
35246         if(ce.title){
35247             tipTitle.update(ce.title);
35248             tipTitle.show();
35249         }else{
35250             tipTitle.update('');
35251             tipTitle.hide();
35252         }
35253         el.dom.style.width  = tm.maxWidth+'px';
35254         //tipBody.dom.style.width = '';
35255         tipBodyText.update(o.text);
35256         var p = getPad(), w = ce.width;
35257         if(!w){
35258             var td = tipBodyText.dom;
35259             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35260             if(aw > tm.maxWidth){
35261                 w = tm.maxWidth;
35262             }else if(aw < tm.minWidth){
35263                 w = tm.minWidth;
35264             }else{
35265                 w = aw;
35266             }
35267         }
35268         //tipBody.setWidth(w);
35269         el.setWidth(parseInt(w, 10) + p);
35270         if(ce.autoHide === false){
35271             close.setDisplayed(true);
35272             if(dd){
35273                 dd.unlock();
35274             }
35275         }else{
35276             close.setDisplayed(false);
35277             if(dd){
35278                 dd.lock();
35279             }
35280         }
35281         if(xy){
35282             el.avoidY = xy[1]-18;
35283             el.setXY(xy);
35284         }
35285         if(tm.animate){
35286             el.setOpacity(.1);
35287             el.setStyle("visibility", "visible");
35288             el.fadeIn({callback: afterShow});
35289         }else{
35290             afterShow();
35291         }
35292     };
35293     
35294     var afterShow = function(){
35295         if(ce){
35296             el.show();
35297             esc.enable();
35298             if(tm.autoDismiss && ce.autoHide !== false){
35299                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35300             }
35301         }
35302     };
35303     
35304     var hide = function(noanim){
35305         clearTimeout(dismissProc);
35306         clearTimeout(hideProc);
35307         ce = null;
35308         if(el.isVisible()){
35309             esc.disable();
35310             if(noanim !== true && tm.animate){
35311                 el.fadeOut({callback: afterHide});
35312             }else{
35313                 afterHide();
35314             } 
35315         }
35316     };
35317     
35318     var afterHide = function(){
35319         el.hide();
35320         if(removeCls){
35321             el.removeClass(removeCls);
35322             removeCls = null;
35323         }
35324     };
35325     
35326     return {
35327         /**
35328         * @cfg {Number} minWidth
35329         * The minimum width of the quick tip (defaults to 40)
35330         */
35331        minWidth : 40,
35332         /**
35333         * @cfg {Number} maxWidth
35334         * The maximum width of the quick tip (defaults to 300)
35335         */
35336        maxWidth : 300,
35337         /**
35338         * @cfg {Boolean} interceptTitles
35339         * True to automatically use the element's DOM title value if available (defaults to false)
35340         */
35341        interceptTitles : false,
35342         /**
35343         * @cfg {Boolean} trackMouse
35344         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35345         */
35346        trackMouse : false,
35347         /**
35348         * @cfg {Boolean} hideOnClick
35349         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35350         */
35351        hideOnClick : true,
35352         /**
35353         * @cfg {Number} showDelay
35354         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35355         */
35356        showDelay : 500,
35357         /**
35358         * @cfg {Number} hideDelay
35359         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35360         */
35361        hideDelay : 200,
35362         /**
35363         * @cfg {Boolean} autoHide
35364         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35365         * Used in conjunction with hideDelay.
35366         */
35367        autoHide : true,
35368         /**
35369         * @cfg {Boolean}
35370         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35371         * (defaults to true).  Used in conjunction with autoDismissDelay.
35372         */
35373        autoDismiss : true,
35374         /**
35375         * @cfg {Number}
35376         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35377         */
35378        autoDismissDelay : 5000,
35379        /**
35380         * @cfg {Boolean} animate
35381         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35382         */
35383        animate : false,
35384
35385        /**
35386         * @cfg {String} title
35387         * Title text to display (defaults to '').  This can be any valid HTML markup.
35388         */
35389         title: '',
35390        /**
35391         * @cfg {String} text
35392         * Body text to display (defaults to '').  This can be any valid HTML markup.
35393         */
35394         text : '',
35395        /**
35396         * @cfg {String} cls
35397         * A CSS class to apply to the base quick tip element (defaults to '').
35398         */
35399         cls : '',
35400        /**
35401         * @cfg {Number} width
35402         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35403         * minWidth or maxWidth.
35404         */
35405         width : null,
35406
35407     /**
35408      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35409      * or display QuickTips in a page.
35410      */
35411        init : function(){
35412           tm = Roo.QuickTips;
35413           cfg = tm.tagConfig;
35414           if(!inited){
35415               if(!Roo.isReady){ // allow calling of init() before onReady
35416                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35417                   return;
35418               }
35419               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35420               el.fxDefaults = {stopFx: true};
35421               // maximum custom styling
35422               //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>');
35423               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>');              
35424               tipTitle = el.child('h3');
35425               tipTitle.enableDisplayMode("block");
35426               tipBody = el.child('div.x-tip-bd');
35427               tipBodyText = el.child('div.x-tip-bd-inner');
35428               //bdLeft = el.child('div.x-tip-bd-left');
35429               //bdRight = el.child('div.x-tip-bd-right');
35430               close = el.child('div.x-tip-close');
35431               close.enableDisplayMode("block");
35432               close.on("click", hide);
35433               var d = Roo.get(document);
35434               d.on("mousedown", onDown);
35435               d.on("mouseover", onOver);
35436               d.on("mouseout", onOut);
35437               d.on("mousemove", onMove);
35438               esc = d.addKeyListener(27, hide);
35439               esc.disable();
35440               if(Roo.dd.DD){
35441                   dd = el.initDD("default", null, {
35442                       onDrag : function(){
35443                           el.sync();  
35444                       }
35445                   });
35446                   dd.setHandleElId(tipTitle.id);
35447                   dd.lock();
35448               }
35449               inited = true;
35450           }
35451           this.enable(); 
35452        },
35453
35454     /**
35455      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35456      * are supported:
35457      * <pre>
35458 Property    Type                   Description
35459 ----------  ---------------------  ------------------------------------------------------------------------
35460 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35461      * </ul>
35462      * @param {Object} config The config object
35463      */
35464        register : function(config){
35465            var cs = config instanceof Array ? config : arguments;
35466            for(var i = 0, len = cs.length; i < len; i++) {
35467                var c = cs[i];
35468                var target = c.target;
35469                if(target){
35470                    if(target instanceof Array){
35471                        for(var j = 0, jlen = target.length; j < jlen; j++){
35472                            tagEls[target[j]] = c;
35473                        }
35474                    }else{
35475                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35476                    }
35477                }
35478            }
35479        },
35480
35481     /**
35482      * Removes this quick tip from its element and destroys it.
35483      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35484      */
35485        unregister : function(el){
35486            delete tagEls[Roo.id(el)];
35487        },
35488
35489     /**
35490      * Enable this quick tip.
35491      */
35492        enable : function(){
35493            if(inited && disabled){
35494                locks.pop();
35495                if(locks.length < 1){
35496                    disabled = false;
35497                }
35498            }
35499        },
35500
35501     /**
35502      * Disable this quick tip.
35503      */
35504        disable : function(){
35505           disabled = true;
35506           clearTimeout(showProc);
35507           clearTimeout(hideProc);
35508           clearTimeout(dismissProc);
35509           if(ce){
35510               hide(true);
35511           }
35512           locks.push(1);
35513        },
35514
35515     /**
35516      * Returns true if the quick tip is enabled, else false.
35517      */
35518        isEnabled : function(){
35519             return !disabled;
35520        },
35521
35522         // private
35523        tagConfig : {
35524            namespace : "roo", // was ext?? this may break..
35525            alt_namespace : "ext",
35526            attribute : "qtip",
35527            width : "width",
35528            target : "target",
35529            title : "qtitle",
35530            hide : "hide",
35531            cls : "qclass"
35532        }
35533    };
35534 }();
35535
35536 // backwards compat
35537 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35538  * Based on:
35539  * Ext JS Library 1.1.1
35540  * Copyright(c) 2006-2007, Ext JS, LLC.
35541  *
35542  * Originally Released Under LGPL - original licence link has changed is not relivant.
35543  *
35544  * Fork - LGPL
35545  * <script type="text/javascript">
35546  */
35547  
35548
35549 /**
35550  * @class Roo.tree.TreePanel
35551  * @extends Roo.data.Tree
35552  * @cfg {Roo.tree.TreeNode} root The root node
35553  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35554  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35555  * @cfg {Boolean} enableDD true to enable drag and drop
35556  * @cfg {Boolean} enableDrag true to enable just drag
35557  * @cfg {Boolean} enableDrop true to enable just drop
35558  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35559  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35560  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35561  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35562  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35563  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35564  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35565  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35566  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35567  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35568  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35569  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35570  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35571  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35572  * @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>
35573  * @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>
35574  * 
35575  * @constructor
35576  * @param {String/HTMLElement/Element} el The container element
35577  * @param {Object} config
35578  */
35579 Roo.tree.TreePanel = function(el, config){
35580     var root = false;
35581     var loader = false;
35582     if (config.root) {
35583         root = config.root;
35584         delete config.root;
35585     }
35586     if (config.loader) {
35587         loader = config.loader;
35588         delete config.loader;
35589     }
35590     
35591     Roo.apply(this, config);
35592     Roo.tree.TreePanel.superclass.constructor.call(this);
35593     this.el = Roo.get(el);
35594     this.el.addClass('x-tree');
35595     //console.log(root);
35596     if (root) {
35597         this.setRootNode( Roo.factory(root, Roo.tree));
35598     }
35599     if (loader) {
35600         this.loader = Roo.factory(loader, Roo.tree);
35601     }
35602    /**
35603     * Read-only. The id of the container element becomes this TreePanel's id.
35604     */
35605     this.id = this.el.id;
35606     this.addEvents({
35607         /**
35608         * @event beforeload
35609         * Fires before a node is loaded, return false to cancel
35610         * @param {Node} node The node being loaded
35611         */
35612         "beforeload" : true,
35613         /**
35614         * @event load
35615         * Fires when a node is loaded
35616         * @param {Node} node The node that was loaded
35617         */
35618         "load" : true,
35619         /**
35620         * @event textchange
35621         * Fires when the text for a node is changed
35622         * @param {Node} node The node
35623         * @param {String} text The new text
35624         * @param {String} oldText The old text
35625         */
35626         "textchange" : true,
35627         /**
35628         * @event beforeexpand
35629         * Fires before a node is expanded, return false to cancel.
35630         * @param {Node} node The node
35631         * @param {Boolean} deep
35632         * @param {Boolean} anim
35633         */
35634         "beforeexpand" : true,
35635         /**
35636         * @event beforecollapse
35637         * Fires before a node is collapsed, return false to cancel.
35638         * @param {Node} node The node
35639         * @param {Boolean} deep
35640         * @param {Boolean} anim
35641         */
35642         "beforecollapse" : true,
35643         /**
35644         * @event expand
35645         * Fires when a node is expanded
35646         * @param {Node} node The node
35647         */
35648         "expand" : true,
35649         /**
35650         * @event disabledchange
35651         * Fires when the disabled status of a node changes
35652         * @param {Node} node The node
35653         * @param {Boolean} disabled
35654         */
35655         "disabledchange" : true,
35656         /**
35657         * @event collapse
35658         * Fires when a node is collapsed
35659         * @param {Node} node The node
35660         */
35661         "collapse" : true,
35662         /**
35663         * @event beforeclick
35664         * Fires before click processing on a node. Return false to cancel the default action.
35665         * @param {Node} node The node
35666         * @param {Roo.EventObject} e The event object
35667         */
35668         "beforeclick":true,
35669         /**
35670         * @event checkchange
35671         * Fires when a node with a checkbox's checked property changes
35672         * @param {Node} this This node
35673         * @param {Boolean} checked
35674         */
35675         "checkchange":true,
35676         /**
35677         * @event click
35678         * Fires when a node is clicked
35679         * @param {Node} node The node
35680         * @param {Roo.EventObject} e The event object
35681         */
35682         "click":true,
35683         /**
35684         * @event dblclick
35685         * Fires when a node is double clicked
35686         * @param {Node} node The node
35687         * @param {Roo.EventObject} e The event object
35688         */
35689         "dblclick":true,
35690         /**
35691         * @event contextmenu
35692         * Fires when a node is right clicked
35693         * @param {Node} node The node
35694         * @param {Roo.EventObject} e The event object
35695         */
35696         "contextmenu":true,
35697         /**
35698         * @event beforechildrenrendered
35699         * Fires right before the child nodes for a node are rendered
35700         * @param {Node} node The node
35701         */
35702         "beforechildrenrendered":true,
35703         /**
35704         * @event startdrag
35705         * Fires when a node starts being dragged
35706         * @param {Roo.tree.TreePanel} this
35707         * @param {Roo.tree.TreeNode} node
35708         * @param {event} e The raw browser event
35709         */ 
35710        "startdrag" : true,
35711        /**
35712         * @event enddrag
35713         * Fires when a drag operation is complete
35714         * @param {Roo.tree.TreePanel} this
35715         * @param {Roo.tree.TreeNode} node
35716         * @param {event} e The raw browser event
35717         */
35718        "enddrag" : true,
35719        /**
35720         * @event dragdrop
35721         * Fires when a dragged node is dropped on a valid DD target
35722         * @param {Roo.tree.TreePanel} this
35723         * @param {Roo.tree.TreeNode} node
35724         * @param {DD} dd The dd it was dropped on
35725         * @param {event} e The raw browser event
35726         */
35727        "dragdrop" : true,
35728        /**
35729         * @event beforenodedrop
35730         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35731         * passed to handlers has the following properties:<br />
35732         * <ul style="padding:5px;padding-left:16px;">
35733         * <li>tree - The TreePanel</li>
35734         * <li>target - The node being targeted for the drop</li>
35735         * <li>data - The drag data from the drag source</li>
35736         * <li>point - The point of the drop - append, above or below</li>
35737         * <li>source - The drag source</li>
35738         * <li>rawEvent - Raw mouse event</li>
35739         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35740         * to be inserted by setting them on this object.</li>
35741         * <li>cancel - Set this to true to cancel the drop.</li>
35742         * </ul>
35743         * @param {Object} dropEvent
35744         */
35745        "beforenodedrop" : true,
35746        /**
35747         * @event nodedrop
35748         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35749         * passed to handlers has the following properties:<br />
35750         * <ul style="padding:5px;padding-left:16px;">
35751         * <li>tree - The TreePanel</li>
35752         * <li>target - The node being targeted for the drop</li>
35753         * <li>data - The drag data from the drag source</li>
35754         * <li>point - The point of the drop - append, above or below</li>
35755         * <li>source - The drag source</li>
35756         * <li>rawEvent - Raw mouse event</li>
35757         * <li>dropNode - Dropped node(s).</li>
35758         * </ul>
35759         * @param {Object} dropEvent
35760         */
35761        "nodedrop" : true,
35762         /**
35763         * @event nodedragover
35764         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35765         * passed to handlers has the following properties:<br />
35766         * <ul style="padding:5px;padding-left:16px;">
35767         * <li>tree - The TreePanel</li>
35768         * <li>target - The node being targeted for the drop</li>
35769         * <li>data - The drag data from the drag source</li>
35770         * <li>point - The point of the drop - append, above or below</li>
35771         * <li>source - The drag source</li>
35772         * <li>rawEvent - Raw mouse event</li>
35773         * <li>dropNode - Drop node(s) provided by the source.</li>
35774         * <li>cancel - Set this to true to signal drop not allowed.</li>
35775         * </ul>
35776         * @param {Object} dragOverEvent
35777         */
35778        "nodedragover" : true,
35779        /**
35780         * @event appendnode
35781         * Fires when append node to the tree
35782         * @param {Roo.tree.TreePanel} this
35783         * @param {Roo.tree.TreeNode} node
35784         * @param {Number} index The index of the newly appended node
35785         */
35786        "appendnode" : true
35787         
35788     });
35789     if(this.singleExpand){
35790        this.on("beforeexpand", this.restrictExpand, this);
35791     }
35792     if (this.editor) {
35793         this.editor.tree = this;
35794         this.editor = Roo.factory(this.editor, Roo.tree);
35795     }
35796     
35797     if (this.selModel) {
35798         this.selModel = Roo.factory(this.selModel, Roo.tree);
35799     }
35800    
35801 };
35802 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35803     rootVisible : true,
35804     animate: Roo.enableFx,
35805     lines : true,
35806     enableDD : false,
35807     hlDrop : Roo.enableFx,
35808   
35809     renderer: false,
35810     
35811     rendererTip: false,
35812     // private
35813     restrictExpand : function(node){
35814         var p = node.parentNode;
35815         if(p){
35816             if(p.expandedChild && p.expandedChild.parentNode == p){
35817                 p.expandedChild.collapse();
35818             }
35819             p.expandedChild = node;
35820         }
35821     },
35822
35823     // private override
35824     setRootNode : function(node){
35825         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35826         if(!this.rootVisible){
35827             node.ui = new Roo.tree.RootTreeNodeUI(node);
35828         }
35829         return node;
35830     },
35831
35832     /**
35833      * Returns the container element for this TreePanel
35834      */
35835     getEl : function(){
35836         return this.el;
35837     },
35838
35839     /**
35840      * Returns the default TreeLoader for this TreePanel
35841      */
35842     getLoader : function(){
35843         return this.loader;
35844     },
35845
35846     /**
35847      * Expand all nodes
35848      */
35849     expandAll : function(){
35850         this.root.expand(true);
35851     },
35852
35853     /**
35854      * Collapse all nodes
35855      */
35856     collapseAll : function(){
35857         this.root.collapse(true);
35858     },
35859
35860     /**
35861      * Returns the selection model used by this TreePanel
35862      */
35863     getSelectionModel : function(){
35864         if(!this.selModel){
35865             this.selModel = new Roo.tree.DefaultSelectionModel();
35866         }
35867         return this.selModel;
35868     },
35869
35870     /**
35871      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35872      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35873      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35874      * @return {Array}
35875      */
35876     getChecked : function(a, startNode){
35877         startNode = startNode || this.root;
35878         var r = [];
35879         var f = function(){
35880             if(this.attributes.checked){
35881                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35882             }
35883         }
35884         startNode.cascade(f);
35885         return r;
35886     },
35887
35888     /**
35889      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35890      * @param {String} path
35891      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35892      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35893      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35894      */
35895     expandPath : function(path, attr, callback){
35896         attr = attr || "id";
35897         var keys = path.split(this.pathSeparator);
35898         var curNode = this.root;
35899         if(curNode.attributes[attr] != keys[1]){ // invalid root
35900             if(callback){
35901                 callback(false, null);
35902             }
35903             return;
35904         }
35905         var index = 1;
35906         var f = function(){
35907             if(++index == keys.length){
35908                 if(callback){
35909                     callback(true, curNode);
35910                 }
35911                 return;
35912             }
35913             var c = curNode.findChild(attr, keys[index]);
35914             if(!c){
35915                 if(callback){
35916                     callback(false, curNode);
35917                 }
35918                 return;
35919             }
35920             curNode = c;
35921             c.expand(false, false, f);
35922         };
35923         curNode.expand(false, false, f);
35924     },
35925
35926     /**
35927      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35928      * @param {String} path
35929      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35930      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35931      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35932      */
35933     selectPath : function(path, attr, callback){
35934         attr = attr || "id";
35935         var keys = path.split(this.pathSeparator);
35936         var v = keys.pop();
35937         if(keys.length > 0){
35938             var f = function(success, node){
35939                 if(success && node){
35940                     var n = node.findChild(attr, v);
35941                     if(n){
35942                         n.select();
35943                         if(callback){
35944                             callback(true, n);
35945                         }
35946                     }else if(callback){
35947                         callback(false, n);
35948                     }
35949                 }else{
35950                     if(callback){
35951                         callback(false, n);
35952                     }
35953                 }
35954             };
35955             this.expandPath(keys.join(this.pathSeparator), attr, f);
35956         }else{
35957             this.root.select();
35958             if(callback){
35959                 callback(true, this.root);
35960             }
35961         }
35962     },
35963
35964     getTreeEl : function(){
35965         return this.el;
35966     },
35967
35968     /**
35969      * Trigger rendering of this TreePanel
35970      */
35971     render : function(){
35972         if (this.innerCt) {
35973             return this; // stop it rendering more than once!!
35974         }
35975         
35976         this.innerCt = this.el.createChild({tag:"ul",
35977                cls:"x-tree-root-ct " +
35978                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35979
35980         if(this.containerScroll){
35981             Roo.dd.ScrollManager.register(this.el);
35982         }
35983         if((this.enableDD || this.enableDrop) && !this.dropZone){
35984            /**
35985             * The dropZone used by this tree if drop is enabled
35986             * @type Roo.tree.TreeDropZone
35987             */
35988              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35989                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35990            });
35991         }
35992         if((this.enableDD || this.enableDrag) && !this.dragZone){
35993            /**
35994             * The dragZone used by this tree if drag is enabled
35995             * @type Roo.tree.TreeDragZone
35996             */
35997             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35998                ddGroup: this.ddGroup || "TreeDD",
35999                scroll: this.ddScroll
36000            });
36001         }
36002         this.getSelectionModel().init(this);
36003         if (!this.root) {
36004             Roo.log("ROOT not set in tree");
36005             return this;
36006         }
36007         this.root.render();
36008         if(!this.rootVisible){
36009             this.root.renderChildren();
36010         }
36011         return this;
36012     }
36013 });/*
36014  * Based on:
36015  * Ext JS Library 1.1.1
36016  * Copyright(c) 2006-2007, Ext JS, LLC.
36017  *
36018  * Originally Released Under LGPL - original licence link has changed is not relivant.
36019  *
36020  * Fork - LGPL
36021  * <script type="text/javascript">
36022  */
36023  
36024
36025 /**
36026  * @class Roo.tree.DefaultSelectionModel
36027  * @extends Roo.util.Observable
36028  * The default single selection for a TreePanel.
36029  * @param {Object} cfg Configuration
36030  */
36031 Roo.tree.DefaultSelectionModel = function(cfg){
36032    this.selNode = null;
36033    
36034    
36035    
36036    this.addEvents({
36037        /**
36038         * @event selectionchange
36039         * Fires when the selected node changes
36040         * @param {DefaultSelectionModel} this
36041         * @param {TreeNode} node the new selection
36042         */
36043        "selectionchange" : true,
36044
36045        /**
36046         * @event beforeselect
36047         * Fires before the selected node changes, return false to cancel the change
36048         * @param {DefaultSelectionModel} this
36049         * @param {TreeNode} node the new selection
36050         * @param {TreeNode} node the old selection
36051         */
36052        "beforeselect" : true
36053    });
36054    
36055     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36056 };
36057
36058 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36059     init : function(tree){
36060         this.tree = tree;
36061         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36062         tree.on("click", this.onNodeClick, this);
36063     },
36064     
36065     onNodeClick : function(node, e){
36066         if (e.ctrlKey && this.selNode == node)  {
36067             this.unselect(node);
36068             return;
36069         }
36070         this.select(node);
36071     },
36072     
36073     /**
36074      * Select a node.
36075      * @param {TreeNode} node The node to select
36076      * @return {TreeNode} The selected node
36077      */
36078     select : function(node){
36079         var last = this.selNode;
36080         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36081             if(last){
36082                 last.ui.onSelectedChange(false);
36083             }
36084             this.selNode = node;
36085             node.ui.onSelectedChange(true);
36086             this.fireEvent("selectionchange", this, node, last);
36087         }
36088         return node;
36089     },
36090     
36091     /**
36092      * Deselect a node.
36093      * @param {TreeNode} node The node to unselect
36094      */
36095     unselect : function(node){
36096         if(this.selNode == node){
36097             this.clearSelections();
36098         }    
36099     },
36100     
36101     /**
36102      * Clear all selections
36103      */
36104     clearSelections : function(){
36105         var n = this.selNode;
36106         if(n){
36107             n.ui.onSelectedChange(false);
36108             this.selNode = null;
36109             this.fireEvent("selectionchange", this, null);
36110         }
36111         return n;
36112     },
36113     
36114     /**
36115      * Get the selected node
36116      * @return {TreeNode} The selected node
36117      */
36118     getSelectedNode : function(){
36119         return this.selNode;    
36120     },
36121     
36122     /**
36123      * Returns true if the node is selected
36124      * @param {TreeNode} node The node to check
36125      * @return {Boolean}
36126      */
36127     isSelected : function(node){
36128         return this.selNode == node;  
36129     },
36130
36131     /**
36132      * Selects the node above the selected node in the tree, intelligently walking the nodes
36133      * @return TreeNode The new selection
36134      */
36135     selectPrevious : function(){
36136         var s = this.selNode || this.lastSelNode;
36137         if(!s){
36138             return null;
36139         }
36140         var ps = s.previousSibling;
36141         if(ps){
36142             if(!ps.isExpanded() || ps.childNodes.length < 1){
36143                 return this.select(ps);
36144             } else{
36145                 var lc = ps.lastChild;
36146                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36147                     lc = lc.lastChild;
36148                 }
36149                 return this.select(lc);
36150             }
36151         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36152             return this.select(s.parentNode);
36153         }
36154         return null;
36155     },
36156
36157     /**
36158      * Selects the node above the selected node in the tree, intelligently walking the nodes
36159      * @return TreeNode The new selection
36160      */
36161     selectNext : function(){
36162         var s = this.selNode || this.lastSelNode;
36163         if(!s){
36164             return null;
36165         }
36166         if(s.firstChild && s.isExpanded()){
36167              return this.select(s.firstChild);
36168          }else if(s.nextSibling){
36169              return this.select(s.nextSibling);
36170          }else if(s.parentNode){
36171             var newS = null;
36172             s.parentNode.bubble(function(){
36173                 if(this.nextSibling){
36174                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36175                     return false;
36176                 }
36177             });
36178             return newS;
36179          }
36180         return null;
36181     },
36182
36183     onKeyDown : function(e){
36184         var s = this.selNode || this.lastSelNode;
36185         // undesirable, but required
36186         var sm = this;
36187         if(!s){
36188             return;
36189         }
36190         var k = e.getKey();
36191         switch(k){
36192              case e.DOWN:
36193                  e.stopEvent();
36194                  this.selectNext();
36195              break;
36196              case e.UP:
36197                  e.stopEvent();
36198                  this.selectPrevious();
36199              break;
36200              case e.RIGHT:
36201                  e.preventDefault();
36202                  if(s.hasChildNodes()){
36203                      if(!s.isExpanded()){
36204                          s.expand();
36205                      }else if(s.firstChild){
36206                          this.select(s.firstChild, e);
36207                      }
36208                  }
36209              break;
36210              case e.LEFT:
36211                  e.preventDefault();
36212                  if(s.hasChildNodes() && s.isExpanded()){
36213                      s.collapse();
36214                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36215                      this.select(s.parentNode, e);
36216                  }
36217              break;
36218         };
36219     }
36220 });
36221
36222 /**
36223  * @class Roo.tree.MultiSelectionModel
36224  * @extends Roo.util.Observable
36225  * Multi selection for a TreePanel.
36226  * @param {Object} cfg Configuration
36227  */
36228 Roo.tree.MultiSelectionModel = function(){
36229    this.selNodes = [];
36230    this.selMap = {};
36231    this.addEvents({
36232        /**
36233         * @event selectionchange
36234         * Fires when the selected nodes change
36235         * @param {MultiSelectionModel} this
36236         * @param {Array} nodes Array of the selected nodes
36237         */
36238        "selectionchange" : true
36239    });
36240    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36241    
36242 };
36243
36244 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36245     init : function(tree){
36246         this.tree = tree;
36247         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36248         tree.on("click", this.onNodeClick, this);
36249     },
36250     
36251     onNodeClick : function(node, e){
36252         this.select(node, e, e.ctrlKey);
36253     },
36254     
36255     /**
36256      * Select a node.
36257      * @param {TreeNode} node The node to select
36258      * @param {EventObject} e (optional) An event associated with the selection
36259      * @param {Boolean} keepExisting True to retain existing selections
36260      * @return {TreeNode} The selected node
36261      */
36262     select : function(node, e, keepExisting){
36263         if(keepExisting !== true){
36264             this.clearSelections(true);
36265         }
36266         if(this.isSelected(node)){
36267             this.lastSelNode = node;
36268             return node;
36269         }
36270         this.selNodes.push(node);
36271         this.selMap[node.id] = node;
36272         this.lastSelNode = node;
36273         node.ui.onSelectedChange(true);
36274         this.fireEvent("selectionchange", this, this.selNodes);
36275         return node;
36276     },
36277     
36278     /**
36279      * Deselect a node.
36280      * @param {TreeNode} node The node to unselect
36281      */
36282     unselect : function(node){
36283         if(this.selMap[node.id]){
36284             node.ui.onSelectedChange(false);
36285             var sn = this.selNodes;
36286             var index = -1;
36287             if(sn.indexOf){
36288                 index = sn.indexOf(node);
36289             }else{
36290                 for(var i = 0, len = sn.length; i < len; i++){
36291                     if(sn[i] == node){
36292                         index = i;
36293                         break;
36294                     }
36295                 }
36296             }
36297             if(index != -1){
36298                 this.selNodes.splice(index, 1);
36299             }
36300             delete this.selMap[node.id];
36301             this.fireEvent("selectionchange", this, this.selNodes);
36302         }
36303     },
36304     
36305     /**
36306      * Clear all selections
36307      */
36308     clearSelections : function(suppressEvent){
36309         var sn = this.selNodes;
36310         if(sn.length > 0){
36311             for(var i = 0, len = sn.length; i < len; i++){
36312                 sn[i].ui.onSelectedChange(false);
36313             }
36314             this.selNodes = [];
36315             this.selMap = {};
36316             if(suppressEvent !== true){
36317                 this.fireEvent("selectionchange", this, this.selNodes);
36318             }
36319         }
36320     },
36321     
36322     /**
36323      * Returns true if the node is selected
36324      * @param {TreeNode} node The node to check
36325      * @return {Boolean}
36326      */
36327     isSelected : function(node){
36328         return this.selMap[node.id] ? true : false;  
36329     },
36330     
36331     /**
36332      * Returns an array of the selected nodes
36333      * @return {Array}
36334      */
36335     getSelectedNodes : function(){
36336         return this.selNodes;    
36337     },
36338
36339     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36340
36341     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36342
36343     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36344 });/*
36345  * Based on:
36346  * Ext JS Library 1.1.1
36347  * Copyright(c) 2006-2007, Ext JS, LLC.
36348  *
36349  * Originally Released Under LGPL - original licence link has changed is not relivant.
36350  *
36351  * Fork - LGPL
36352  * <script type="text/javascript">
36353  */
36354  
36355 /**
36356  * @class Roo.tree.TreeNode
36357  * @extends Roo.data.Node
36358  * @cfg {String} text The text for this node
36359  * @cfg {Boolean} expanded true to start the node expanded
36360  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36361  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36362  * @cfg {Boolean} disabled true to start the node disabled
36363  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36364  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36365  * @cfg {String} cls A css class to be added to the node
36366  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36367  * @cfg {String} href URL of the link used for the node (defaults to #)
36368  * @cfg {String} hrefTarget target frame for the link
36369  * @cfg {String} qtip An Ext QuickTip for the node
36370  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36371  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36372  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36373  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36374  * (defaults to undefined with no checkbox rendered)
36375  * @constructor
36376  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36377  */
36378 Roo.tree.TreeNode = function(attributes){
36379     attributes = attributes || {};
36380     if(typeof attributes == "string"){
36381         attributes = {text: attributes};
36382     }
36383     this.childrenRendered = false;
36384     this.rendered = false;
36385     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36386     this.expanded = attributes.expanded === true;
36387     this.isTarget = attributes.isTarget !== false;
36388     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36389     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36390
36391     /**
36392      * Read-only. The text for this node. To change it use setText().
36393      * @type String
36394      */
36395     this.text = attributes.text;
36396     /**
36397      * True if this node is disabled.
36398      * @type Boolean
36399      */
36400     this.disabled = attributes.disabled === true;
36401
36402     this.addEvents({
36403         /**
36404         * @event textchange
36405         * Fires when the text for this node is changed
36406         * @param {Node} this This node
36407         * @param {String} text The new text
36408         * @param {String} oldText The old text
36409         */
36410         "textchange" : true,
36411         /**
36412         * @event beforeexpand
36413         * Fires before this node is expanded, return false to cancel.
36414         * @param {Node} this This node
36415         * @param {Boolean} deep
36416         * @param {Boolean} anim
36417         */
36418         "beforeexpand" : true,
36419         /**
36420         * @event beforecollapse
36421         * Fires before this node is collapsed, return false to cancel.
36422         * @param {Node} this This node
36423         * @param {Boolean} deep
36424         * @param {Boolean} anim
36425         */
36426         "beforecollapse" : true,
36427         /**
36428         * @event expand
36429         * Fires when this node is expanded
36430         * @param {Node} this This node
36431         */
36432         "expand" : true,
36433         /**
36434         * @event disabledchange
36435         * Fires when the disabled status of this node changes
36436         * @param {Node} this This node
36437         * @param {Boolean} disabled
36438         */
36439         "disabledchange" : true,
36440         /**
36441         * @event collapse
36442         * Fires when this node is collapsed
36443         * @param {Node} this This node
36444         */
36445         "collapse" : true,
36446         /**
36447         * @event beforeclick
36448         * Fires before click processing. Return false to cancel the default action.
36449         * @param {Node} this This node
36450         * @param {Roo.EventObject} e The event object
36451         */
36452         "beforeclick":true,
36453         /**
36454         * @event checkchange
36455         * Fires when a node with a checkbox's checked property changes
36456         * @param {Node} this This node
36457         * @param {Boolean} checked
36458         */
36459         "checkchange":true,
36460         /**
36461         * @event click
36462         * Fires when this node is clicked
36463         * @param {Node} this This node
36464         * @param {Roo.EventObject} e The event object
36465         */
36466         "click":true,
36467         /**
36468         * @event dblclick
36469         * Fires when this node is double clicked
36470         * @param {Node} this This node
36471         * @param {Roo.EventObject} e The event object
36472         */
36473         "dblclick":true,
36474         /**
36475         * @event contextmenu
36476         * Fires when this node is right clicked
36477         * @param {Node} this This node
36478         * @param {Roo.EventObject} e The event object
36479         */
36480         "contextmenu":true,
36481         /**
36482         * @event beforechildrenrendered
36483         * Fires right before the child nodes for this node are rendered
36484         * @param {Node} this This node
36485         */
36486         "beforechildrenrendered":true
36487     });
36488
36489     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36490
36491     /**
36492      * Read-only. The UI for this node
36493      * @type TreeNodeUI
36494      */
36495     this.ui = new uiClass(this);
36496     
36497     // finally support items[]
36498     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36499         return;
36500     }
36501     
36502     
36503     Roo.each(this.attributes.items, function(c) {
36504         this.appendChild(Roo.factory(c,Roo.Tree));
36505     }, this);
36506     delete this.attributes.items;
36507     
36508     
36509     
36510 };
36511 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36512     preventHScroll: true,
36513     /**
36514      * Returns true if this node is expanded
36515      * @return {Boolean}
36516      */
36517     isExpanded : function(){
36518         return this.expanded;
36519     },
36520
36521     /**
36522      * Returns the UI object for this node
36523      * @return {TreeNodeUI}
36524      */
36525     getUI : function(){
36526         return this.ui;
36527     },
36528
36529     // private override
36530     setFirstChild : function(node){
36531         var of = this.firstChild;
36532         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36533         if(this.childrenRendered && of && node != of){
36534             of.renderIndent(true, true);
36535         }
36536         if(this.rendered){
36537             this.renderIndent(true, true);
36538         }
36539     },
36540
36541     // private override
36542     setLastChild : function(node){
36543         var ol = this.lastChild;
36544         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36545         if(this.childrenRendered && ol && node != ol){
36546             ol.renderIndent(true, true);
36547         }
36548         if(this.rendered){
36549             this.renderIndent(true, true);
36550         }
36551     },
36552
36553     // these methods are overridden to provide lazy rendering support
36554     // private override
36555     appendChild : function()
36556     {
36557         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36558         if(node && this.childrenRendered){
36559             node.render();
36560         }
36561         this.ui.updateExpandIcon();
36562         return node;
36563     },
36564
36565     // private override
36566     removeChild : function(node){
36567         this.ownerTree.getSelectionModel().unselect(node);
36568         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36569         // if it's been rendered remove dom node
36570         if(this.childrenRendered){
36571             node.ui.remove();
36572         }
36573         if(this.childNodes.length < 1){
36574             this.collapse(false, false);
36575         }else{
36576             this.ui.updateExpandIcon();
36577         }
36578         if(!this.firstChild) {
36579             this.childrenRendered = false;
36580         }
36581         return node;
36582     },
36583
36584     // private override
36585     insertBefore : function(node, refNode){
36586         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36587         if(newNode && refNode && this.childrenRendered){
36588             node.render();
36589         }
36590         this.ui.updateExpandIcon();
36591         return newNode;
36592     },
36593
36594     /**
36595      * Sets the text for this node
36596      * @param {String} text
36597      */
36598     setText : function(text){
36599         var oldText = this.text;
36600         this.text = text;
36601         this.attributes.text = text;
36602         if(this.rendered){ // event without subscribing
36603             this.ui.onTextChange(this, text, oldText);
36604         }
36605         this.fireEvent("textchange", this, text, oldText);
36606     },
36607
36608     /**
36609      * Triggers selection of this node
36610      */
36611     select : function(){
36612         this.getOwnerTree().getSelectionModel().select(this);
36613     },
36614
36615     /**
36616      * Triggers deselection of this node
36617      */
36618     unselect : function(){
36619         this.getOwnerTree().getSelectionModel().unselect(this);
36620     },
36621
36622     /**
36623      * Returns true if this node is selected
36624      * @return {Boolean}
36625      */
36626     isSelected : function(){
36627         return this.getOwnerTree().getSelectionModel().isSelected(this);
36628     },
36629
36630     /**
36631      * Expand this node.
36632      * @param {Boolean} deep (optional) True to expand all children as well
36633      * @param {Boolean} anim (optional) false to cancel the default animation
36634      * @param {Function} callback (optional) A callback to be called when
36635      * expanding this node completes (does not wait for deep expand to complete).
36636      * Called with 1 parameter, this node.
36637      */
36638     expand : function(deep, anim, callback){
36639         if(!this.expanded){
36640             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36641                 return;
36642             }
36643             if(!this.childrenRendered){
36644                 this.renderChildren();
36645             }
36646             this.expanded = true;
36647             
36648             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36649                 this.ui.animExpand(function(){
36650                     this.fireEvent("expand", this);
36651                     if(typeof callback == "function"){
36652                         callback(this);
36653                     }
36654                     if(deep === true){
36655                         this.expandChildNodes(true);
36656                     }
36657                 }.createDelegate(this));
36658                 return;
36659             }else{
36660                 this.ui.expand();
36661                 this.fireEvent("expand", this);
36662                 if(typeof callback == "function"){
36663                     callback(this);
36664                 }
36665             }
36666         }else{
36667            if(typeof callback == "function"){
36668                callback(this);
36669            }
36670         }
36671         if(deep === true){
36672             this.expandChildNodes(true);
36673         }
36674     },
36675
36676     isHiddenRoot : function(){
36677         return this.isRoot && !this.getOwnerTree().rootVisible;
36678     },
36679
36680     /**
36681      * Collapse this node.
36682      * @param {Boolean} deep (optional) True to collapse all children as well
36683      * @param {Boolean} anim (optional) false to cancel the default animation
36684      */
36685     collapse : function(deep, anim){
36686         if(this.expanded && !this.isHiddenRoot()){
36687             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36688                 return;
36689             }
36690             this.expanded = false;
36691             if((this.getOwnerTree().animate && anim !== false) || anim){
36692                 this.ui.animCollapse(function(){
36693                     this.fireEvent("collapse", this);
36694                     if(deep === true){
36695                         this.collapseChildNodes(true);
36696                     }
36697                 }.createDelegate(this));
36698                 return;
36699             }else{
36700                 this.ui.collapse();
36701                 this.fireEvent("collapse", this);
36702             }
36703         }
36704         if(deep === true){
36705             var cs = this.childNodes;
36706             for(var i = 0, len = cs.length; i < len; i++) {
36707                 cs[i].collapse(true, false);
36708             }
36709         }
36710     },
36711
36712     // private
36713     delayedExpand : function(delay){
36714         if(!this.expandProcId){
36715             this.expandProcId = this.expand.defer(delay, this);
36716         }
36717     },
36718
36719     // private
36720     cancelExpand : function(){
36721         if(this.expandProcId){
36722             clearTimeout(this.expandProcId);
36723         }
36724         this.expandProcId = false;
36725     },
36726
36727     /**
36728      * Toggles expanded/collapsed state of the node
36729      */
36730     toggle : function(){
36731         if(this.expanded){
36732             this.collapse();
36733         }else{
36734             this.expand();
36735         }
36736     },
36737
36738     /**
36739      * Ensures all parent nodes are expanded
36740      */
36741     ensureVisible : function(callback){
36742         var tree = this.getOwnerTree();
36743         tree.expandPath(this.parentNode.getPath(), false, function(){
36744             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36745             Roo.callback(callback);
36746         }.createDelegate(this));
36747     },
36748
36749     /**
36750      * Expand all child nodes
36751      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36752      */
36753     expandChildNodes : function(deep){
36754         var cs = this.childNodes;
36755         for(var i = 0, len = cs.length; i < len; i++) {
36756                 cs[i].expand(deep);
36757         }
36758     },
36759
36760     /**
36761      * Collapse all child nodes
36762      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36763      */
36764     collapseChildNodes : function(deep){
36765         var cs = this.childNodes;
36766         for(var i = 0, len = cs.length; i < len; i++) {
36767                 cs[i].collapse(deep);
36768         }
36769     },
36770
36771     /**
36772      * Disables this node
36773      */
36774     disable : function(){
36775         this.disabled = true;
36776         this.unselect();
36777         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36778             this.ui.onDisableChange(this, true);
36779         }
36780         this.fireEvent("disabledchange", this, true);
36781     },
36782
36783     /**
36784      * Enables this node
36785      */
36786     enable : function(){
36787         this.disabled = false;
36788         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36789             this.ui.onDisableChange(this, false);
36790         }
36791         this.fireEvent("disabledchange", this, false);
36792     },
36793
36794     // private
36795     renderChildren : function(suppressEvent){
36796         if(suppressEvent !== false){
36797             this.fireEvent("beforechildrenrendered", this);
36798         }
36799         var cs = this.childNodes;
36800         for(var i = 0, len = cs.length; i < len; i++){
36801             cs[i].render(true);
36802         }
36803         this.childrenRendered = true;
36804     },
36805
36806     // private
36807     sort : function(fn, scope){
36808         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36809         if(this.childrenRendered){
36810             var cs = this.childNodes;
36811             for(var i = 0, len = cs.length; i < len; i++){
36812                 cs[i].render(true);
36813             }
36814         }
36815     },
36816
36817     // private
36818     render : function(bulkRender){
36819         this.ui.render(bulkRender);
36820         if(!this.rendered){
36821             this.rendered = true;
36822             if(this.expanded){
36823                 this.expanded = false;
36824                 this.expand(false, false);
36825             }
36826         }
36827     },
36828
36829     // private
36830     renderIndent : function(deep, refresh){
36831         if(refresh){
36832             this.ui.childIndent = null;
36833         }
36834         this.ui.renderIndent();
36835         if(deep === true && this.childrenRendered){
36836             var cs = this.childNodes;
36837             for(var i = 0, len = cs.length; i < len; i++){
36838                 cs[i].renderIndent(true, refresh);
36839             }
36840         }
36841     }
36842 });/*
36843  * Based on:
36844  * Ext JS Library 1.1.1
36845  * Copyright(c) 2006-2007, Ext JS, LLC.
36846  *
36847  * Originally Released Under LGPL - original licence link has changed is not relivant.
36848  *
36849  * Fork - LGPL
36850  * <script type="text/javascript">
36851  */
36852  
36853 /**
36854  * @class Roo.tree.AsyncTreeNode
36855  * @extends Roo.tree.TreeNode
36856  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36857  * @constructor
36858  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36859  */
36860  Roo.tree.AsyncTreeNode = function(config){
36861     this.loaded = false;
36862     this.loading = false;
36863     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36864     /**
36865     * @event beforeload
36866     * Fires before this node is loaded, return false to cancel
36867     * @param {Node} this This node
36868     */
36869     this.addEvents({'beforeload':true, 'load': true});
36870     /**
36871     * @event load
36872     * Fires when this node is loaded
36873     * @param {Node} this This node
36874     */
36875     /**
36876      * The loader used by this node (defaults to using the tree's defined loader)
36877      * @type TreeLoader
36878      * @property loader
36879      */
36880 };
36881 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36882     expand : function(deep, anim, callback){
36883         if(this.loading){ // if an async load is already running, waiting til it's done
36884             var timer;
36885             var f = function(){
36886                 if(!this.loading){ // done loading
36887                     clearInterval(timer);
36888                     this.expand(deep, anim, callback);
36889                 }
36890             }.createDelegate(this);
36891             timer = setInterval(f, 200);
36892             return;
36893         }
36894         if(!this.loaded){
36895             if(this.fireEvent("beforeload", this) === false){
36896                 return;
36897             }
36898             this.loading = true;
36899             this.ui.beforeLoad(this);
36900             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36901             if(loader){
36902                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36903                 return;
36904             }
36905         }
36906         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36907     },
36908     
36909     /**
36910      * Returns true if this node is currently loading
36911      * @return {Boolean}
36912      */
36913     isLoading : function(){
36914         return this.loading;  
36915     },
36916     
36917     loadComplete : function(deep, anim, callback){
36918         this.loading = false;
36919         this.loaded = true;
36920         this.ui.afterLoad(this);
36921         this.fireEvent("load", this);
36922         this.expand(deep, anim, callback);
36923     },
36924     
36925     /**
36926      * Returns true if this node has been loaded
36927      * @return {Boolean}
36928      */
36929     isLoaded : function(){
36930         return this.loaded;
36931     },
36932     
36933     hasChildNodes : function(){
36934         if(!this.isLeaf() && !this.loaded){
36935             return true;
36936         }else{
36937             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36938         }
36939     },
36940
36941     /**
36942      * Trigger a reload for this node
36943      * @param {Function} callback
36944      */
36945     reload : function(callback){
36946         this.collapse(false, false);
36947         while(this.firstChild){
36948             this.removeChild(this.firstChild);
36949         }
36950         this.childrenRendered = false;
36951         this.loaded = false;
36952         if(this.isHiddenRoot()){
36953             this.expanded = false;
36954         }
36955         this.expand(false, false, callback);
36956     }
36957 });/*
36958  * Based on:
36959  * Ext JS Library 1.1.1
36960  * Copyright(c) 2006-2007, Ext JS, LLC.
36961  *
36962  * Originally Released Under LGPL - original licence link has changed is not relivant.
36963  *
36964  * Fork - LGPL
36965  * <script type="text/javascript">
36966  */
36967  
36968 /**
36969  * @class Roo.tree.TreeNodeUI
36970  * @constructor
36971  * @param {Object} node The node to render
36972  * The TreeNode UI implementation is separate from the
36973  * tree implementation. Unless you are customizing the tree UI,
36974  * you should never have to use this directly.
36975  */
36976 Roo.tree.TreeNodeUI = function(node){
36977     this.node = node;
36978     this.rendered = false;
36979     this.animating = false;
36980     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36981 };
36982
36983 Roo.tree.TreeNodeUI.prototype = {
36984     removeChild : function(node){
36985         if(this.rendered){
36986             this.ctNode.removeChild(node.ui.getEl());
36987         }
36988     },
36989
36990     beforeLoad : function(){
36991          this.addClass("x-tree-node-loading");
36992     },
36993
36994     afterLoad : function(){
36995          this.removeClass("x-tree-node-loading");
36996     },
36997
36998     onTextChange : function(node, text, oldText){
36999         if(this.rendered){
37000             this.textNode.innerHTML = text;
37001         }
37002     },
37003
37004     onDisableChange : function(node, state){
37005         this.disabled = state;
37006         if(state){
37007             this.addClass("x-tree-node-disabled");
37008         }else{
37009             this.removeClass("x-tree-node-disabled");
37010         }
37011     },
37012
37013     onSelectedChange : function(state){
37014         if(state){
37015             this.focus();
37016             this.addClass("x-tree-selected");
37017         }else{
37018             //this.blur();
37019             this.removeClass("x-tree-selected");
37020         }
37021     },
37022
37023     onMove : function(tree, node, oldParent, newParent, index, refNode){
37024         this.childIndent = null;
37025         if(this.rendered){
37026             var targetNode = newParent.ui.getContainer();
37027             if(!targetNode){//target not rendered
37028                 this.holder = document.createElement("div");
37029                 this.holder.appendChild(this.wrap);
37030                 return;
37031             }
37032             var insertBefore = refNode ? refNode.ui.getEl() : null;
37033             if(insertBefore){
37034                 targetNode.insertBefore(this.wrap, insertBefore);
37035             }else{
37036                 targetNode.appendChild(this.wrap);
37037             }
37038             this.node.renderIndent(true);
37039         }
37040     },
37041
37042     addClass : function(cls){
37043         if(this.elNode){
37044             Roo.fly(this.elNode).addClass(cls);
37045         }
37046     },
37047
37048     removeClass : function(cls){
37049         if(this.elNode){
37050             Roo.fly(this.elNode).removeClass(cls);
37051         }
37052     },
37053
37054     remove : function(){
37055         if(this.rendered){
37056             this.holder = document.createElement("div");
37057             this.holder.appendChild(this.wrap);
37058         }
37059     },
37060
37061     fireEvent : function(){
37062         return this.node.fireEvent.apply(this.node, arguments);
37063     },
37064
37065     initEvents : function(){
37066         this.node.on("move", this.onMove, this);
37067         var E = Roo.EventManager;
37068         var a = this.anchor;
37069
37070         var el = Roo.fly(a, '_treeui');
37071
37072         if(Roo.isOpera){ // opera render bug ignores the CSS
37073             el.setStyle("text-decoration", "none");
37074         }
37075
37076         el.on("click", this.onClick, this);
37077         el.on("dblclick", this.onDblClick, this);
37078
37079         if(this.checkbox){
37080             Roo.EventManager.on(this.checkbox,
37081                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37082         }
37083
37084         el.on("contextmenu", this.onContextMenu, this);
37085
37086         var icon = Roo.fly(this.iconNode);
37087         icon.on("click", this.onClick, this);
37088         icon.on("dblclick", this.onDblClick, this);
37089         icon.on("contextmenu", this.onContextMenu, this);
37090         E.on(this.ecNode, "click", this.ecClick, this, true);
37091
37092         if(this.node.disabled){
37093             this.addClass("x-tree-node-disabled");
37094         }
37095         if(this.node.hidden){
37096             this.addClass("x-tree-node-disabled");
37097         }
37098         var ot = this.node.getOwnerTree();
37099         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37100         if(dd && (!this.node.isRoot || ot.rootVisible)){
37101             Roo.dd.Registry.register(this.elNode, {
37102                 node: this.node,
37103                 handles: this.getDDHandles(),
37104                 isHandle: false
37105             });
37106         }
37107     },
37108
37109     getDDHandles : function(){
37110         return [this.iconNode, this.textNode];
37111     },
37112
37113     hide : function(){
37114         if(this.rendered){
37115             this.wrap.style.display = "none";
37116         }
37117     },
37118
37119     show : function(){
37120         if(this.rendered){
37121             this.wrap.style.display = "";
37122         }
37123     },
37124
37125     onContextMenu : function(e){
37126         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37127             e.preventDefault();
37128             this.focus();
37129             this.fireEvent("contextmenu", this.node, e);
37130         }
37131     },
37132
37133     onClick : function(e){
37134         if(this.dropping){
37135             e.stopEvent();
37136             return;
37137         }
37138         if(this.fireEvent("beforeclick", this.node, e) !== false){
37139             if(!this.disabled && this.node.attributes.href){
37140                 this.fireEvent("click", this.node, e);
37141                 return;
37142             }
37143             e.preventDefault();
37144             if(this.disabled){
37145                 return;
37146             }
37147
37148             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37149                 this.node.toggle();
37150             }
37151
37152             this.fireEvent("click", this.node, e);
37153         }else{
37154             e.stopEvent();
37155         }
37156     },
37157
37158     onDblClick : function(e){
37159         e.preventDefault();
37160         if(this.disabled){
37161             return;
37162         }
37163         if(this.checkbox){
37164             this.toggleCheck();
37165         }
37166         if(!this.animating && this.node.hasChildNodes()){
37167             this.node.toggle();
37168         }
37169         this.fireEvent("dblclick", this.node, e);
37170     },
37171
37172     onCheckChange : function(){
37173         var checked = this.checkbox.checked;
37174         this.node.attributes.checked = checked;
37175         this.fireEvent('checkchange', this.node, checked);
37176     },
37177
37178     ecClick : function(e){
37179         if(!this.animating && this.node.hasChildNodes()){
37180             this.node.toggle();
37181         }
37182     },
37183
37184     startDrop : function(){
37185         this.dropping = true;
37186     },
37187
37188     // delayed drop so the click event doesn't get fired on a drop
37189     endDrop : function(){
37190        setTimeout(function(){
37191            this.dropping = false;
37192        }.createDelegate(this), 50);
37193     },
37194
37195     expand : function(){
37196         this.updateExpandIcon();
37197         this.ctNode.style.display = "";
37198     },
37199
37200     focus : function(){
37201         if(!this.node.preventHScroll){
37202             try{this.anchor.focus();
37203             }catch(e){}
37204         }else if(!Roo.isIE){
37205             try{
37206                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37207                 var l = noscroll.scrollLeft;
37208                 this.anchor.focus();
37209                 noscroll.scrollLeft = l;
37210             }catch(e){}
37211         }
37212     },
37213
37214     toggleCheck : function(value){
37215         var cb = this.checkbox;
37216         if(cb){
37217             cb.checked = (value === undefined ? !cb.checked : value);
37218         }
37219     },
37220
37221     blur : function(){
37222         try{
37223             this.anchor.blur();
37224         }catch(e){}
37225     },
37226
37227     animExpand : function(callback){
37228         var ct = Roo.get(this.ctNode);
37229         ct.stopFx();
37230         if(!this.node.hasChildNodes()){
37231             this.updateExpandIcon();
37232             this.ctNode.style.display = "";
37233             Roo.callback(callback);
37234             return;
37235         }
37236         this.animating = true;
37237         this.updateExpandIcon();
37238
37239         ct.slideIn('t', {
37240            callback : function(){
37241                this.animating = false;
37242                Roo.callback(callback);
37243             },
37244             scope: this,
37245             duration: this.node.ownerTree.duration || .25
37246         });
37247     },
37248
37249     highlight : function(){
37250         var tree = this.node.getOwnerTree();
37251         Roo.fly(this.wrap).highlight(
37252             tree.hlColor || "C3DAF9",
37253             {endColor: tree.hlBaseColor}
37254         );
37255     },
37256
37257     collapse : function(){
37258         this.updateExpandIcon();
37259         this.ctNode.style.display = "none";
37260     },
37261
37262     animCollapse : function(callback){
37263         var ct = Roo.get(this.ctNode);
37264         ct.enableDisplayMode('block');
37265         ct.stopFx();
37266
37267         this.animating = true;
37268         this.updateExpandIcon();
37269
37270         ct.slideOut('t', {
37271             callback : function(){
37272                this.animating = false;
37273                Roo.callback(callback);
37274             },
37275             scope: this,
37276             duration: this.node.ownerTree.duration || .25
37277         });
37278     },
37279
37280     getContainer : function(){
37281         return this.ctNode;
37282     },
37283
37284     getEl : function(){
37285         return this.wrap;
37286     },
37287
37288     appendDDGhost : function(ghostNode){
37289         ghostNode.appendChild(this.elNode.cloneNode(true));
37290     },
37291
37292     getDDRepairXY : function(){
37293         return Roo.lib.Dom.getXY(this.iconNode);
37294     },
37295
37296     onRender : function(){
37297         this.render();
37298     },
37299
37300     render : function(bulkRender){
37301         var n = this.node, a = n.attributes;
37302         var targetNode = n.parentNode ?
37303               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37304
37305         if(!this.rendered){
37306             this.rendered = true;
37307
37308             this.renderElements(n, a, targetNode, bulkRender);
37309
37310             if(a.qtip){
37311                if(this.textNode.setAttributeNS){
37312                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37313                    if(a.qtipTitle){
37314                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37315                    }
37316                }else{
37317                    this.textNode.setAttribute("ext:qtip", a.qtip);
37318                    if(a.qtipTitle){
37319                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37320                    }
37321                }
37322             }else if(a.qtipCfg){
37323                 a.qtipCfg.target = Roo.id(this.textNode);
37324                 Roo.QuickTips.register(a.qtipCfg);
37325             }
37326             this.initEvents();
37327             if(!this.node.expanded){
37328                 this.updateExpandIcon();
37329             }
37330         }else{
37331             if(bulkRender === true) {
37332                 targetNode.appendChild(this.wrap);
37333             }
37334         }
37335     },
37336
37337     renderElements : function(n, a, targetNode, bulkRender)
37338     {
37339         // add some indent caching, this helps performance when rendering a large tree
37340         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37341         var t = n.getOwnerTree();
37342         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37343         if (typeof(n.attributes.html) != 'undefined') {
37344             txt = n.attributes.html;
37345         }
37346         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37347         var cb = typeof a.checked == 'boolean';
37348         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37349         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37350             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37351             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37352             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37353             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37354             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37355              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37356                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37357             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37358             "</li>"];
37359
37360         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37361             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37362                                 n.nextSibling.ui.getEl(), buf.join(""));
37363         }else{
37364             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37365         }
37366
37367         this.elNode = this.wrap.childNodes[0];
37368         this.ctNode = this.wrap.childNodes[1];
37369         var cs = this.elNode.childNodes;
37370         this.indentNode = cs[0];
37371         this.ecNode = cs[1];
37372         this.iconNode = cs[2];
37373         var index = 3;
37374         if(cb){
37375             this.checkbox = cs[3];
37376             index++;
37377         }
37378         this.anchor = cs[index];
37379         this.textNode = cs[index].firstChild;
37380     },
37381
37382     getAnchor : function(){
37383         return this.anchor;
37384     },
37385
37386     getTextEl : function(){
37387         return this.textNode;
37388     },
37389
37390     getIconEl : function(){
37391         return this.iconNode;
37392     },
37393
37394     isChecked : function(){
37395         return this.checkbox ? this.checkbox.checked : false;
37396     },
37397
37398     updateExpandIcon : function(){
37399         if(this.rendered){
37400             var n = this.node, c1, c2;
37401             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37402             var hasChild = n.hasChildNodes();
37403             if(hasChild){
37404                 if(n.expanded){
37405                     cls += "-minus";
37406                     c1 = "x-tree-node-collapsed";
37407                     c2 = "x-tree-node-expanded";
37408                 }else{
37409                     cls += "-plus";
37410                     c1 = "x-tree-node-expanded";
37411                     c2 = "x-tree-node-collapsed";
37412                 }
37413                 if(this.wasLeaf){
37414                     this.removeClass("x-tree-node-leaf");
37415                     this.wasLeaf = false;
37416                 }
37417                 if(this.c1 != c1 || this.c2 != c2){
37418                     Roo.fly(this.elNode).replaceClass(c1, c2);
37419                     this.c1 = c1; this.c2 = c2;
37420                 }
37421             }else{
37422                 // this changes non-leafs into leafs if they have no children.
37423                 // it's not very rational behaviour..
37424                 
37425                 if(!this.wasLeaf && this.node.leaf){
37426                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37427                     delete this.c1;
37428                     delete this.c2;
37429                     this.wasLeaf = true;
37430                 }
37431             }
37432             var ecc = "x-tree-ec-icon "+cls;
37433             if(this.ecc != ecc){
37434                 this.ecNode.className = ecc;
37435                 this.ecc = ecc;
37436             }
37437         }
37438     },
37439
37440     getChildIndent : function(){
37441         if(!this.childIndent){
37442             var buf = [];
37443             var p = this.node;
37444             while(p){
37445                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37446                     if(!p.isLast()) {
37447                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37448                     } else {
37449                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37450                     }
37451                 }
37452                 p = p.parentNode;
37453             }
37454             this.childIndent = buf.join("");
37455         }
37456         return this.childIndent;
37457     },
37458
37459     renderIndent : function(){
37460         if(this.rendered){
37461             var indent = "";
37462             var p = this.node.parentNode;
37463             if(p){
37464                 indent = p.ui.getChildIndent();
37465             }
37466             if(this.indentMarkup != indent){ // don't rerender if not required
37467                 this.indentNode.innerHTML = indent;
37468                 this.indentMarkup = indent;
37469             }
37470             this.updateExpandIcon();
37471         }
37472     }
37473 };
37474
37475 Roo.tree.RootTreeNodeUI = function(){
37476     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37477 };
37478 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37479     render : function(){
37480         if(!this.rendered){
37481             var targetNode = this.node.ownerTree.innerCt.dom;
37482             this.node.expanded = true;
37483             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37484             this.wrap = this.ctNode = targetNode.firstChild;
37485         }
37486     },
37487     collapse : function(){
37488     },
37489     expand : function(){
37490     }
37491 });/*
37492  * Based on:
37493  * Ext JS Library 1.1.1
37494  * Copyright(c) 2006-2007, Ext JS, LLC.
37495  *
37496  * Originally Released Under LGPL - original licence link has changed is not relivant.
37497  *
37498  * Fork - LGPL
37499  * <script type="text/javascript">
37500  */
37501 /**
37502  * @class Roo.tree.TreeLoader
37503  * @extends Roo.util.Observable
37504  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37505  * nodes from a specified URL. The response must be a javascript Array definition
37506  * who's elements are node definition objects. eg:
37507  * <pre><code>
37508 {  success : true,
37509    data :      [
37510    
37511     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37512     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37513     ]
37514 }
37515
37516
37517 </code></pre>
37518  * <br><br>
37519  * The old style respose with just an array is still supported, but not recommended.
37520  * <br><br>
37521  *
37522  * A server request is sent, and child nodes are loaded only when a node is expanded.
37523  * The loading node's id is passed to the server under the parameter name "node" to
37524  * enable the server to produce the correct child nodes.
37525  * <br><br>
37526  * To pass extra parameters, an event handler may be attached to the "beforeload"
37527  * event, and the parameters specified in the TreeLoader's baseParams property:
37528  * <pre><code>
37529     myTreeLoader.on("beforeload", function(treeLoader, node) {
37530         this.baseParams.category = node.attributes.category;
37531     }, this);
37532     
37533 </code></pre>
37534  *
37535  * This would pass an HTTP parameter called "category" to the server containing
37536  * the value of the Node's "category" attribute.
37537  * @constructor
37538  * Creates a new Treeloader.
37539  * @param {Object} config A config object containing config properties.
37540  */
37541 Roo.tree.TreeLoader = function(config){
37542     this.baseParams = {};
37543     this.requestMethod = "POST";
37544     Roo.apply(this, config);
37545
37546     this.addEvents({
37547     
37548         /**
37549          * @event beforeload
37550          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37551          * @param {Object} This TreeLoader object.
37552          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37553          * @param {Object} callback The callback function specified in the {@link #load} call.
37554          */
37555         beforeload : true,
37556         /**
37557          * @event load
37558          * Fires when the node has been successfuly loaded.
37559          * @param {Object} This TreeLoader object.
37560          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37561          * @param {Object} response The response object containing the data from the server.
37562          */
37563         load : true,
37564         /**
37565          * @event loadexception
37566          * Fires if the network request failed.
37567          * @param {Object} This TreeLoader object.
37568          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37569          * @param {Object} response The response object containing the data from the server.
37570          */
37571         loadexception : true,
37572         /**
37573          * @event create
37574          * Fires before a node is created, enabling you to return custom Node types 
37575          * @param {Object} This TreeLoader object.
37576          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37577          */
37578         create : true
37579     });
37580
37581     Roo.tree.TreeLoader.superclass.constructor.call(this);
37582 };
37583
37584 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37585     /**
37586     * @cfg {String} dataUrl The URL from which to request a Json string which
37587     * specifies an array of node definition object representing the child nodes
37588     * to be loaded.
37589     */
37590     /**
37591     * @cfg {String} requestMethod either GET or POST
37592     * defaults to POST (due to BC)
37593     * to be loaded.
37594     */
37595     /**
37596     * @cfg {Object} baseParams (optional) An object containing properties which
37597     * specify HTTP parameters to be passed to each request for child nodes.
37598     */
37599     /**
37600     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37601     * created by this loader. If the attributes sent by the server have an attribute in this object,
37602     * they take priority.
37603     */
37604     /**
37605     * @cfg {Object} uiProviders (optional) An object containing properties which
37606     * 
37607     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37608     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37609     * <i>uiProvider</i> attribute of a returned child node is a string rather
37610     * than a reference to a TreeNodeUI implementation, this that string value
37611     * is used as a property name in the uiProviders object. You can define the provider named
37612     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37613     */
37614     uiProviders : {},
37615
37616     /**
37617     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37618     * child nodes before loading.
37619     */
37620     clearOnLoad : true,
37621
37622     /**
37623     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37624     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37625     * Grid query { data : [ .....] }
37626     */
37627     
37628     root : false,
37629      /**
37630     * @cfg {String} queryParam (optional) 
37631     * Name of the query as it will be passed on the querystring (defaults to 'node')
37632     * eg. the request will be ?node=[id]
37633     */
37634     
37635     
37636     queryParam: false,
37637     
37638     /**
37639      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37640      * This is called automatically when a node is expanded, but may be used to reload
37641      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37642      * @param {Roo.tree.TreeNode} node
37643      * @param {Function} callback
37644      */
37645     load : function(node, callback){
37646         if(this.clearOnLoad){
37647             while(node.firstChild){
37648                 node.removeChild(node.firstChild);
37649             }
37650         }
37651         if(node.attributes.children){ // preloaded json children
37652             var cs = node.attributes.children;
37653             for(var i = 0, len = cs.length; i < len; i++){
37654                 node.appendChild(this.createNode(cs[i]));
37655             }
37656             if(typeof callback == "function"){
37657                 callback();
37658             }
37659         }else if(this.dataUrl){
37660             this.requestData(node, callback);
37661         }
37662     },
37663
37664     getParams: function(node){
37665         var buf = [], bp = this.baseParams;
37666         for(var key in bp){
37667             if(typeof bp[key] != "function"){
37668                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37669             }
37670         }
37671         var n = this.queryParam === false ? 'node' : this.queryParam;
37672         buf.push(n + "=", encodeURIComponent(node.id));
37673         return buf.join("");
37674     },
37675
37676     requestData : function(node, callback){
37677         if(this.fireEvent("beforeload", this, node, callback) !== false){
37678             this.transId = Roo.Ajax.request({
37679                 method:this.requestMethod,
37680                 url: this.dataUrl||this.url,
37681                 success: this.handleResponse,
37682                 failure: this.handleFailure,
37683                 scope: this,
37684                 argument: {callback: callback, node: node},
37685                 params: this.getParams(node)
37686             });
37687         }else{
37688             // if the load is cancelled, make sure we notify
37689             // the node that we are done
37690             if(typeof callback == "function"){
37691                 callback();
37692             }
37693         }
37694     },
37695
37696     isLoading : function(){
37697         return this.transId ? true : false;
37698     },
37699
37700     abort : function(){
37701         if(this.isLoading()){
37702             Roo.Ajax.abort(this.transId);
37703         }
37704     },
37705
37706     // private
37707     createNode : function(attr)
37708     {
37709         // apply baseAttrs, nice idea Corey!
37710         if(this.baseAttrs){
37711             Roo.applyIf(attr, this.baseAttrs);
37712         }
37713         if(this.applyLoader !== false){
37714             attr.loader = this;
37715         }
37716         // uiProvider = depreciated..
37717         
37718         if(typeof(attr.uiProvider) == 'string'){
37719            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37720                 /**  eval:var:attr */ eval(attr.uiProvider);
37721         }
37722         if(typeof(this.uiProviders['default']) != 'undefined') {
37723             attr.uiProvider = this.uiProviders['default'];
37724         }
37725         
37726         this.fireEvent('create', this, attr);
37727         
37728         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37729         return(attr.leaf ?
37730                         new Roo.tree.TreeNode(attr) :
37731                         new Roo.tree.AsyncTreeNode(attr));
37732     },
37733
37734     processResponse : function(response, node, callback)
37735     {
37736         var json = response.responseText;
37737         try {
37738             
37739             var o = Roo.decode(json);
37740             
37741             if (this.root === false && typeof(o.success) != undefined) {
37742                 this.root = 'data'; // the default behaviour for list like data..
37743                 }
37744                 
37745             if (this.root !== false &&  !o.success) {
37746                 // it's a failure condition.
37747                 var a = response.argument;
37748                 this.fireEvent("loadexception", this, a.node, response);
37749                 Roo.log("Load failed - should have a handler really");
37750                 return;
37751             }
37752             
37753             
37754             
37755             if (this.root !== false) {
37756                  o = o[this.root];
37757             }
37758             
37759             for(var i = 0, len = o.length; i < len; i++){
37760                 var n = this.createNode(o[i]);
37761                 if(n){
37762                     node.appendChild(n);
37763                 }
37764             }
37765             if(typeof callback == "function"){
37766                 callback(this, node);
37767             }
37768         }catch(e){
37769             this.handleFailure(response);
37770         }
37771     },
37772
37773     handleResponse : function(response){
37774         this.transId = false;
37775         var a = response.argument;
37776         this.processResponse(response, a.node, a.callback);
37777         this.fireEvent("load", this, a.node, response);
37778     },
37779
37780     handleFailure : function(response)
37781     {
37782         // should handle failure better..
37783         this.transId = false;
37784         var a = response.argument;
37785         this.fireEvent("loadexception", this, a.node, response);
37786         if(typeof a.callback == "function"){
37787             a.callback(this, a.node);
37788         }
37789     }
37790 });/*
37791  * Based on:
37792  * Ext JS Library 1.1.1
37793  * Copyright(c) 2006-2007, Ext JS, LLC.
37794  *
37795  * Originally Released Under LGPL - original licence link has changed is not relivant.
37796  *
37797  * Fork - LGPL
37798  * <script type="text/javascript">
37799  */
37800
37801 /**
37802 * @class Roo.tree.TreeFilter
37803 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37804 * @param {TreePanel} tree
37805 * @param {Object} config (optional)
37806  */
37807 Roo.tree.TreeFilter = function(tree, config){
37808     this.tree = tree;
37809     this.filtered = {};
37810     Roo.apply(this, config);
37811 };
37812
37813 Roo.tree.TreeFilter.prototype = {
37814     clearBlank:false,
37815     reverse:false,
37816     autoClear:false,
37817     remove:false,
37818
37819      /**
37820      * Filter the data by a specific attribute.
37821      * @param {String/RegExp} value Either string that the attribute value
37822      * should start with or a RegExp to test against the attribute
37823      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37824      * @param {TreeNode} startNode (optional) The node to start the filter at.
37825      */
37826     filter : function(value, attr, startNode){
37827         attr = attr || "text";
37828         var f;
37829         if(typeof value == "string"){
37830             var vlen = value.length;
37831             // auto clear empty filter
37832             if(vlen == 0 && this.clearBlank){
37833                 this.clear();
37834                 return;
37835             }
37836             value = value.toLowerCase();
37837             f = function(n){
37838                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37839             };
37840         }else if(value.exec){ // regex?
37841             f = function(n){
37842                 return value.test(n.attributes[attr]);
37843             };
37844         }else{
37845             throw 'Illegal filter type, must be string or regex';
37846         }
37847         this.filterBy(f, null, startNode);
37848         },
37849
37850     /**
37851      * Filter by a function. The passed function will be called with each
37852      * node in the tree (or from the startNode). If the function returns true, the node is kept
37853      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37854      * @param {Function} fn The filter function
37855      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37856      */
37857     filterBy : function(fn, scope, startNode){
37858         startNode = startNode || this.tree.root;
37859         if(this.autoClear){
37860             this.clear();
37861         }
37862         var af = this.filtered, rv = this.reverse;
37863         var f = function(n){
37864             if(n == startNode){
37865                 return true;
37866             }
37867             if(af[n.id]){
37868                 return false;
37869             }
37870             var m = fn.call(scope || n, n);
37871             if(!m || rv){
37872                 af[n.id] = n;
37873                 n.ui.hide();
37874                 return false;
37875             }
37876             return true;
37877         };
37878         startNode.cascade(f);
37879         if(this.remove){
37880            for(var id in af){
37881                if(typeof id != "function"){
37882                    var n = af[id];
37883                    if(n && n.parentNode){
37884                        n.parentNode.removeChild(n);
37885                    }
37886                }
37887            }
37888         }
37889     },
37890
37891     /**
37892      * Clears the current filter. Note: with the "remove" option
37893      * set a filter cannot be cleared.
37894      */
37895     clear : function(){
37896         var t = this.tree;
37897         var af = this.filtered;
37898         for(var id in af){
37899             if(typeof id != "function"){
37900                 var n = af[id];
37901                 if(n){
37902                     n.ui.show();
37903                 }
37904             }
37905         }
37906         this.filtered = {};
37907     }
37908 };
37909 /*
37910  * Based on:
37911  * Ext JS Library 1.1.1
37912  * Copyright(c) 2006-2007, Ext JS, LLC.
37913  *
37914  * Originally Released Under LGPL - original licence link has changed is not relivant.
37915  *
37916  * Fork - LGPL
37917  * <script type="text/javascript">
37918  */
37919  
37920
37921 /**
37922  * @class Roo.tree.TreeSorter
37923  * Provides sorting of nodes in a TreePanel
37924  * 
37925  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37926  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37927  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37928  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37929  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37930  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37931  * @constructor
37932  * @param {TreePanel} tree
37933  * @param {Object} config
37934  */
37935 Roo.tree.TreeSorter = function(tree, config){
37936     Roo.apply(this, config);
37937     tree.on("beforechildrenrendered", this.doSort, this);
37938     tree.on("append", this.updateSort, this);
37939     tree.on("insert", this.updateSort, this);
37940     
37941     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37942     var p = this.property || "text";
37943     var sortType = this.sortType;
37944     var fs = this.folderSort;
37945     var cs = this.caseSensitive === true;
37946     var leafAttr = this.leafAttr || 'leaf';
37947
37948     this.sortFn = function(n1, n2){
37949         if(fs){
37950             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37951                 return 1;
37952             }
37953             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37954                 return -1;
37955             }
37956         }
37957         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37958         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37959         if(v1 < v2){
37960                         return dsc ? +1 : -1;
37961                 }else if(v1 > v2){
37962                         return dsc ? -1 : +1;
37963         }else{
37964                 return 0;
37965         }
37966     };
37967 };
37968
37969 Roo.tree.TreeSorter.prototype = {
37970     doSort : function(node){
37971         node.sort(this.sortFn);
37972     },
37973     
37974     compareNodes : function(n1, n2){
37975         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37976     },
37977     
37978     updateSort : function(tree, node){
37979         if(node.childrenRendered){
37980             this.doSort.defer(1, this, [node]);
37981         }
37982     }
37983 };/*
37984  * Based on:
37985  * Ext JS Library 1.1.1
37986  * Copyright(c) 2006-2007, Ext JS, LLC.
37987  *
37988  * Originally Released Under LGPL - original licence link has changed is not relivant.
37989  *
37990  * Fork - LGPL
37991  * <script type="text/javascript">
37992  */
37993
37994 if(Roo.dd.DropZone){
37995     
37996 Roo.tree.TreeDropZone = function(tree, config){
37997     this.allowParentInsert = false;
37998     this.allowContainerDrop = false;
37999     this.appendOnly = false;
38000     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38001     this.tree = tree;
38002     this.lastInsertClass = "x-tree-no-status";
38003     this.dragOverData = {};
38004 };
38005
38006 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38007     ddGroup : "TreeDD",
38008     scroll:  true,
38009     
38010     expandDelay : 1000,
38011     
38012     expandNode : function(node){
38013         if(node.hasChildNodes() && !node.isExpanded()){
38014             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38015         }
38016     },
38017     
38018     queueExpand : function(node){
38019         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38020     },
38021     
38022     cancelExpand : function(){
38023         if(this.expandProcId){
38024             clearTimeout(this.expandProcId);
38025             this.expandProcId = false;
38026         }
38027     },
38028     
38029     isValidDropPoint : function(n, pt, dd, e, data){
38030         if(!n || !data){ return false; }
38031         var targetNode = n.node;
38032         var dropNode = data.node;
38033         // default drop rules
38034         if(!(targetNode && targetNode.isTarget && pt)){
38035             return false;
38036         }
38037         if(pt == "append" && targetNode.allowChildren === false){
38038             return false;
38039         }
38040         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38041             return false;
38042         }
38043         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38044             return false;
38045         }
38046         // reuse the object
38047         var overEvent = this.dragOverData;
38048         overEvent.tree = this.tree;
38049         overEvent.target = targetNode;
38050         overEvent.data = data;
38051         overEvent.point = pt;
38052         overEvent.source = dd;
38053         overEvent.rawEvent = e;
38054         overEvent.dropNode = dropNode;
38055         overEvent.cancel = false;  
38056         var result = this.tree.fireEvent("nodedragover", overEvent);
38057         return overEvent.cancel === false && result !== false;
38058     },
38059     
38060     getDropPoint : function(e, n, dd)
38061     {
38062         var tn = n.node;
38063         if(tn.isRoot){
38064             return tn.allowChildren !== false ? "append" : false; // always append for root
38065         }
38066         var dragEl = n.ddel;
38067         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38068         var y = Roo.lib.Event.getPageY(e);
38069         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38070         
38071         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38072         var noAppend = tn.allowChildren === false;
38073         if(this.appendOnly || tn.parentNode.allowChildren === false){
38074             return noAppend ? false : "append";
38075         }
38076         var noBelow = false;
38077         if(!this.allowParentInsert){
38078             noBelow = tn.hasChildNodes() && tn.isExpanded();
38079         }
38080         var q = (b - t) / (noAppend ? 2 : 3);
38081         if(y >= t && y < (t + q)){
38082             return "above";
38083         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38084             return "below";
38085         }else{
38086             return "append";
38087         }
38088     },
38089     
38090     onNodeEnter : function(n, dd, e, data)
38091     {
38092         this.cancelExpand();
38093     },
38094     
38095     onNodeOver : function(n, dd, e, data)
38096     {
38097        
38098         var pt = this.getDropPoint(e, n, dd);
38099         var node = n.node;
38100         
38101         // auto node expand check
38102         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38103             this.queueExpand(node);
38104         }else if(pt != "append"){
38105             this.cancelExpand();
38106         }
38107         
38108         // set the insert point style on the target node
38109         var returnCls = this.dropNotAllowed;
38110         if(this.isValidDropPoint(n, pt, dd, e, data)){
38111            if(pt){
38112                var el = n.ddel;
38113                var cls;
38114                if(pt == "above"){
38115                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38116                    cls = "x-tree-drag-insert-above";
38117                }else if(pt == "below"){
38118                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38119                    cls = "x-tree-drag-insert-below";
38120                }else{
38121                    returnCls = "x-tree-drop-ok-append";
38122                    cls = "x-tree-drag-append";
38123                }
38124                if(this.lastInsertClass != cls){
38125                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38126                    this.lastInsertClass = cls;
38127                }
38128            }
38129        }
38130        return returnCls;
38131     },
38132     
38133     onNodeOut : function(n, dd, e, data){
38134         
38135         this.cancelExpand();
38136         this.removeDropIndicators(n);
38137     },
38138     
38139     onNodeDrop : function(n, dd, e, data){
38140         var point = this.getDropPoint(e, n, dd);
38141         var targetNode = n.node;
38142         targetNode.ui.startDrop();
38143         if(!this.isValidDropPoint(n, point, dd, e, data)){
38144             targetNode.ui.endDrop();
38145             return false;
38146         }
38147         // first try to find the drop node
38148         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38149         var dropEvent = {
38150             tree : this.tree,
38151             target: targetNode,
38152             data: data,
38153             point: point,
38154             source: dd,
38155             rawEvent: e,
38156             dropNode: dropNode,
38157             cancel: !dropNode   
38158         };
38159         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38160         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38161             targetNode.ui.endDrop();
38162             return false;
38163         }
38164         // allow target changing
38165         targetNode = dropEvent.target;
38166         if(point == "append" && !targetNode.isExpanded()){
38167             targetNode.expand(false, null, function(){
38168                 this.completeDrop(dropEvent);
38169             }.createDelegate(this));
38170         }else{
38171             this.completeDrop(dropEvent);
38172         }
38173         return true;
38174     },
38175     
38176     completeDrop : function(de){
38177         var ns = de.dropNode, p = de.point, t = de.target;
38178         if(!(ns instanceof Array)){
38179             ns = [ns];
38180         }
38181         var n;
38182         for(var i = 0, len = ns.length; i < len; i++){
38183             n = ns[i];
38184             if(p == "above"){
38185                 t.parentNode.insertBefore(n, t);
38186             }else if(p == "below"){
38187                 t.parentNode.insertBefore(n, t.nextSibling);
38188             }else{
38189                 t.appendChild(n);
38190             }
38191         }
38192         n.ui.focus();
38193         if(this.tree.hlDrop){
38194             n.ui.highlight();
38195         }
38196         t.ui.endDrop();
38197         this.tree.fireEvent("nodedrop", de);
38198     },
38199     
38200     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38201         if(this.tree.hlDrop){
38202             dropNode.ui.focus();
38203             dropNode.ui.highlight();
38204         }
38205         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38206     },
38207     
38208     getTree : function(){
38209         return this.tree;
38210     },
38211     
38212     removeDropIndicators : function(n){
38213         if(n && n.ddel){
38214             var el = n.ddel;
38215             Roo.fly(el).removeClass([
38216                     "x-tree-drag-insert-above",
38217                     "x-tree-drag-insert-below",
38218                     "x-tree-drag-append"]);
38219             this.lastInsertClass = "_noclass";
38220         }
38221     },
38222     
38223     beforeDragDrop : function(target, e, id){
38224         this.cancelExpand();
38225         return true;
38226     },
38227     
38228     afterRepair : function(data){
38229         if(data && Roo.enableFx){
38230             data.node.ui.highlight();
38231         }
38232         this.hideProxy();
38233     } 
38234     
38235 });
38236
38237 }
38238 /*
38239  * Based on:
38240  * Ext JS Library 1.1.1
38241  * Copyright(c) 2006-2007, Ext JS, LLC.
38242  *
38243  * Originally Released Under LGPL - original licence link has changed is not relivant.
38244  *
38245  * Fork - LGPL
38246  * <script type="text/javascript">
38247  */
38248  
38249
38250 if(Roo.dd.DragZone){
38251 Roo.tree.TreeDragZone = function(tree, config){
38252     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38253     this.tree = tree;
38254 };
38255
38256 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38257     ddGroup : "TreeDD",
38258    
38259     onBeforeDrag : function(data, e){
38260         var n = data.node;
38261         return n && n.draggable && !n.disabled;
38262     },
38263      
38264     
38265     onInitDrag : function(e){
38266         var data = this.dragData;
38267         this.tree.getSelectionModel().select(data.node);
38268         this.proxy.update("");
38269         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38270         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38271     },
38272     
38273     getRepairXY : function(e, data){
38274         return data.node.ui.getDDRepairXY();
38275     },
38276     
38277     onEndDrag : function(data, e){
38278         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38279         
38280         
38281     },
38282     
38283     onValidDrop : function(dd, e, id){
38284         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38285         this.hideProxy();
38286     },
38287     
38288     beforeInvalidDrop : function(e, id){
38289         // this scrolls the original position back into view
38290         var sm = this.tree.getSelectionModel();
38291         sm.clearSelections();
38292         sm.select(this.dragData.node);
38293     }
38294 });
38295 }/*
38296  * Based on:
38297  * Ext JS Library 1.1.1
38298  * Copyright(c) 2006-2007, Ext JS, LLC.
38299  *
38300  * Originally Released Under LGPL - original licence link has changed is not relivant.
38301  *
38302  * Fork - LGPL
38303  * <script type="text/javascript">
38304  */
38305 /**
38306  * @class Roo.tree.TreeEditor
38307  * @extends Roo.Editor
38308  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38309  * as the editor field.
38310  * @constructor
38311  * @param {Object} config (used to be the tree panel.)
38312  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38313  * 
38314  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38315  * @cfg {Roo.form.TextField} field [required] The field configuration
38316  *
38317  * 
38318  */
38319 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38320     var tree = config;
38321     var field;
38322     if (oldconfig) { // old style..
38323         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38324     } else {
38325         // new style..
38326         tree = config.tree;
38327         config.field = config.field  || {};
38328         config.field.xtype = 'TextField';
38329         field = Roo.factory(config.field, Roo.form);
38330     }
38331     config = config || {};
38332     
38333     
38334     this.addEvents({
38335         /**
38336          * @event beforenodeedit
38337          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38338          * false from the handler of this event.
38339          * @param {Editor} this
38340          * @param {Roo.tree.Node} node 
38341          */
38342         "beforenodeedit" : true
38343     });
38344     
38345     //Roo.log(config);
38346     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38347
38348     this.tree = tree;
38349
38350     tree.on('beforeclick', this.beforeNodeClick, this);
38351     tree.getTreeEl().on('mousedown', this.hide, this);
38352     this.on('complete', this.updateNode, this);
38353     this.on('beforestartedit', this.fitToTree, this);
38354     this.on('startedit', this.bindScroll, this, {delay:10});
38355     this.on('specialkey', this.onSpecialKey, this);
38356 };
38357
38358 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38359     /**
38360      * @cfg {String} alignment
38361      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38362      */
38363     alignment: "l-l",
38364     // inherit
38365     autoSize: false,
38366     /**
38367      * @cfg {Boolean} hideEl
38368      * True to hide the bound element while the editor is displayed (defaults to false)
38369      */
38370     hideEl : false,
38371     /**
38372      * @cfg {String} cls
38373      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38374      */
38375     cls: "x-small-editor x-tree-editor",
38376     /**
38377      * @cfg {Boolean} shim
38378      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38379      */
38380     shim:false,
38381     // inherit
38382     shadow:"frame",
38383     /**
38384      * @cfg {Number} maxWidth
38385      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38386      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38387      * scroll and client offsets into account prior to each edit.
38388      */
38389     maxWidth: 250,
38390
38391     editDelay : 350,
38392
38393     // private
38394     fitToTree : function(ed, el){
38395         var td = this.tree.getTreeEl().dom, nd = el.dom;
38396         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38397             td.scrollLeft = nd.offsetLeft;
38398         }
38399         var w = Math.min(
38400                 this.maxWidth,
38401                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38402         this.setSize(w, '');
38403         
38404         return this.fireEvent('beforenodeedit', this, this.editNode);
38405         
38406     },
38407
38408     // private
38409     triggerEdit : function(node){
38410         this.completeEdit();
38411         this.editNode = node;
38412         this.startEdit(node.ui.textNode, node.text);
38413     },
38414
38415     // private
38416     bindScroll : function(){
38417         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38418     },
38419
38420     // private
38421     beforeNodeClick : function(node, e){
38422         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38423         this.lastClick = new Date();
38424         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38425             e.stopEvent();
38426             this.triggerEdit(node);
38427             return false;
38428         }
38429         return true;
38430     },
38431
38432     // private
38433     updateNode : function(ed, value){
38434         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38435         this.editNode.setText(value);
38436     },
38437
38438     // private
38439     onHide : function(){
38440         Roo.tree.TreeEditor.superclass.onHide.call(this);
38441         if(this.editNode){
38442             this.editNode.ui.focus();
38443         }
38444     },
38445
38446     // private
38447     onSpecialKey : function(field, e){
38448         var k = e.getKey();
38449         if(k == e.ESC){
38450             e.stopEvent();
38451             this.cancelEdit();
38452         }else if(k == e.ENTER && !e.hasModifier()){
38453             e.stopEvent();
38454             this.completeEdit();
38455         }
38456     }
38457 });//<Script type="text/javascript">
38458 /*
38459  * Based on:
38460  * Ext JS Library 1.1.1
38461  * Copyright(c) 2006-2007, Ext JS, LLC.
38462  *
38463  * Originally Released Under LGPL - original licence link has changed is not relivant.
38464  *
38465  * Fork - LGPL
38466  * <script type="text/javascript">
38467  */
38468  
38469 /**
38470  * Not documented??? - probably should be...
38471  */
38472
38473 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38474     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38475     
38476     renderElements : function(n, a, targetNode, bulkRender){
38477         //consel.log("renderElements?");
38478         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38479
38480         var t = n.getOwnerTree();
38481         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38482         
38483         var cols = t.columns;
38484         var bw = t.borderWidth;
38485         var c = cols[0];
38486         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38487          var cb = typeof a.checked == "boolean";
38488         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38489         var colcls = 'x-t-' + tid + '-c0';
38490         var buf = [
38491             '<li class="x-tree-node">',
38492             
38493                 
38494                 '<div class="x-tree-node-el ', a.cls,'">',
38495                     // extran...
38496                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38497                 
38498                 
38499                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38500                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38501                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38502                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38503                            (a.iconCls ? ' '+a.iconCls : ''),
38504                            '" unselectable="on" />',
38505                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38506                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38507                              
38508                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38509                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38510                             '<span unselectable="on" qtip="' + tx + '">',
38511                              tx,
38512                              '</span></a>' ,
38513                     '</div>',
38514                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38515                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38516                  ];
38517         for(var i = 1, len = cols.length; i < len; i++){
38518             c = cols[i];
38519             colcls = 'x-t-' + tid + '-c' +i;
38520             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38521             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38522                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38523                       "</div>");
38524          }
38525          
38526          buf.push(
38527             '</a>',
38528             '<div class="x-clear"></div></div>',
38529             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38530             "</li>");
38531         
38532         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38533             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38534                                 n.nextSibling.ui.getEl(), buf.join(""));
38535         }else{
38536             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38537         }
38538         var el = this.wrap.firstChild;
38539         this.elRow = el;
38540         this.elNode = el.firstChild;
38541         this.ranchor = el.childNodes[1];
38542         this.ctNode = this.wrap.childNodes[1];
38543         var cs = el.firstChild.childNodes;
38544         this.indentNode = cs[0];
38545         this.ecNode = cs[1];
38546         this.iconNode = cs[2];
38547         var index = 3;
38548         if(cb){
38549             this.checkbox = cs[3];
38550             index++;
38551         }
38552         this.anchor = cs[index];
38553         
38554         this.textNode = cs[index].firstChild;
38555         
38556         //el.on("click", this.onClick, this);
38557         //el.on("dblclick", this.onDblClick, this);
38558         
38559         
38560        // console.log(this);
38561     },
38562     initEvents : function(){
38563         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38564         
38565             
38566         var a = this.ranchor;
38567
38568         var el = Roo.get(a);
38569
38570         if(Roo.isOpera){ // opera render bug ignores the CSS
38571             el.setStyle("text-decoration", "none");
38572         }
38573
38574         el.on("click", this.onClick, this);
38575         el.on("dblclick", this.onDblClick, this);
38576         el.on("contextmenu", this.onContextMenu, this);
38577         
38578     },
38579     
38580     /*onSelectedChange : function(state){
38581         if(state){
38582             this.focus();
38583             this.addClass("x-tree-selected");
38584         }else{
38585             //this.blur();
38586             this.removeClass("x-tree-selected");
38587         }
38588     },*/
38589     addClass : function(cls){
38590         if(this.elRow){
38591             Roo.fly(this.elRow).addClass(cls);
38592         }
38593         
38594     },
38595     
38596     
38597     removeClass : function(cls){
38598         if(this.elRow){
38599             Roo.fly(this.elRow).removeClass(cls);
38600         }
38601     }
38602
38603     
38604     
38605 });//<Script type="text/javascript">
38606
38607 /*
38608  * Based on:
38609  * Ext JS Library 1.1.1
38610  * Copyright(c) 2006-2007, Ext JS, LLC.
38611  *
38612  * Originally Released Under LGPL - original licence link has changed is not relivant.
38613  *
38614  * Fork - LGPL
38615  * <script type="text/javascript">
38616  */
38617  
38618
38619 /**
38620  * @class Roo.tree.ColumnTree
38621  * @extends Roo.tree.TreePanel
38622  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38623  * @cfg {int} borderWidth  compined right/left border allowance
38624  * @constructor
38625  * @param {String/HTMLElement/Element} el The container element
38626  * @param {Object} config
38627  */
38628 Roo.tree.ColumnTree =  function(el, config)
38629 {
38630    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38631    this.addEvents({
38632         /**
38633         * @event resize
38634         * Fire this event on a container when it resizes
38635         * @param {int} w Width
38636         * @param {int} h Height
38637         */
38638        "resize" : true
38639     });
38640     this.on('resize', this.onResize, this);
38641 };
38642
38643 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38644     //lines:false,
38645     
38646     
38647     borderWidth: Roo.isBorderBox ? 0 : 2, 
38648     headEls : false,
38649     
38650     render : function(){
38651         // add the header.....
38652        
38653         Roo.tree.ColumnTree.superclass.render.apply(this);
38654         
38655         this.el.addClass('x-column-tree');
38656         
38657         this.headers = this.el.createChild(
38658             {cls:'x-tree-headers'},this.innerCt.dom);
38659    
38660         var cols = this.columns, c;
38661         var totalWidth = 0;
38662         this.headEls = [];
38663         var  len = cols.length;
38664         for(var i = 0; i < len; i++){
38665              c = cols[i];
38666              totalWidth += c.width;
38667             this.headEls.push(this.headers.createChild({
38668                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38669                  cn: {
38670                      cls:'x-tree-hd-text',
38671                      html: c.header
38672                  },
38673                  style:'width:'+(c.width-this.borderWidth)+'px;'
38674              }));
38675         }
38676         this.headers.createChild({cls:'x-clear'});
38677         // prevent floats from wrapping when clipped
38678         this.headers.setWidth(totalWidth);
38679         //this.innerCt.setWidth(totalWidth);
38680         this.innerCt.setStyle({ overflow: 'auto' });
38681         this.onResize(this.width, this.height);
38682              
38683         
38684     },
38685     onResize : function(w,h)
38686     {
38687         this.height = h;
38688         this.width = w;
38689         // resize cols..
38690         this.innerCt.setWidth(this.width);
38691         this.innerCt.setHeight(this.height-20);
38692         
38693         // headers...
38694         var cols = this.columns, c;
38695         var totalWidth = 0;
38696         var expEl = false;
38697         var len = cols.length;
38698         for(var i = 0; i < len; i++){
38699             c = cols[i];
38700             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38701                 // it's the expander..
38702                 expEl  = this.headEls[i];
38703                 continue;
38704             }
38705             totalWidth += c.width;
38706             
38707         }
38708         if (expEl) {
38709             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38710         }
38711         this.headers.setWidth(w-20);
38712
38713         
38714         
38715         
38716     }
38717 });
38718 /*
38719  * Based on:
38720  * Ext JS Library 1.1.1
38721  * Copyright(c) 2006-2007, Ext JS, LLC.
38722  *
38723  * Originally Released Under LGPL - original licence link has changed is not relivant.
38724  *
38725  * Fork - LGPL
38726  * <script type="text/javascript">
38727  */
38728  
38729 /**
38730  * @class Roo.menu.Menu
38731  * @extends Roo.util.Observable
38732  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38733  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38734  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38735  * @constructor
38736  * Creates a new Menu
38737  * @param {Object} config Configuration options
38738  */
38739 Roo.menu.Menu = function(config){
38740     
38741     Roo.menu.Menu.superclass.constructor.call(this, config);
38742     
38743     this.id = this.id || Roo.id();
38744     this.addEvents({
38745         /**
38746          * @event beforeshow
38747          * Fires before this menu is displayed
38748          * @param {Roo.menu.Menu} this
38749          */
38750         beforeshow : true,
38751         /**
38752          * @event beforehide
38753          * Fires before this menu is hidden
38754          * @param {Roo.menu.Menu} this
38755          */
38756         beforehide : true,
38757         /**
38758          * @event show
38759          * Fires after this menu is displayed
38760          * @param {Roo.menu.Menu} this
38761          */
38762         show : true,
38763         /**
38764          * @event hide
38765          * Fires after this menu is hidden
38766          * @param {Roo.menu.Menu} this
38767          */
38768         hide : true,
38769         /**
38770          * @event click
38771          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38772          * @param {Roo.menu.Menu} this
38773          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38774          * @param {Roo.EventObject} e
38775          */
38776         click : true,
38777         /**
38778          * @event mouseover
38779          * Fires when the mouse is hovering over this menu
38780          * @param {Roo.menu.Menu} this
38781          * @param {Roo.EventObject} e
38782          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38783          */
38784         mouseover : true,
38785         /**
38786          * @event mouseout
38787          * Fires when the mouse exits this menu
38788          * @param {Roo.menu.Menu} this
38789          * @param {Roo.EventObject} e
38790          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38791          */
38792         mouseout : true,
38793         /**
38794          * @event itemclick
38795          * Fires when a menu item contained in this menu is clicked
38796          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38797          * @param {Roo.EventObject} e
38798          */
38799         itemclick: true
38800     });
38801     if (this.registerMenu) {
38802         Roo.menu.MenuMgr.register(this);
38803     }
38804     
38805     var mis = this.items;
38806     this.items = new Roo.util.MixedCollection();
38807     if(mis){
38808         this.add.apply(this, mis);
38809     }
38810 };
38811
38812 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38813     /**
38814      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38815      */
38816     minWidth : 120,
38817     /**
38818      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38819      * for bottom-right shadow (defaults to "sides")
38820      */
38821     shadow : "sides",
38822     /**
38823      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38824      * this menu (defaults to "tl-tr?")
38825      */
38826     subMenuAlign : "tl-tr?",
38827     /**
38828      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38829      * relative to its element of origin (defaults to "tl-bl?")
38830      */
38831     defaultAlign : "tl-bl?",
38832     /**
38833      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38834      */
38835     allowOtherMenus : false,
38836     /**
38837      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38838      */
38839     registerMenu : true,
38840
38841     hidden:true,
38842
38843     // private
38844     render : function(){
38845         if(this.el){
38846             return;
38847         }
38848         var el = this.el = new Roo.Layer({
38849             cls: "x-menu",
38850             shadow:this.shadow,
38851             constrain: false,
38852             parentEl: this.parentEl || document.body,
38853             zindex:15000
38854         });
38855
38856         this.keyNav = new Roo.menu.MenuNav(this);
38857
38858         if(this.plain){
38859             el.addClass("x-menu-plain");
38860         }
38861         if(this.cls){
38862             el.addClass(this.cls);
38863         }
38864         // generic focus element
38865         this.focusEl = el.createChild({
38866             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38867         });
38868         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38869         //disabling touch- as it's causing issues ..
38870         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38871         ul.on('click'   , this.onClick, this);
38872         
38873         
38874         ul.on("mouseover", this.onMouseOver, this);
38875         ul.on("mouseout", this.onMouseOut, this);
38876         this.items.each(function(item){
38877             if (item.hidden) {
38878                 return;
38879             }
38880             
38881             var li = document.createElement("li");
38882             li.className = "x-menu-list-item";
38883             ul.dom.appendChild(li);
38884             item.render(li, this);
38885         }, this);
38886         this.ul = ul;
38887         this.autoWidth();
38888     },
38889
38890     // private
38891     autoWidth : function(){
38892         var el = this.el, ul = this.ul;
38893         if(!el){
38894             return;
38895         }
38896         var w = this.width;
38897         if(w){
38898             el.setWidth(w);
38899         }else if(Roo.isIE){
38900             el.setWidth(this.minWidth);
38901             var t = el.dom.offsetWidth; // force recalc
38902             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38903         }
38904     },
38905
38906     // private
38907     delayAutoWidth : function(){
38908         if(this.rendered){
38909             if(!this.awTask){
38910                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38911             }
38912             this.awTask.delay(20);
38913         }
38914     },
38915
38916     // private
38917     findTargetItem : function(e){
38918         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38919         if(t && t.menuItemId){
38920             return this.items.get(t.menuItemId);
38921         }
38922     },
38923
38924     // private
38925     onClick : function(e){
38926         Roo.log("menu.onClick");
38927         var t = this.findTargetItem(e);
38928         if(!t){
38929             return;
38930         }
38931         Roo.log(e);
38932         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38933             if(t == this.activeItem && t.shouldDeactivate(e)){
38934                 this.activeItem.deactivate();
38935                 delete this.activeItem;
38936                 return;
38937             }
38938             if(t.canActivate){
38939                 this.setActiveItem(t, true);
38940             }
38941             return;
38942             
38943             
38944         }
38945         
38946         t.onClick(e);
38947         this.fireEvent("click", this, t, e);
38948     },
38949
38950     // private
38951     setActiveItem : function(item, autoExpand){
38952         if(item != this.activeItem){
38953             if(this.activeItem){
38954                 this.activeItem.deactivate();
38955             }
38956             this.activeItem = item;
38957             item.activate(autoExpand);
38958         }else if(autoExpand){
38959             item.expandMenu();
38960         }
38961     },
38962
38963     // private
38964     tryActivate : function(start, step){
38965         var items = this.items;
38966         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38967             var item = items.get(i);
38968             if(!item.disabled && item.canActivate){
38969                 this.setActiveItem(item, false);
38970                 return item;
38971             }
38972         }
38973         return false;
38974     },
38975
38976     // private
38977     onMouseOver : function(e){
38978         var t;
38979         if(t = this.findTargetItem(e)){
38980             if(t.canActivate && !t.disabled){
38981                 this.setActiveItem(t, true);
38982             }
38983         }
38984         this.fireEvent("mouseover", this, e, t);
38985     },
38986
38987     // private
38988     onMouseOut : function(e){
38989         var t;
38990         if(t = this.findTargetItem(e)){
38991             if(t == this.activeItem && t.shouldDeactivate(e)){
38992                 this.activeItem.deactivate();
38993                 delete this.activeItem;
38994             }
38995         }
38996         this.fireEvent("mouseout", this, e, t);
38997     },
38998
38999     /**
39000      * Read-only.  Returns true if the menu is currently displayed, else false.
39001      * @type Boolean
39002      */
39003     isVisible : function(){
39004         return this.el && !this.hidden;
39005     },
39006
39007     /**
39008      * Displays this menu relative to another element
39009      * @param {String/HTMLElement/Roo.Element} element The element to align to
39010      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39011      * the element (defaults to this.defaultAlign)
39012      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39013      */
39014     show : function(el, pos, parentMenu){
39015         this.parentMenu = parentMenu;
39016         if(!this.el){
39017             this.render();
39018         }
39019         this.fireEvent("beforeshow", this);
39020         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39021     },
39022
39023     /**
39024      * Displays this menu at a specific xy position
39025      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39026      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39027      */
39028     showAt : function(xy, parentMenu, /* private: */_e){
39029         this.parentMenu = parentMenu;
39030         if(!this.el){
39031             this.render();
39032         }
39033         if(_e !== false){
39034             this.fireEvent("beforeshow", this);
39035             xy = this.el.adjustForConstraints(xy);
39036         }
39037         this.el.setXY(xy);
39038         this.el.show();
39039         this.hidden = false;
39040         this.focus();
39041         this.fireEvent("show", this);
39042     },
39043
39044     focus : function(){
39045         if(!this.hidden){
39046             this.doFocus.defer(50, this);
39047         }
39048     },
39049
39050     doFocus : function(){
39051         if(!this.hidden){
39052             this.focusEl.focus();
39053         }
39054     },
39055
39056     /**
39057      * Hides this menu and optionally all parent menus
39058      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39059      */
39060     hide : function(deep){
39061         if(this.el && this.isVisible()){
39062             this.fireEvent("beforehide", this);
39063             if(this.activeItem){
39064                 this.activeItem.deactivate();
39065                 this.activeItem = null;
39066             }
39067             this.el.hide();
39068             this.hidden = true;
39069             this.fireEvent("hide", this);
39070         }
39071         if(deep === true && this.parentMenu){
39072             this.parentMenu.hide(true);
39073         }
39074     },
39075
39076     /**
39077      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39078      * Any of the following are valid:
39079      * <ul>
39080      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39081      * <li>An HTMLElement object which will be converted to a menu item</li>
39082      * <li>A menu item config object that will be created as a new menu item</li>
39083      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39084      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39085      * </ul>
39086      * Usage:
39087      * <pre><code>
39088 // Create the menu
39089 var menu = new Roo.menu.Menu();
39090
39091 // Create a menu item to add by reference
39092 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39093
39094 // Add a bunch of items at once using different methods.
39095 // Only the last item added will be returned.
39096 var item = menu.add(
39097     menuItem,                // add existing item by ref
39098     'Dynamic Item',          // new TextItem
39099     '-',                     // new separator
39100     { text: 'Config Item' }  // new item by config
39101 );
39102 </code></pre>
39103      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39104      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39105      */
39106     add : function(){
39107         var a = arguments, l = a.length, item;
39108         for(var i = 0; i < l; i++){
39109             var el = a[i];
39110             if ((typeof(el) == "object") && el.xtype && el.xns) {
39111                 el = Roo.factory(el, Roo.menu);
39112             }
39113             
39114             if(el.render){ // some kind of Item
39115                 item = this.addItem(el);
39116             }else if(typeof el == "string"){ // string
39117                 if(el == "separator" || el == "-"){
39118                     item = this.addSeparator();
39119                 }else{
39120                     item = this.addText(el);
39121                 }
39122             }else if(el.tagName || el.el){ // element
39123                 item = this.addElement(el);
39124             }else if(typeof el == "object"){ // must be menu item config?
39125                 item = this.addMenuItem(el);
39126             }
39127         }
39128         return item;
39129     },
39130
39131     /**
39132      * Returns this menu's underlying {@link Roo.Element} object
39133      * @return {Roo.Element} The element
39134      */
39135     getEl : function(){
39136         if(!this.el){
39137             this.render();
39138         }
39139         return this.el;
39140     },
39141
39142     /**
39143      * Adds a separator bar to the menu
39144      * @return {Roo.menu.Item} The menu item that was added
39145      */
39146     addSeparator : function(){
39147         return this.addItem(new Roo.menu.Separator());
39148     },
39149
39150     /**
39151      * Adds an {@link Roo.Element} object to the menu
39152      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39153      * @return {Roo.menu.Item} The menu item that was added
39154      */
39155     addElement : function(el){
39156         return this.addItem(new Roo.menu.BaseItem(el));
39157     },
39158
39159     /**
39160      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39161      * @param {Roo.menu.Item} item The menu item to add
39162      * @return {Roo.menu.Item} The menu item that was added
39163      */
39164     addItem : function(item){
39165         this.items.add(item);
39166         if(this.ul){
39167             var li = document.createElement("li");
39168             li.className = "x-menu-list-item";
39169             this.ul.dom.appendChild(li);
39170             item.render(li, this);
39171             this.delayAutoWidth();
39172         }
39173         return item;
39174     },
39175
39176     /**
39177      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39178      * @param {Object} config A MenuItem config object
39179      * @return {Roo.menu.Item} The menu item that was added
39180      */
39181     addMenuItem : function(config){
39182         if(!(config instanceof Roo.menu.Item)){
39183             if(typeof config.checked == "boolean"){ // must be check menu item config?
39184                 config = new Roo.menu.CheckItem(config);
39185             }else{
39186                 config = new Roo.menu.Item(config);
39187             }
39188         }
39189         return this.addItem(config);
39190     },
39191
39192     /**
39193      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39194      * @param {String} text The text to display in the menu item
39195      * @return {Roo.menu.Item} The menu item that was added
39196      */
39197     addText : function(text){
39198         return this.addItem(new Roo.menu.TextItem({ text : text }));
39199     },
39200
39201     /**
39202      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39203      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39204      * @param {Roo.menu.Item} item The menu item to add
39205      * @return {Roo.menu.Item} The menu item that was added
39206      */
39207     insert : function(index, item){
39208         this.items.insert(index, item);
39209         if(this.ul){
39210             var li = document.createElement("li");
39211             li.className = "x-menu-list-item";
39212             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39213             item.render(li, this);
39214             this.delayAutoWidth();
39215         }
39216         return item;
39217     },
39218
39219     /**
39220      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39221      * @param {Roo.menu.Item} item The menu item to remove
39222      */
39223     remove : function(item){
39224         this.items.removeKey(item.id);
39225         item.destroy();
39226     },
39227
39228     /**
39229      * Removes and destroys all items in the menu
39230      */
39231     removeAll : function(){
39232         var f;
39233         while(f = this.items.first()){
39234             this.remove(f);
39235         }
39236     }
39237 });
39238
39239 // MenuNav is a private utility class used internally by the Menu
39240 Roo.menu.MenuNav = function(menu){
39241     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39242     this.scope = this.menu = menu;
39243 };
39244
39245 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39246     doRelay : function(e, h){
39247         var k = e.getKey();
39248         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39249             this.menu.tryActivate(0, 1);
39250             return false;
39251         }
39252         return h.call(this.scope || this, e, this.menu);
39253     },
39254
39255     up : function(e, m){
39256         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39257             m.tryActivate(m.items.length-1, -1);
39258         }
39259     },
39260
39261     down : function(e, m){
39262         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39263             m.tryActivate(0, 1);
39264         }
39265     },
39266
39267     right : function(e, m){
39268         if(m.activeItem){
39269             m.activeItem.expandMenu(true);
39270         }
39271     },
39272
39273     left : function(e, m){
39274         m.hide();
39275         if(m.parentMenu && m.parentMenu.activeItem){
39276             m.parentMenu.activeItem.activate();
39277         }
39278     },
39279
39280     enter : function(e, m){
39281         if(m.activeItem){
39282             e.stopPropagation();
39283             m.activeItem.onClick(e);
39284             m.fireEvent("click", this, m.activeItem);
39285             return true;
39286         }
39287     }
39288 });/*
39289  * Based on:
39290  * Ext JS Library 1.1.1
39291  * Copyright(c) 2006-2007, Ext JS, LLC.
39292  *
39293  * Originally Released Under LGPL - original licence link has changed is not relivant.
39294  *
39295  * Fork - LGPL
39296  * <script type="text/javascript">
39297  */
39298  
39299 /**
39300  * @class Roo.menu.MenuMgr
39301  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39302  * @static
39303  */
39304 Roo.menu.MenuMgr = function(){
39305    var menus, active, groups = {}, attached = false, lastShow = new Date();
39306
39307    // private - called when first menu is created
39308    function init(){
39309        menus = {};
39310        active = new Roo.util.MixedCollection();
39311        Roo.get(document).addKeyListener(27, function(){
39312            if(active.length > 0){
39313                hideAll();
39314            }
39315        });
39316    }
39317
39318    // private
39319    function hideAll(){
39320        if(active && active.length > 0){
39321            var c = active.clone();
39322            c.each(function(m){
39323                m.hide();
39324            });
39325        }
39326    }
39327
39328    // private
39329    function onHide(m){
39330        active.remove(m);
39331        if(active.length < 1){
39332            Roo.get(document).un("mousedown", onMouseDown);
39333            attached = false;
39334        }
39335    }
39336
39337    // private
39338    function onShow(m){
39339        var last = active.last();
39340        lastShow = new Date();
39341        active.add(m);
39342        if(!attached){
39343            Roo.get(document).on("mousedown", onMouseDown);
39344            attached = true;
39345        }
39346        if(m.parentMenu){
39347           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39348           m.parentMenu.activeChild = m;
39349        }else if(last && last.isVisible()){
39350           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39351        }
39352    }
39353
39354    // private
39355    function onBeforeHide(m){
39356        if(m.activeChild){
39357            m.activeChild.hide();
39358        }
39359        if(m.autoHideTimer){
39360            clearTimeout(m.autoHideTimer);
39361            delete m.autoHideTimer;
39362        }
39363    }
39364
39365    // private
39366    function onBeforeShow(m){
39367        var pm = m.parentMenu;
39368        if(!pm && !m.allowOtherMenus){
39369            hideAll();
39370        }else if(pm && pm.activeChild && active != m){
39371            pm.activeChild.hide();
39372        }
39373    }
39374
39375    // private
39376    function onMouseDown(e){
39377        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39378            hideAll();
39379        }
39380    }
39381
39382    // private
39383    function onBeforeCheck(mi, state){
39384        if(state){
39385            var g = groups[mi.group];
39386            for(var i = 0, l = g.length; i < l; i++){
39387                if(g[i] != mi){
39388                    g[i].setChecked(false);
39389                }
39390            }
39391        }
39392    }
39393
39394    return {
39395
39396        /**
39397         * Hides all menus that are currently visible
39398         */
39399        hideAll : function(){
39400             hideAll();  
39401        },
39402
39403        // private
39404        register : function(menu){
39405            if(!menus){
39406                init();
39407            }
39408            menus[menu.id] = menu;
39409            menu.on("beforehide", onBeforeHide);
39410            menu.on("hide", onHide);
39411            menu.on("beforeshow", onBeforeShow);
39412            menu.on("show", onShow);
39413            var g = menu.group;
39414            if(g && menu.events["checkchange"]){
39415                if(!groups[g]){
39416                    groups[g] = [];
39417                }
39418                groups[g].push(menu);
39419                menu.on("checkchange", onCheck);
39420            }
39421        },
39422
39423         /**
39424          * Returns a {@link Roo.menu.Menu} object
39425          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39426          * be used to generate and return a new Menu instance.
39427          */
39428        get : function(menu){
39429            if(typeof menu == "string"){ // menu id
39430                return menus[menu];
39431            }else if(menu.events){  // menu instance
39432                return menu;
39433            }else if(typeof menu.length == 'number'){ // array of menu items?
39434                return new Roo.menu.Menu({items:menu});
39435            }else{ // otherwise, must be a config
39436                return new Roo.menu.Menu(menu);
39437            }
39438        },
39439
39440        // private
39441        unregister : function(menu){
39442            delete menus[menu.id];
39443            menu.un("beforehide", onBeforeHide);
39444            menu.un("hide", onHide);
39445            menu.un("beforeshow", onBeforeShow);
39446            menu.un("show", onShow);
39447            var g = menu.group;
39448            if(g && menu.events["checkchange"]){
39449                groups[g].remove(menu);
39450                menu.un("checkchange", onCheck);
39451            }
39452        },
39453
39454        // private
39455        registerCheckable : function(menuItem){
39456            var g = menuItem.group;
39457            if(g){
39458                if(!groups[g]){
39459                    groups[g] = [];
39460                }
39461                groups[g].push(menuItem);
39462                menuItem.on("beforecheckchange", onBeforeCheck);
39463            }
39464        },
39465
39466        // private
39467        unregisterCheckable : function(menuItem){
39468            var g = menuItem.group;
39469            if(g){
39470                groups[g].remove(menuItem);
39471                menuItem.un("beforecheckchange", onBeforeCheck);
39472            }
39473        }
39474    };
39475 }();/*
39476  * Based on:
39477  * Ext JS Library 1.1.1
39478  * Copyright(c) 2006-2007, Ext JS, LLC.
39479  *
39480  * Originally Released Under LGPL - original licence link has changed is not relivant.
39481  *
39482  * Fork - LGPL
39483  * <script type="text/javascript">
39484  */
39485  
39486
39487 /**
39488  * @class Roo.menu.BaseItem
39489  * @extends Roo.Component
39490  * @abstract
39491  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39492  * management and base configuration options shared by all menu components.
39493  * @constructor
39494  * Creates a new BaseItem
39495  * @param {Object} config Configuration options
39496  */
39497 Roo.menu.BaseItem = function(config){
39498     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39499
39500     this.addEvents({
39501         /**
39502          * @event click
39503          * Fires when this item is clicked
39504          * @param {Roo.menu.BaseItem} this
39505          * @param {Roo.EventObject} e
39506          */
39507         click: true,
39508         /**
39509          * @event activate
39510          * Fires when this item is activated
39511          * @param {Roo.menu.BaseItem} this
39512          */
39513         activate : true,
39514         /**
39515          * @event deactivate
39516          * Fires when this item is deactivated
39517          * @param {Roo.menu.BaseItem} this
39518          */
39519         deactivate : true
39520     });
39521
39522     if(this.handler){
39523         this.on("click", this.handler, this.scope, true);
39524     }
39525 };
39526
39527 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39528     /**
39529      * @cfg {Function} handler
39530      * A function that will handle the click event of this menu item (defaults to undefined)
39531      */
39532     /**
39533      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39534      */
39535     canActivate : false,
39536     
39537      /**
39538      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39539      */
39540     hidden: false,
39541     
39542     /**
39543      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39544      */
39545     activeClass : "x-menu-item-active",
39546     /**
39547      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39548      */
39549     hideOnClick : true,
39550     /**
39551      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39552      */
39553     hideDelay : 100,
39554
39555     // private
39556     ctype: "Roo.menu.BaseItem",
39557
39558     // private
39559     actionMode : "container",
39560
39561     // private
39562     render : function(container, parentMenu){
39563         this.parentMenu = parentMenu;
39564         Roo.menu.BaseItem.superclass.render.call(this, container);
39565         this.container.menuItemId = this.id;
39566     },
39567
39568     // private
39569     onRender : function(container, position){
39570         this.el = Roo.get(this.el);
39571         container.dom.appendChild(this.el.dom);
39572     },
39573
39574     // private
39575     onClick : function(e){
39576         if(!this.disabled && this.fireEvent("click", this, e) !== false
39577                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39578             this.handleClick(e);
39579         }else{
39580             e.stopEvent();
39581         }
39582     },
39583
39584     // private
39585     activate : function(){
39586         if(this.disabled){
39587             return false;
39588         }
39589         var li = this.container;
39590         li.addClass(this.activeClass);
39591         this.region = li.getRegion().adjust(2, 2, -2, -2);
39592         this.fireEvent("activate", this);
39593         return true;
39594     },
39595
39596     // private
39597     deactivate : function(){
39598         this.container.removeClass(this.activeClass);
39599         this.fireEvent("deactivate", this);
39600     },
39601
39602     // private
39603     shouldDeactivate : function(e){
39604         return !this.region || !this.region.contains(e.getPoint());
39605     },
39606
39607     // private
39608     handleClick : function(e){
39609         if(this.hideOnClick){
39610             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39611         }
39612     },
39613
39614     // private
39615     expandMenu : function(autoActivate){
39616         // do nothing
39617     },
39618
39619     // private
39620     hideMenu : function(){
39621         // do nothing
39622     }
39623 });/*
39624  * Based on:
39625  * Ext JS Library 1.1.1
39626  * Copyright(c) 2006-2007, Ext JS, LLC.
39627  *
39628  * Originally Released Under LGPL - original licence link has changed is not relivant.
39629  *
39630  * Fork - LGPL
39631  * <script type="text/javascript">
39632  */
39633  
39634 /**
39635  * @class Roo.menu.Adapter
39636  * @extends Roo.menu.BaseItem
39637  * @abstract
39638  * 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.
39639  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39640  * @constructor
39641  * Creates a new Adapter
39642  * @param {Object} config Configuration options
39643  */
39644 Roo.menu.Adapter = function(component, config){
39645     Roo.menu.Adapter.superclass.constructor.call(this, config);
39646     this.component = component;
39647 };
39648 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39649     // private
39650     canActivate : true,
39651
39652     // private
39653     onRender : function(container, position){
39654         this.component.render(container);
39655         this.el = this.component.getEl();
39656     },
39657
39658     // private
39659     activate : function(){
39660         if(this.disabled){
39661             return false;
39662         }
39663         this.component.focus();
39664         this.fireEvent("activate", this);
39665         return true;
39666     },
39667
39668     // private
39669     deactivate : function(){
39670         this.fireEvent("deactivate", this);
39671     },
39672
39673     // private
39674     disable : function(){
39675         this.component.disable();
39676         Roo.menu.Adapter.superclass.disable.call(this);
39677     },
39678
39679     // private
39680     enable : function(){
39681         this.component.enable();
39682         Roo.menu.Adapter.superclass.enable.call(this);
39683     }
39684 });/*
39685  * Based on:
39686  * Ext JS Library 1.1.1
39687  * Copyright(c) 2006-2007, Ext JS, LLC.
39688  *
39689  * Originally Released Under LGPL - original licence link has changed is not relivant.
39690  *
39691  * Fork - LGPL
39692  * <script type="text/javascript">
39693  */
39694
39695 /**
39696  * @class Roo.menu.TextItem
39697  * @extends Roo.menu.BaseItem
39698  * Adds a static text string to a menu, usually used as either a heading or group separator.
39699  * Note: old style constructor with text is still supported.
39700  * 
39701  * @constructor
39702  * Creates a new TextItem
39703  * @param {Object} cfg Configuration
39704  */
39705 Roo.menu.TextItem = function(cfg){
39706     if (typeof(cfg) == 'string') {
39707         this.text = cfg;
39708     } else {
39709         Roo.apply(this,cfg);
39710     }
39711     
39712     Roo.menu.TextItem.superclass.constructor.call(this);
39713 };
39714
39715 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39716     /**
39717      * @cfg {String} text Text to show on item.
39718      */
39719     text : '',
39720     
39721     /**
39722      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39723      */
39724     hideOnClick : false,
39725     /**
39726      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39727      */
39728     itemCls : "x-menu-text",
39729
39730     // private
39731     onRender : function(){
39732         var s = document.createElement("span");
39733         s.className = this.itemCls;
39734         s.innerHTML = this.text;
39735         this.el = s;
39736         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39737     }
39738 });/*
39739  * Based on:
39740  * Ext JS Library 1.1.1
39741  * Copyright(c) 2006-2007, Ext JS, LLC.
39742  *
39743  * Originally Released Under LGPL - original licence link has changed is not relivant.
39744  *
39745  * Fork - LGPL
39746  * <script type="text/javascript">
39747  */
39748
39749 /**
39750  * @class Roo.menu.Separator
39751  * @extends Roo.menu.BaseItem
39752  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39753  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39754  * @constructor
39755  * @param {Object} config Configuration options
39756  */
39757 Roo.menu.Separator = function(config){
39758     Roo.menu.Separator.superclass.constructor.call(this, config);
39759 };
39760
39761 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39762     /**
39763      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39764      */
39765     itemCls : "x-menu-sep",
39766     /**
39767      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39768      */
39769     hideOnClick : false,
39770
39771     // private
39772     onRender : function(li){
39773         var s = document.createElement("span");
39774         s.className = this.itemCls;
39775         s.innerHTML = "&#160;";
39776         this.el = s;
39777         li.addClass("x-menu-sep-li");
39778         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39779     }
39780 });/*
39781  * Based on:
39782  * Ext JS Library 1.1.1
39783  * Copyright(c) 2006-2007, Ext JS, LLC.
39784  *
39785  * Originally Released Under LGPL - original licence link has changed is not relivant.
39786  *
39787  * Fork - LGPL
39788  * <script type="text/javascript">
39789  */
39790 /**
39791  * @class Roo.menu.Item
39792  * @extends Roo.menu.BaseItem
39793  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39794  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39795  * activation and click handling.
39796  * @constructor
39797  * Creates a new Item
39798  * @param {Object} config Configuration options
39799  */
39800 Roo.menu.Item = function(config){
39801     Roo.menu.Item.superclass.constructor.call(this, config);
39802     if(this.menu){
39803         this.menu = Roo.menu.MenuMgr.get(this.menu);
39804     }
39805 };
39806 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39807     /**
39808      * @cfg {Roo.menu.Menu} menu
39809      * A Sub menu
39810      */
39811     /**
39812      * @cfg {String} text
39813      * The text to show on the menu item.
39814      */
39815     text: '',
39816      /**
39817      * @cfg {String} HTML to render in menu
39818      * The text to show on the menu item (HTML version).
39819      */
39820     html: '',
39821     /**
39822      * @cfg {String} icon
39823      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39824      */
39825     icon: undefined,
39826     /**
39827      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39828      */
39829     itemCls : "x-menu-item",
39830     /**
39831      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39832      */
39833     canActivate : true,
39834     /**
39835      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39836      */
39837     showDelay: 200,
39838     // doc'd in BaseItem
39839     hideDelay: 200,
39840
39841     // private
39842     ctype: "Roo.menu.Item",
39843     
39844     // private
39845     onRender : function(container, position){
39846         var el = document.createElement("a");
39847         el.hideFocus = true;
39848         el.unselectable = "on";
39849         el.href = this.href || "#";
39850         if(this.hrefTarget){
39851             el.target = this.hrefTarget;
39852         }
39853         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39854         
39855         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39856         
39857         el.innerHTML = String.format(
39858                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39859                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39860         this.el = el;
39861         Roo.menu.Item.superclass.onRender.call(this, container, position);
39862     },
39863
39864     /**
39865      * Sets the text to display in this menu item
39866      * @param {String} text The text to display
39867      * @param {Boolean} isHTML true to indicate text is pure html.
39868      */
39869     setText : function(text, isHTML){
39870         if (isHTML) {
39871             this.html = text;
39872         } else {
39873             this.text = text;
39874             this.html = '';
39875         }
39876         if(this.rendered){
39877             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39878      
39879             this.el.update(String.format(
39880                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39881                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39882             this.parentMenu.autoWidth();
39883         }
39884     },
39885
39886     // private
39887     handleClick : function(e){
39888         if(!this.href){ // if no link defined, stop the event automatically
39889             e.stopEvent();
39890         }
39891         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39892     },
39893
39894     // private
39895     activate : function(autoExpand){
39896         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39897             this.focus();
39898             if(autoExpand){
39899                 this.expandMenu();
39900             }
39901         }
39902         return true;
39903     },
39904
39905     // private
39906     shouldDeactivate : function(e){
39907         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39908             if(this.menu && this.menu.isVisible()){
39909                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39910             }
39911             return true;
39912         }
39913         return false;
39914     },
39915
39916     // private
39917     deactivate : function(){
39918         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39919         this.hideMenu();
39920     },
39921
39922     // private
39923     expandMenu : function(autoActivate){
39924         if(!this.disabled && this.menu){
39925             clearTimeout(this.hideTimer);
39926             delete this.hideTimer;
39927             if(!this.menu.isVisible() && !this.showTimer){
39928                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39929             }else if (this.menu.isVisible() && autoActivate){
39930                 this.menu.tryActivate(0, 1);
39931             }
39932         }
39933     },
39934
39935     // private
39936     deferExpand : function(autoActivate){
39937         delete this.showTimer;
39938         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39939         if(autoActivate){
39940             this.menu.tryActivate(0, 1);
39941         }
39942     },
39943
39944     // private
39945     hideMenu : function(){
39946         clearTimeout(this.showTimer);
39947         delete this.showTimer;
39948         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39949             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39950         }
39951     },
39952
39953     // private
39954     deferHide : function(){
39955         delete this.hideTimer;
39956         this.menu.hide();
39957     }
39958 });/*
39959  * Based on:
39960  * Ext JS Library 1.1.1
39961  * Copyright(c) 2006-2007, Ext JS, LLC.
39962  *
39963  * Originally Released Under LGPL - original licence link has changed is not relivant.
39964  *
39965  * Fork - LGPL
39966  * <script type="text/javascript">
39967  */
39968  
39969 /**
39970  * @class Roo.menu.CheckItem
39971  * @extends Roo.menu.Item
39972  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39973  * @constructor
39974  * Creates a new CheckItem
39975  * @param {Object} config Configuration options
39976  */
39977 Roo.menu.CheckItem = function(config){
39978     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39979     this.addEvents({
39980         /**
39981          * @event beforecheckchange
39982          * Fires before the checked value is set, providing an opportunity to cancel if needed
39983          * @param {Roo.menu.CheckItem} this
39984          * @param {Boolean} checked The new checked value that will be set
39985          */
39986         "beforecheckchange" : true,
39987         /**
39988          * @event checkchange
39989          * Fires after the checked value has been set
39990          * @param {Roo.menu.CheckItem} this
39991          * @param {Boolean} checked The checked value that was set
39992          */
39993         "checkchange" : true
39994     });
39995     if(this.checkHandler){
39996         this.on('checkchange', this.checkHandler, this.scope);
39997     }
39998 };
39999 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40000     /**
40001      * @cfg {String} group
40002      * All check items with the same group name will automatically be grouped into a single-select
40003      * radio button group (defaults to '')
40004      */
40005     /**
40006      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40007      */
40008     itemCls : "x-menu-item x-menu-check-item",
40009     /**
40010      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40011      */
40012     groupClass : "x-menu-group-item",
40013
40014     /**
40015      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40016      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40017      * initialized with checked = true will be rendered as checked.
40018      */
40019     checked: false,
40020
40021     // private
40022     ctype: "Roo.menu.CheckItem",
40023
40024     // private
40025     onRender : function(c){
40026         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40027         if(this.group){
40028             this.el.addClass(this.groupClass);
40029         }
40030         Roo.menu.MenuMgr.registerCheckable(this);
40031         if(this.checked){
40032             this.checked = false;
40033             this.setChecked(true, true);
40034         }
40035     },
40036
40037     // private
40038     destroy : function(){
40039         if(this.rendered){
40040             Roo.menu.MenuMgr.unregisterCheckable(this);
40041         }
40042         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40043     },
40044
40045     /**
40046      * Set the checked state of this item
40047      * @param {Boolean} checked The new checked value
40048      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40049      */
40050     setChecked : function(state, suppressEvent){
40051         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40052             if(this.container){
40053                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40054             }
40055             this.checked = state;
40056             if(suppressEvent !== true){
40057                 this.fireEvent("checkchange", this, state);
40058             }
40059         }
40060     },
40061
40062     // private
40063     handleClick : function(e){
40064        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40065            this.setChecked(!this.checked);
40066        }
40067        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40068     }
40069 });/*
40070  * Based on:
40071  * Ext JS Library 1.1.1
40072  * Copyright(c) 2006-2007, Ext JS, LLC.
40073  *
40074  * Originally Released Under LGPL - original licence link has changed is not relivant.
40075  *
40076  * Fork - LGPL
40077  * <script type="text/javascript">
40078  */
40079  
40080 /**
40081  * @class Roo.menu.DateItem
40082  * @extends Roo.menu.Adapter
40083  * A menu item that wraps the {@link Roo.DatPicker} component.
40084  * @constructor
40085  * Creates a new DateItem
40086  * @param {Object} config Configuration options
40087  */
40088 Roo.menu.DateItem = function(config){
40089     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40090     /** The Roo.DatePicker object @type Roo.DatePicker */
40091     this.picker = this.component;
40092     this.addEvents({select: true});
40093     
40094     this.picker.on("render", function(picker){
40095         picker.getEl().swallowEvent("click");
40096         picker.container.addClass("x-menu-date-item");
40097     });
40098
40099     this.picker.on("select", this.onSelect, this);
40100 };
40101
40102 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40103     // private
40104     onSelect : function(picker, date){
40105         this.fireEvent("select", this, date, picker);
40106         Roo.menu.DateItem.superclass.handleClick.call(this);
40107     }
40108 });/*
40109  * Based on:
40110  * Ext JS Library 1.1.1
40111  * Copyright(c) 2006-2007, Ext JS, LLC.
40112  *
40113  * Originally Released Under LGPL - original licence link has changed is not relivant.
40114  *
40115  * Fork - LGPL
40116  * <script type="text/javascript">
40117  */
40118  
40119 /**
40120  * @class Roo.menu.ColorItem
40121  * @extends Roo.menu.Adapter
40122  * A menu item that wraps the {@link Roo.ColorPalette} component.
40123  * @constructor
40124  * Creates a new ColorItem
40125  * @param {Object} config Configuration options
40126  */
40127 Roo.menu.ColorItem = function(config){
40128     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40129     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40130     this.palette = this.component;
40131     this.relayEvents(this.palette, ["select"]);
40132     if(this.selectHandler){
40133         this.on('select', this.selectHandler, this.scope);
40134     }
40135 };
40136 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40137  * Based on:
40138  * Ext JS Library 1.1.1
40139  * Copyright(c) 2006-2007, Ext JS, LLC.
40140  *
40141  * Originally Released Under LGPL - original licence link has changed is not relivant.
40142  *
40143  * Fork - LGPL
40144  * <script type="text/javascript">
40145  */
40146  
40147
40148 /**
40149  * @class Roo.menu.DateMenu
40150  * @extends Roo.menu.Menu
40151  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40152  * @constructor
40153  * Creates a new DateMenu
40154  * @param {Object} config Configuration options
40155  */
40156 Roo.menu.DateMenu = function(config){
40157     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40158     this.plain = true;
40159     var di = new Roo.menu.DateItem(config);
40160     this.add(di);
40161     /**
40162      * The {@link Roo.DatePicker} instance for this DateMenu
40163      * @type DatePicker
40164      */
40165     this.picker = di.picker;
40166     /**
40167      * @event select
40168      * @param {DatePicker} picker
40169      * @param {Date} date
40170      */
40171     this.relayEvents(di, ["select"]);
40172     this.on('beforeshow', function(){
40173         if(this.picker){
40174             this.picker.hideMonthPicker(false);
40175         }
40176     }, this);
40177 };
40178 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40179     cls:'x-date-menu'
40180 });/*
40181  * Based on:
40182  * Ext JS Library 1.1.1
40183  * Copyright(c) 2006-2007, Ext JS, LLC.
40184  *
40185  * Originally Released Under LGPL - original licence link has changed is not relivant.
40186  *
40187  * Fork - LGPL
40188  * <script type="text/javascript">
40189  */
40190  
40191
40192 /**
40193  * @class Roo.menu.ColorMenu
40194  * @extends Roo.menu.Menu
40195  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40196  * @constructor
40197  * Creates a new ColorMenu
40198  * @param {Object} config Configuration options
40199  */
40200 Roo.menu.ColorMenu = function(config){
40201     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40202     this.plain = true;
40203     var ci = new Roo.menu.ColorItem(config);
40204     this.add(ci);
40205     /**
40206      * The {@link Roo.ColorPalette} instance for this ColorMenu
40207      * @type ColorPalette
40208      */
40209     this.palette = ci.palette;
40210     /**
40211      * @event select
40212      * @param {ColorPalette} palette
40213      * @param {String} color
40214      */
40215     this.relayEvents(ci, ["select"]);
40216 };
40217 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40218  * Based on:
40219  * Ext JS Library 1.1.1
40220  * Copyright(c) 2006-2007, Ext JS, LLC.
40221  *
40222  * Originally Released Under LGPL - original licence link has changed is not relivant.
40223  *
40224  * Fork - LGPL
40225  * <script type="text/javascript">
40226  */
40227  
40228 /**
40229  * @class Roo.form.TextItem
40230  * @extends Roo.BoxComponent
40231  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40232  * @constructor
40233  * Creates a new TextItem
40234  * @param {Object} config Configuration options
40235  */
40236 Roo.form.TextItem = function(config){
40237     Roo.form.TextItem.superclass.constructor.call(this, config);
40238 };
40239
40240 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40241     
40242     /**
40243      * @cfg {String} tag the tag for this item (default div)
40244      */
40245     tag : 'div',
40246     /**
40247      * @cfg {String} html the content for this item
40248      */
40249     html : '',
40250     
40251     getAutoCreate : function()
40252     {
40253         var cfg = {
40254             id: this.id,
40255             tag: this.tag,
40256             html: this.html,
40257             cls: 'x-form-item'
40258         };
40259         
40260         return cfg;
40261         
40262     },
40263     
40264     onRender : function(ct, position)
40265     {
40266         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40267         
40268         if(!this.el){
40269             var cfg = this.getAutoCreate();
40270             if(!cfg.name){
40271                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40272             }
40273             if (!cfg.name.length) {
40274                 delete cfg.name;
40275             }
40276             this.el = ct.createChild(cfg, position);
40277         }
40278     },
40279     /*
40280      * setHTML
40281      * @param {String} html update the Contents of the element.
40282      */
40283     setHTML : function(html)
40284     {
40285         this.fieldEl.dom.innerHTML = html;
40286     }
40287     
40288 });/*
40289  * Based on:
40290  * Ext JS Library 1.1.1
40291  * Copyright(c) 2006-2007, Ext JS, LLC.
40292  *
40293  * Originally Released Under LGPL - original licence link has changed is not relivant.
40294  *
40295  * Fork - LGPL
40296  * <script type="text/javascript">
40297  */
40298  
40299 /**
40300  * @class Roo.form.Field
40301  * @extends Roo.BoxComponent
40302  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40303  * @constructor
40304  * Creates a new Field
40305  * @param {Object} config Configuration options
40306  */
40307 Roo.form.Field = function(config){
40308     Roo.form.Field.superclass.constructor.call(this, config);
40309 };
40310
40311 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40312     /**
40313      * @cfg {String} fieldLabel Label to use when rendering a form.
40314      */
40315        /**
40316      * @cfg {String} qtip Mouse over tip
40317      */
40318      
40319     /**
40320      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40321      */
40322     invalidClass : "x-form-invalid",
40323     /**
40324      * @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")
40325      */
40326     invalidText : "The value in this field is invalid",
40327     /**
40328      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40329      */
40330     focusClass : "x-form-focus",
40331     /**
40332      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40333       automatic validation (defaults to "keyup").
40334      */
40335     validationEvent : "keyup",
40336     /**
40337      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40338      */
40339     validateOnBlur : true,
40340     /**
40341      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40342      */
40343     validationDelay : 250,
40344     /**
40345      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40346      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40347      */
40348     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40349     /**
40350      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40351      */
40352     fieldClass : "x-form-field",
40353     /**
40354      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40355      *<pre>
40356 Value         Description
40357 -----------   ----------------------------------------------------------------------
40358 qtip          Display a quick tip when the user hovers over the field
40359 title         Display a default browser title attribute popup
40360 under         Add a block div beneath the field containing the error text
40361 side          Add an error icon to the right of the field with a popup on hover
40362 [element id]  Add the error text directly to the innerHTML of the specified element
40363 </pre>
40364      */
40365     msgTarget : 'qtip',
40366     /**
40367      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40368      */
40369     msgFx : 'normal',
40370
40371     /**
40372      * @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.
40373      */
40374     readOnly : false,
40375
40376     /**
40377      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40378      */
40379     disabled : false,
40380
40381     /**
40382      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40383      */
40384     inputType : undefined,
40385     
40386     /**
40387      * @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).
40388          */
40389         tabIndex : undefined,
40390         
40391     // private
40392     isFormField : true,
40393
40394     // private
40395     hasFocus : false,
40396     /**
40397      * @property {Roo.Element} fieldEl
40398      * Element Containing the rendered Field (with label etc.)
40399      */
40400     /**
40401      * @cfg {Mixed} value A value to initialize this field with.
40402      */
40403     value : undefined,
40404
40405     /**
40406      * @cfg {String} name The field's HTML name attribute.
40407      */
40408     /**
40409      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40410      */
40411     // private
40412     loadedValue : false,
40413      
40414      
40415         // private ??
40416         initComponent : function(){
40417         Roo.form.Field.superclass.initComponent.call(this);
40418         this.addEvents({
40419             /**
40420              * @event focus
40421              * Fires when this field receives input focus.
40422              * @param {Roo.form.Field} this
40423              */
40424             focus : true,
40425             /**
40426              * @event blur
40427              * Fires when this field loses input focus.
40428              * @param {Roo.form.Field} this
40429              */
40430             blur : true,
40431             /**
40432              * @event specialkey
40433              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40434              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40435              * @param {Roo.form.Field} this
40436              * @param {Roo.EventObject} e The event object
40437              */
40438             specialkey : true,
40439             /**
40440              * @event change
40441              * Fires just before the field blurs if the field value has changed.
40442              * @param {Roo.form.Field} this
40443              * @param {Mixed} newValue The new value
40444              * @param {Mixed} oldValue The original value
40445              */
40446             change : true,
40447             /**
40448              * @event invalid
40449              * Fires after the field has been marked as invalid.
40450              * @param {Roo.form.Field} this
40451              * @param {String} msg The validation message
40452              */
40453             invalid : true,
40454             /**
40455              * @event valid
40456              * Fires after the field has been validated with no errors.
40457              * @param {Roo.form.Field} this
40458              */
40459             valid : true,
40460              /**
40461              * @event keyup
40462              * Fires after the key up
40463              * @param {Roo.form.Field} this
40464              * @param {Roo.EventObject}  e The event Object
40465              */
40466             keyup : true
40467         });
40468     },
40469
40470     /**
40471      * Returns the name attribute of the field if available
40472      * @return {String} name The field name
40473      */
40474     getName: function(){
40475          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40476     },
40477
40478     // private
40479     onRender : function(ct, position){
40480         Roo.form.Field.superclass.onRender.call(this, ct, position);
40481         if(!this.el){
40482             var cfg = this.getAutoCreate();
40483             if(!cfg.name){
40484                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40485             }
40486             if (!cfg.name.length) {
40487                 delete cfg.name;
40488             }
40489             if(this.inputType){
40490                 cfg.type = this.inputType;
40491             }
40492             this.el = ct.createChild(cfg, position);
40493         }
40494         var type = this.el.dom.type;
40495         if(type){
40496             if(type == 'password'){
40497                 type = 'text';
40498             }
40499             this.el.addClass('x-form-'+type);
40500         }
40501         if(this.readOnly){
40502             this.el.dom.readOnly = true;
40503         }
40504         if(this.tabIndex !== undefined){
40505             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40506         }
40507
40508         this.el.addClass([this.fieldClass, this.cls]);
40509         this.initValue();
40510     },
40511
40512     /**
40513      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40514      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40515      * @return {Roo.form.Field} this
40516      */
40517     applyTo : function(target){
40518         this.allowDomMove = false;
40519         this.el = Roo.get(target);
40520         this.render(this.el.dom.parentNode);
40521         return this;
40522     },
40523
40524     // private
40525     initValue : function(){
40526         if(this.value !== undefined){
40527             this.setValue(this.value);
40528         }else if(this.el.dom.value.length > 0){
40529             this.setValue(this.el.dom.value);
40530         }
40531     },
40532
40533     /**
40534      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40535      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40536      */
40537     isDirty : function() {
40538         if(this.disabled) {
40539             return false;
40540         }
40541         return String(this.getValue()) !== String(this.originalValue);
40542     },
40543
40544     /**
40545      * stores the current value in loadedValue
40546      */
40547     resetHasChanged : function()
40548     {
40549         this.loadedValue = String(this.getValue());
40550     },
40551     /**
40552      * checks the current value against the 'loaded' value.
40553      * Note - will return false if 'resetHasChanged' has not been called first.
40554      */
40555     hasChanged : function()
40556     {
40557         if(this.disabled || this.readOnly) {
40558             return false;
40559         }
40560         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40561     },
40562     
40563     
40564     
40565     // private
40566     afterRender : function(){
40567         Roo.form.Field.superclass.afterRender.call(this);
40568         this.initEvents();
40569     },
40570
40571     // private
40572     fireKey : function(e){
40573         //Roo.log('field ' + e.getKey());
40574         if(e.isNavKeyPress()){
40575             this.fireEvent("specialkey", this, e);
40576         }
40577     },
40578
40579     /**
40580      * Resets the current field value to the originally loaded value and clears any validation messages
40581      */
40582     reset : function(){
40583         this.setValue(this.resetValue);
40584         this.originalValue = this.getValue();
40585         this.clearInvalid();
40586     },
40587
40588     // private
40589     initEvents : function(){
40590         // safari killled keypress - so keydown is now used..
40591         this.el.on("keydown" , this.fireKey,  this);
40592         this.el.on("focus", this.onFocus,  this);
40593         this.el.on("blur", this.onBlur,  this);
40594         this.el.relayEvent('keyup', this);
40595
40596         // reference to original value for reset
40597         this.originalValue = this.getValue();
40598         this.resetValue =  this.getValue();
40599     },
40600
40601     // private
40602     onFocus : function(){
40603         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40604             this.el.addClass(this.focusClass);
40605         }
40606         if(!this.hasFocus){
40607             this.hasFocus = true;
40608             this.startValue = this.getValue();
40609             this.fireEvent("focus", this);
40610         }
40611     },
40612
40613     beforeBlur : Roo.emptyFn,
40614
40615     // private
40616     onBlur : function(){
40617         this.beforeBlur();
40618         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40619             this.el.removeClass(this.focusClass);
40620         }
40621         this.hasFocus = false;
40622         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40623             this.validate();
40624         }
40625         var v = this.getValue();
40626         if(String(v) !== String(this.startValue)){
40627             this.fireEvent('change', this, v, this.startValue);
40628         }
40629         this.fireEvent("blur", this);
40630     },
40631
40632     /**
40633      * Returns whether or not the field value is currently valid
40634      * @param {Boolean} preventMark True to disable marking the field invalid
40635      * @return {Boolean} True if the value is valid, else false
40636      */
40637     isValid : function(preventMark){
40638         if(this.disabled){
40639             return true;
40640         }
40641         var restore = this.preventMark;
40642         this.preventMark = preventMark === true;
40643         var v = this.validateValue(this.processValue(this.getRawValue()));
40644         this.preventMark = restore;
40645         return v;
40646     },
40647
40648     /**
40649      * Validates the field value
40650      * @return {Boolean} True if the value is valid, else false
40651      */
40652     validate : function(){
40653         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40654             this.clearInvalid();
40655             return true;
40656         }
40657         return false;
40658     },
40659
40660     processValue : function(value){
40661         return value;
40662     },
40663
40664     // private
40665     // Subclasses should provide the validation implementation by overriding this
40666     validateValue : function(value){
40667         return true;
40668     },
40669
40670     /**
40671      * Mark this field as invalid
40672      * @param {String} msg The validation message
40673      */
40674     markInvalid : function(msg){
40675         if(!this.rendered || this.preventMark){ // not rendered
40676             return;
40677         }
40678         
40679         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40680         
40681         obj.el.addClass(this.invalidClass);
40682         msg = msg || this.invalidText;
40683         switch(this.msgTarget){
40684             case 'qtip':
40685                 obj.el.dom.qtip = msg;
40686                 obj.el.dom.qclass = 'x-form-invalid-tip';
40687                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40688                     Roo.QuickTips.enable();
40689                 }
40690                 break;
40691             case 'title':
40692                 this.el.dom.title = msg;
40693                 break;
40694             case 'under':
40695                 if(!this.errorEl){
40696                     var elp = this.el.findParent('.x-form-element', 5, true);
40697                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40698                     this.errorEl.setWidth(elp.getWidth(true)-20);
40699                 }
40700                 this.errorEl.update(msg);
40701                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40702                 break;
40703             case 'side':
40704                 if(!this.errorIcon){
40705                     var elp = this.el.findParent('.x-form-element', 5, true);
40706                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40707                 }
40708                 this.alignErrorIcon();
40709                 this.errorIcon.dom.qtip = msg;
40710                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40711                 this.errorIcon.show();
40712                 this.on('resize', this.alignErrorIcon, this);
40713                 break;
40714             default:
40715                 var t = Roo.getDom(this.msgTarget);
40716                 t.innerHTML = msg;
40717                 t.style.display = this.msgDisplay;
40718                 break;
40719         }
40720         this.fireEvent('invalid', this, msg);
40721     },
40722
40723     // private
40724     alignErrorIcon : function(){
40725         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40726     },
40727
40728     /**
40729      * Clear any invalid styles/messages for this field
40730      */
40731     clearInvalid : function(){
40732         if(!this.rendered || this.preventMark){ // not rendered
40733             return;
40734         }
40735         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40736         
40737         obj.el.removeClass(this.invalidClass);
40738         switch(this.msgTarget){
40739             case 'qtip':
40740                 obj.el.dom.qtip = '';
40741                 break;
40742             case 'title':
40743                 this.el.dom.title = '';
40744                 break;
40745             case 'under':
40746                 if(this.errorEl){
40747                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40748                 }
40749                 break;
40750             case 'side':
40751                 if(this.errorIcon){
40752                     this.errorIcon.dom.qtip = '';
40753                     this.errorIcon.hide();
40754                     this.un('resize', this.alignErrorIcon, this);
40755                 }
40756                 break;
40757             default:
40758                 var t = Roo.getDom(this.msgTarget);
40759                 t.innerHTML = '';
40760                 t.style.display = 'none';
40761                 break;
40762         }
40763         this.fireEvent('valid', this);
40764     },
40765
40766     /**
40767      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40768      * @return {Mixed} value The field value
40769      */
40770     getRawValue : function(){
40771         var v = this.el.getValue();
40772         
40773         return v;
40774     },
40775
40776     /**
40777      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40778      * @return {Mixed} value The field value
40779      */
40780     getValue : function(){
40781         var v = this.el.getValue();
40782          
40783         return v;
40784     },
40785
40786     /**
40787      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40788      * @param {Mixed} value The value to set
40789      */
40790     setRawValue : function(v){
40791         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40792     },
40793
40794     /**
40795      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40796      * @param {Mixed} value The value to set
40797      */
40798     setValue : function(v){
40799         this.value = v;
40800         if(this.rendered){
40801             this.el.dom.value = (v === null || v === undefined ? '' : v);
40802              this.validate();
40803         }
40804     },
40805
40806     adjustSize : function(w, h){
40807         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40808         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40809         return s;
40810     },
40811
40812     adjustWidth : function(tag, w){
40813         tag = tag.toLowerCase();
40814         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40815             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40816                 if(tag == 'input'){
40817                     return w + 2;
40818                 }
40819                 if(tag == 'textarea'){
40820                     return w-2;
40821                 }
40822             }else if(Roo.isOpera){
40823                 if(tag == 'input'){
40824                     return w + 2;
40825                 }
40826                 if(tag == 'textarea'){
40827                     return w-2;
40828                 }
40829             }
40830         }
40831         return w;
40832     }
40833 });
40834
40835
40836 // anything other than normal should be considered experimental
40837 Roo.form.Field.msgFx = {
40838     normal : {
40839         show: function(msgEl, f){
40840             msgEl.setDisplayed('block');
40841         },
40842
40843         hide : function(msgEl, f){
40844             msgEl.setDisplayed(false).update('');
40845         }
40846     },
40847
40848     slide : {
40849         show: function(msgEl, f){
40850             msgEl.slideIn('t', {stopFx:true});
40851         },
40852
40853         hide : function(msgEl, f){
40854             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40855         }
40856     },
40857
40858     slideRight : {
40859         show: function(msgEl, f){
40860             msgEl.fixDisplay();
40861             msgEl.alignTo(f.el, 'tl-tr');
40862             msgEl.slideIn('l', {stopFx:true});
40863         },
40864
40865         hide : function(msgEl, f){
40866             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40867         }
40868     }
40869 };/*
40870  * Based on:
40871  * Ext JS Library 1.1.1
40872  * Copyright(c) 2006-2007, Ext JS, LLC.
40873  *
40874  * Originally Released Under LGPL - original licence link has changed is not relivant.
40875  *
40876  * Fork - LGPL
40877  * <script type="text/javascript">
40878  */
40879  
40880
40881 /**
40882  * @class Roo.form.TextField
40883  * @extends Roo.form.Field
40884  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40885  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40886  * @constructor
40887  * Creates a new TextField
40888  * @param {Object} config Configuration options
40889  */
40890 Roo.form.TextField = function(config){
40891     Roo.form.TextField.superclass.constructor.call(this, config);
40892     this.addEvents({
40893         /**
40894          * @event autosize
40895          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40896          * according to the default logic, but this event provides a hook for the developer to apply additional
40897          * logic at runtime to resize the field if needed.
40898              * @param {Roo.form.Field} this This text field
40899              * @param {Number} width The new field width
40900              */
40901         autosize : true
40902     });
40903 };
40904
40905 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40906     /**
40907      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40908      */
40909     grow : false,
40910     /**
40911      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40912      */
40913     growMin : 30,
40914     /**
40915      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40916      */
40917     growMax : 800,
40918     /**
40919      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40920      */
40921     vtype : null,
40922     /**
40923      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40924      */
40925     maskRe : null,
40926     /**
40927      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40928      */
40929     disableKeyFilter : false,
40930     /**
40931      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40932      */
40933     allowBlank : true,
40934     /**
40935      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40936      */
40937     minLength : 0,
40938     /**
40939      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40940      */
40941     maxLength : Number.MAX_VALUE,
40942     /**
40943      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40944      */
40945     minLengthText : "The minimum length for this field is {0}",
40946     /**
40947      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40948      */
40949     maxLengthText : "The maximum length for this field is {0}",
40950     /**
40951      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40952      */
40953     selectOnFocus : false,
40954     /**
40955      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40956      */    
40957     allowLeadingSpace : false,
40958     /**
40959      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40960      */
40961     blankText : "This field is required",
40962     /**
40963      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40964      * If available, this function will be called only after the basic validators all return true, and will be passed the
40965      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40966      */
40967     validator : null,
40968     /**
40969      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40970      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40971      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40972      */
40973     regex : null,
40974     /**
40975      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40976      */
40977     regexText : "",
40978     /**
40979      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40980      */
40981     emptyText : null,
40982    
40983
40984     // private
40985     initEvents : function()
40986     {
40987         if (this.emptyText) {
40988             this.el.attr('placeholder', this.emptyText);
40989         }
40990         
40991         Roo.form.TextField.superclass.initEvents.call(this);
40992         if(this.validationEvent == 'keyup'){
40993             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40994             this.el.on('keyup', this.filterValidation, this);
40995         }
40996         else if(this.validationEvent !== false){
40997             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40998         }
40999         
41000         if(this.selectOnFocus){
41001             this.on("focus", this.preFocus, this);
41002         }
41003         if (!this.allowLeadingSpace) {
41004             this.on('blur', this.cleanLeadingSpace, this);
41005         }
41006         
41007         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41008             this.el.on("keypress", this.filterKeys, this);
41009         }
41010         if(this.grow){
41011             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41012             this.el.on("click", this.autoSize,  this);
41013         }
41014         if(this.el.is('input[type=password]') && Roo.isSafari){
41015             this.el.on('keydown', this.SafariOnKeyDown, this);
41016         }
41017     },
41018
41019     processValue : function(value){
41020         if(this.stripCharsRe){
41021             var newValue = value.replace(this.stripCharsRe, '');
41022             if(newValue !== value){
41023                 this.setRawValue(newValue);
41024                 return newValue;
41025             }
41026         }
41027         return value;
41028     },
41029
41030     filterValidation : function(e){
41031         if(!e.isNavKeyPress()){
41032             this.validationTask.delay(this.validationDelay);
41033         }
41034     },
41035
41036     // private
41037     onKeyUp : function(e){
41038         if(!e.isNavKeyPress()){
41039             this.autoSize();
41040         }
41041     },
41042     // private - clean the leading white space
41043     cleanLeadingSpace : function(e)
41044     {
41045         if ( this.inputType == 'file') {
41046             return;
41047         }
41048         
41049         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41050     },
41051     /**
41052      * Resets the current field value to the originally-loaded value and clears any validation messages.
41053      *  
41054      */
41055     reset : function(){
41056         Roo.form.TextField.superclass.reset.call(this);
41057        
41058     }, 
41059     // private
41060     preFocus : function(){
41061         
41062         if(this.selectOnFocus){
41063             this.el.dom.select();
41064         }
41065     },
41066
41067     
41068     // private
41069     filterKeys : function(e){
41070         var k = e.getKey();
41071         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41072             return;
41073         }
41074         var c = e.getCharCode(), cc = String.fromCharCode(c);
41075         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41076             return;
41077         }
41078         if(!this.maskRe.test(cc)){
41079             e.stopEvent();
41080         }
41081     },
41082
41083     setValue : function(v){
41084         
41085         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41086         
41087         this.autoSize();
41088     },
41089
41090     /**
41091      * Validates a value according to the field's validation rules and marks the field as invalid
41092      * if the validation fails
41093      * @param {Mixed} value The value to validate
41094      * @return {Boolean} True if the value is valid, else false
41095      */
41096     validateValue : function(value){
41097         if(value.length < 1)  { // if it's blank
41098              if(this.allowBlank){
41099                 this.clearInvalid();
41100                 return true;
41101              }else{
41102                 this.markInvalid(this.blankText);
41103                 return false;
41104              }
41105         }
41106         if(value.length < this.minLength){
41107             this.markInvalid(String.format(this.minLengthText, this.minLength));
41108             return false;
41109         }
41110         if(value.length > this.maxLength){
41111             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41112             return false;
41113         }
41114         if(this.vtype){
41115             var vt = Roo.form.VTypes;
41116             if(!vt[this.vtype](value, this)){
41117                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41118                 return false;
41119             }
41120         }
41121         if(typeof this.validator == "function"){
41122             var msg = this.validator(value);
41123             if(msg !== true){
41124                 this.markInvalid(msg);
41125                 return false;
41126             }
41127         }
41128         if(this.regex && !this.regex.test(value)){
41129             this.markInvalid(this.regexText);
41130             return false;
41131         }
41132         return true;
41133     },
41134
41135     /**
41136      * Selects text in this field
41137      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41138      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41139      */
41140     selectText : function(start, end){
41141         var v = this.getRawValue();
41142         if(v.length > 0){
41143             start = start === undefined ? 0 : start;
41144             end = end === undefined ? v.length : end;
41145             var d = this.el.dom;
41146             if(d.setSelectionRange){
41147                 d.setSelectionRange(start, end);
41148             }else if(d.createTextRange){
41149                 var range = d.createTextRange();
41150                 range.moveStart("character", start);
41151                 range.moveEnd("character", v.length-end);
41152                 range.select();
41153             }
41154         }
41155     },
41156
41157     /**
41158      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41159      * This only takes effect if grow = true, and fires the autosize event.
41160      */
41161     autoSize : function(){
41162         if(!this.grow || !this.rendered){
41163             return;
41164         }
41165         if(!this.metrics){
41166             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41167         }
41168         var el = this.el;
41169         var v = el.dom.value;
41170         var d = document.createElement('div');
41171         d.appendChild(document.createTextNode(v));
41172         v = d.innerHTML;
41173         d = null;
41174         v += "&#160;";
41175         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41176         this.el.setWidth(w);
41177         this.fireEvent("autosize", this, w);
41178     },
41179     
41180     // private
41181     SafariOnKeyDown : function(event)
41182     {
41183         // this is a workaround for a password hang bug on chrome/ webkit.
41184         
41185         var isSelectAll = false;
41186         
41187         if(this.el.dom.selectionEnd > 0){
41188             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41189         }
41190         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41191             event.preventDefault();
41192             this.setValue('');
41193             return;
41194         }
41195         
41196         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41197             
41198             event.preventDefault();
41199             // this is very hacky as keydown always get's upper case.
41200             
41201             var cc = String.fromCharCode(event.getCharCode());
41202             
41203             
41204             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41205             
41206         }
41207         
41208         
41209     }
41210 });/*
41211  * Based on:
41212  * Ext JS Library 1.1.1
41213  * Copyright(c) 2006-2007, Ext JS, LLC.
41214  *
41215  * Originally Released Under LGPL - original licence link has changed is not relivant.
41216  *
41217  * Fork - LGPL
41218  * <script type="text/javascript">
41219  */
41220  
41221 /**
41222  * @class Roo.form.Hidden
41223  * @extends Roo.form.TextField
41224  * Simple Hidden element used on forms 
41225  * 
41226  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41227  * 
41228  * @constructor
41229  * Creates a new Hidden form element.
41230  * @param {Object} config Configuration options
41231  */
41232
41233
41234
41235 // easy hidden field...
41236 Roo.form.Hidden = function(config){
41237     Roo.form.Hidden.superclass.constructor.call(this, config);
41238 };
41239   
41240 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41241     fieldLabel:      '',
41242     inputType:      'hidden',
41243     width:          50,
41244     allowBlank:     true,
41245     labelSeparator: '',
41246     hidden:         true,
41247     itemCls :       'x-form-item-display-none'
41248
41249
41250 });
41251
41252
41253 /*
41254  * Based on:
41255  * Ext JS Library 1.1.1
41256  * Copyright(c) 2006-2007, Ext JS, LLC.
41257  *
41258  * Originally Released Under LGPL - original licence link has changed is not relivant.
41259  *
41260  * Fork - LGPL
41261  * <script type="text/javascript">
41262  */
41263  
41264 /**
41265  * @class Roo.form.TriggerField
41266  * @extends Roo.form.TextField
41267  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41268  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41269  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41270  * for which you can provide a custom implementation.  For example:
41271  * <pre><code>
41272 var trigger = new Roo.form.TriggerField();
41273 trigger.onTriggerClick = myTriggerFn;
41274 trigger.applyTo('my-field');
41275 </code></pre>
41276  *
41277  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41278  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41279  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41280  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41281  * @constructor
41282  * Create a new TriggerField.
41283  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41284  * to the base TextField)
41285  */
41286 Roo.form.TriggerField = function(config){
41287     this.mimicing = false;
41288     Roo.form.TriggerField.superclass.constructor.call(this, config);
41289 };
41290
41291 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41292     /**
41293      * @cfg {String} triggerClass A CSS class to apply to the trigger
41294      */
41295     /**
41296      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41297      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41298      */
41299     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41300     /**
41301      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41302      */
41303     hideTrigger:false,
41304
41305     /** @cfg {Boolean} grow @hide */
41306     /** @cfg {Number} growMin @hide */
41307     /** @cfg {Number} growMax @hide */
41308
41309     /**
41310      * @hide 
41311      * @method
41312      */
41313     autoSize: Roo.emptyFn,
41314     // private
41315     monitorTab : true,
41316     // private
41317     deferHeight : true,
41318
41319     
41320     actionMode : 'wrap',
41321     // private
41322     onResize : function(w, h){
41323         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41324         if(typeof w == 'number'){
41325             var x = w - this.trigger.getWidth();
41326             this.el.setWidth(this.adjustWidth('input', x));
41327             this.trigger.setStyle('left', x+'px');
41328         }
41329     },
41330
41331     // private
41332     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41333
41334     // private
41335     getResizeEl : function(){
41336         return this.wrap;
41337     },
41338
41339     // private
41340     getPositionEl : function(){
41341         return this.wrap;
41342     },
41343
41344     // private
41345     alignErrorIcon : function(){
41346         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41347     },
41348
41349     // private
41350     onRender : function(ct, position){
41351         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41352         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41353         this.trigger = this.wrap.createChild(this.triggerConfig ||
41354                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41355         if(this.hideTrigger){
41356             this.trigger.setDisplayed(false);
41357         }
41358         this.initTrigger();
41359         if(!this.width){
41360             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41361         }
41362     },
41363
41364     // private
41365     initTrigger : function(){
41366         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41367         this.trigger.addClassOnOver('x-form-trigger-over');
41368         this.trigger.addClassOnClick('x-form-trigger-click');
41369     },
41370
41371     // private
41372     onDestroy : function(){
41373         if(this.trigger){
41374             this.trigger.removeAllListeners();
41375             this.trigger.remove();
41376         }
41377         if(this.wrap){
41378             this.wrap.remove();
41379         }
41380         Roo.form.TriggerField.superclass.onDestroy.call(this);
41381     },
41382
41383     // private
41384     onFocus : function(){
41385         Roo.form.TriggerField.superclass.onFocus.call(this);
41386         if(!this.mimicing){
41387             this.wrap.addClass('x-trigger-wrap-focus');
41388             this.mimicing = true;
41389             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41390             if(this.monitorTab){
41391                 this.el.on("keydown", this.checkTab, this);
41392             }
41393         }
41394     },
41395
41396     // private
41397     checkTab : function(e){
41398         if(e.getKey() == e.TAB){
41399             this.triggerBlur();
41400         }
41401     },
41402
41403     // private
41404     onBlur : function(){
41405         // do nothing
41406     },
41407
41408     // private
41409     mimicBlur : function(e, t){
41410         if(!this.wrap.contains(t) && this.validateBlur()){
41411             this.triggerBlur();
41412         }
41413     },
41414
41415     // private
41416     triggerBlur : function(){
41417         this.mimicing = false;
41418         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41419         if(this.monitorTab){
41420             this.el.un("keydown", this.checkTab, this);
41421         }
41422         this.wrap.removeClass('x-trigger-wrap-focus');
41423         Roo.form.TriggerField.superclass.onBlur.call(this);
41424     },
41425
41426     // private
41427     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41428     validateBlur : function(e, t){
41429         return true;
41430     },
41431
41432     // private
41433     onDisable : function(){
41434         Roo.form.TriggerField.superclass.onDisable.call(this);
41435         if(this.wrap){
41436             this.wrap.addClass('x-item-disabled');
41437         }
41438     },
41439
41440     // private
41441     onEnable : function(){
41442         Roo.form.TriggerField.superclass.onEnable.call(this);
41443         if(this.wrap){
41444             this.wrap.removeClass('x-item-disabled');
41445         }
41446     },
41447
41448     // private
41449     onShow : function(){
41450         var ae = this.getActionEl();
41451         
41452         if(ae){
41453             ae.dom.style.display = '';
41454             ae.dom.style.visibility = 'visible';
41455         }
41456     },
41457
41458     // private
41459     
41460     onHide : function(){
41461         var ae = this.getActionEl();
41462         ae.dom.style.display = 'none';
41463     },
41464
41465     /**
41466      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41467      * by an implementing function.
41468      * @method
41469      * @param {EventObject} e
41470      */
41471     onTriggerClick : Roo.emptyFn
41472 });
41473
41474 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41475 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41476 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41477 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41478     initComponent : function(){
41479         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41480
41481         this.triggerConfig = {
41482             tag:'span', cls:'x-form-twin-triggers', cn:[
41483             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41484             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41485         ]};
41486     },
41487
41488     getTrigger : function(index){
41489         return this.triggers[index];
41490     },
41491
41492     initTrigger : function(){
41493         var ts = this.trigger.select('.x-form-trigger', true);
41494         this.wrap.setStyle('overflow', 'hidden');
41495         var triggerField = this;
41496         ts.each(function(t, all, index){
41497             t.hide = function(){
41498                 var w = triggerField.wrap.getWidth();
41499                 this.dom.style.display = 'none';
41500                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41501             };
41502             t.show = function(){
41503                 var w = triggerField.wrap.getWidth();
41504                 this.dom.style.display = '';
41505                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41506             };
41507             var triggerIndex = 'Trigger'+(index+1);
41508
41509             if(this['hide'+triggerIndex]){
41510                 t.dom.style.display = 'none';
41511             }
41512             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41513             t.addClassOnOver('x-form-trigger-over');
41514             t.addClassOnClick('x-form-trigger-click');
41515         }, this);
41516         this.triggers = ts.elements;
41517     },
41518
41519     onTrigger1Click : Roo.emptyFn,
41520     onTrigger2Click : Roo.emptyFn
41521 });/*
41522  * Based on:
41523  * Ext JS Library 1.1.1
41524  * Copyright(c) 2006-2007, Ext JS, LLC.
41525  *
41526  * Originally Released Under LGPL - original licence link has changed is not relivant.
41527  *
41528  * Fork - LGPL
41529  * <script type="text/javascript">
41530  */
41531  
41532 /**
41533  * @class Roo.form.TextArea
41534  * @extends Roo.form.TextField
41535  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41536  * support for auto-sizing.
41537  * @constructor
41538  * Creates a new TextArea
41539  * @param {Object} config Configuration options
41540  */
41541 Roo.form.TextArea = function(config){
41542     Roo.form.TextArea.superclass.constructor.call(this, config);
41543     // these are provided exchanges for backwards compat
41544     // minHeight/maxHeight were replaced by growMin/growMax to be
41545     // compatible with TextField growing config values
41546     if(this.minHeight !== undefined){
41547         this.growMin = this.minHeight;
41548     }
41549     if(this.maxHeight !== undefined){
41550         this.growMax = this.maxHeight;
41551     }
41552 };
41553
41554 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41555     /**
41556      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41557      */
41558     growMin : 60,
41559     /**
41560      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41561      */
41562     growMax: 1000,
41563     /**
41564      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41565      * in the field (equivalent to setting overflow: hidden, defaults to false)
41566      */
41567     preventScrollbars: false,
41568     /**
41569      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41570      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41571      */
41572
41573     // private
41574     onRender : function(ct, position){
41575         if(!this.el){
41576             this.defaultAutoCreate = {
41577                 tag: "textarea",
41578                 style:"width:300px;height:60px;",
41579                 autocomplete: "new-password"
41580             };
41581         }
41582         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41583         if(this.grow){
41584             this.textSizeEl = Roo.DomHelper.append(document.body, {
41585                 tag: "pre", cls: "x-form-grow-sizer"
41586             });
41587             if(this.preventScrollbars){
41588                 this.el.setStyle("overflow", "hidden");
41589             }
41590             this.el.setHeight(this.growMin);
41591         }
41592     },
41593
41594     onDestroy : function(){
41595         if(this.textSizeEl){
41596             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41597         }
41598         Roo.form.TextArea.superclass.onDestroy.call(this);
41599     },
41600
41601     // private
41602     onKeyUp : function(e){
41603         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41604             this.autoSize();
41605         }
41606     },
41607
41608     /**
41609      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41610      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41611      */
41612     autoSize : function(){
41613         if(!this.grow || !this.textSizeEl){
41614             return;
41615         }
41616         var el = this.el;
41617         var v = el.dom.value;
41618         var ts = this.textSizeEl;
41619
41620         ts.innerHTML = '';
41621         ts.appendChild(document.createTextNode(v));
41622         v = ts.innerHTML;
41623
41624         Roo.fly(ts).setWidth(this.el.getWidth());
41625         if(v.length < 1){
41626             v = "&#160;&#160;";
41627         }else{
41628             if(Roo.isIE){
41629                 v = v.replace(/\n/g, '<p>&#160;</p>');
41630             }
41631             v += "&#160;\n&#160;";
41632         }
41633         ts.innerHTML = v;
41634         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41635         if(h != this.lastHeight){
41636             this.lastHeight = h;
41637             this.el.setHeight(h);
41638             this.fireEvent("autosize", this, h);
41639         }
41640     }
41641 });/*
41642  * Based on:
41643  * Ext JS Library 1.1.1
41644  * Copyright(c) 2006-2007, Ext JS, LLC.
41645  *
41646  * Originally Released Under LGPL - original licence link has changed is not relivant.
41647  *
41648  * Fork - LGPL
41649  * <script type="text/javascript">
41650  */
41651  
41652
41653 /**
41654  * @class Roo.form.NumberField
41655  * @extends Roo.form.TextField
41656  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41657  * @constructor
41658  * Creates a new NumberField
41659  * @param {Object} config Configuration options
41660  */
41661 Roo.form.NumberField = function(config){
41662     Roo.form.NumberField.superclass.constructor.call(this, config);
41663 };
41664
41665 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41666     /**
41667      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41668      */
41669     fieldClass: "x-form-field x-form-num-field",
41670     /**
41671      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41672      */
41673     allowDecimals : true,
41674     /**
41675      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41676      */
41677     decimalSeparator : ".",
41678     /**
41679      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41680      */
41681     decimalPrecision : 2,
41682     /**
41683      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41684      */
41685     allowNegative : true,
41686     /**
41687      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41688      */
41689     minValue : Number.NEGATIVE_INFINITY,
41690     /**
41691      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41692      */
41693     maxValue : Number.MAX_VALUE,
41694     /**
41695      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41696      */
41697     minText : "The minimum value for this field is {0}",
41698     /**
41699      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41700      */
41701     maxText : "The maximum value for this field is {0}",
41702     /**
41703      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41704      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41705      */
41706     nanText : "{0} is not a valid number",
41707
41708     // private
41709     initEvents : function(){
41710         Roo.form.NumberField.superclass.initEvents.call(this);
41711         var allowed = "0123456789";
41712         if(this.allowDecimals){
41713             allowed += this.decimalSeparator;
41714         }
41715         if(this.allowNegative){
41716             allowed += "-";
41717         }
41718         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41719         var keyPress = function(e){
41720             var k = e.getKey();
41721             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41722                 return;
41723             }
41724             var c = e.getCharCode();
41725             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41726                 e.stopEvent();
41727             }
41728         };
41729         this.el.on("keypress", keyPress, this);
41730     },
41731
41732     // private
41733     validateValue : function(value){
41734         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41735             return false;
41736         }
41737         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41738              return true;
41739         }
41740         var num = this.parseValue(value);
41741         if(isNaN(num)){
41742             this.markInvalid(String.format(this.nanText, value));
41743             return false;
41744         }
41745         if(num < this.minValue){
41746             this.markInvalid(String.format(this.minText, this.minValue));
41747             return false;
41748         }
41749         if(num > this.maxValue){
41750             this.markInvalid(String.format(this.maxText, this.maxValue));
41751             return false;
41752         }
41753         return true;
41754     },
41755
41756     getValue : function(){
41757         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41758     },
41759
41760     // private
41761     parseValue : function(value){
41762         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41763         return isNaN(value) ? '' : value;
41764     },
41765
41766     // private
41767     fixPrecision : function(value){
41768         var nan = isNaN(value);
41769         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41770             return nan ? '' : value;
41771         }
41772         return parseFloat(value).toFixed(this.decimalPrecision);
41773     },
41774
41775     setValue : function(v){
41776         v = this.fixPrecision(v);
41777         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41778     },
41779
41780     // private
41781     decimalPrecisionFcn : function(v){
41782         return Math.floor(v);
41783     },
41784
41785     beforeBlur : function(){
41786         var v = this.parseValue(this.getRawValue());
41787         if(v){
41788             this.setValue(v);
41789         }
41790     }
41791 });/*
41792  * Based on:
41793  * Ext JS Library 1.1.1
41794  * Copyright(c) 2006-2007, Ext JS, LLC.
41795  *
41796  * Originally Released Under LGPL - original licence link has changed is not relivant.
41797  *
41798  * Fork - LGPL
41799  * <script type="text/javascript">
41800  */
41801  
41802 /**
41803  * @class Roo.form.DateField
41804  * @extends Roo.form.TriggerField
41805  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41806 * @constructor
41807 * Create a new DateField
41808 * @param {Object} config
41809  */
41810 Roo.form.DateField = function(config)
41811 {
41812     Roo.form.DateField.superclass.constructor.call(this, config);
41813     
41814       this.addEvents({
41815          
41816         /**
41817          * @event select
41818          * Fires when a date is selected
41819              * @param {Roo.form.DateField} combo This combo box
41820              * @param {Date} date The date selected
41821              */
41822         'select' : true
41823          
41824     });
41825     
41826     
41827     if(typeof this.minValue == "string") {
41828         this.minValue = this.parseDate(this.minValue);
41829     }
41830     if(typeof this.maxValue == "string") {
41831         this.maxValue = this.parseDate(this.maxValue);
41832     }
41833     this.ddMatch = null;
41834     if(this.disabledDates){
41835         var dd = this.disabledDates;
41836         var re = "(?:";
41837         for(var i = 0; i < dd.length; i++){
41838             re += dd[i];
41839             if(i != dd.length-1) {
41840                 re += "|";
41841             }
41842         }
41843         this.ddMatch = new RegExp(re + ")");
41844     }
41845 };
41846
41847 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41848     /**
41849      * @cfg {String} format
41850      * The default date format string which can be overriden for localization support.  The format must be
41851      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41852      */
41853     format : "m/d/y",
41854     /**
41855      * @cfg {String} altFormats
41856      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41857      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41858      */
41859     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41860     /**
41861      * @cfg {Array} disabledDays
41862      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41863      */
41864     disabledDays : null,
41865     /**
41866      * @cfg {String} disabledDaysText
41867      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41868      */
41869     disabledDaysText : "Disabled",
41870     /**
41871      * @cfg {Array} disabledDates
41872      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41873      * expression so they are very powerful. Some examples:
41874      * <ul>
41875      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41876      * <li>["03/08", "09/16"] would disable those days for every year</li>
41877      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41878      * <li>["03/../2006"] would disable every day in March 2006</li>
41879      * <li>["^03"] would disable every day in every March</li>
41880      * </ul>
41881      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41882      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41883      */
41884     disabledDates : null,
41885     /**
41886      * @cfg {String} disabledDatesText
41887      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41888      */
41889     disabledDatesText : "Disabled",
41890         
41891         
41892         /**
41893      * @cfg {Date/String} zeroValue
41894      * if the date is less that this number, then the field is rendered as empty
41895      * default is 1800
41896      */
41897         zeroValue : '1800-01-01',
41898         
41899         
41900     /**
41901      * @cfg {Date/String} minValue
41902      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41903      * valid format (defaults to null).
41904      */
41905     minValue : null,
41906     /**
41907      * @cfg {Date/String} maxValue
41908      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41909      * valid format (defaults to null).
41910      */
41911     maxValue : null,
41912     /**
41913      * @cfg {String} minText
41914      * The error text to display when the date in the cell is before minValue (defaults to
41915      * 'The date in this field must be after {minValue}').
41916      */
41917     minText : "The date in this field must be equal to or after {0}",
41918     /**
41919      * @cfg {String} maxText
41920      * The error text to display when the date in the cell is after maxValue (defaults to
41921      * 'The date in this field must be before {maxValue}').
41922      */
41923     maxText : "The date in this field must be equal to or before {0}",
41924     /**
41925      * @cfg {String} invalidText
41926      * The error text to display when the date in the field is invalid (defaults to
41927      * '{value} is not a valid date - it must be in the format {format}').
41928      */
41929     invalidText : "{0} is not a valid date - it must be in the format {1}",
41930     /**
41931      * @cfg {String} triggerClass
41932      * An additional CSS class used to style the trigger button.  The trigger will always get the
41933      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41934      * which displays a calendar icon).
41935      */
41936     triggerClass : 'x-form-date-trigger',
41937     
41938
41939     /**
41940      * @cfg {Boolean} useIso
41941      * if enabled, then the date field will use a hidden field to store the 
41942      * real value as iso formated date. default (false)
41943      */ 
41944     useIso : false,
41945     /**
41946      * @cfg {String/Object} autoCreate
41947      * A DomHelper element spec, or true for a default element spec (defaults to
41948      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41949      */ 
41950     // private
41951     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41952     
41953     // private
41954     hiddenField: false,
41955     
41956     onRender : function(ct, position)
41957     {
41958         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41959         if (this.useIso) {
41960             //this.el.dom.removeAttribute('name'); 
41961             Roo.log("Changing name?");
41962             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41963             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41964                     'before', true);
41965             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41966             // prevent input submission
41967             this.hiddenName = this.name;
41968         }
41969             
41970             
41971     },
41972     
41973     // private
41974     validateValue : function(value)
41975     {
41976         value = this.formatDate(value);
41977         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41978             Roo.log('super failed');
41979             return false;
41980         }
41981         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41982              return true;
41983         }
41984         var svalue = value;
41985         value = this.parseDate(value);
41986         if(!value){
41987             Roo.log('parse date failed' + svalue);
41988             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41989             return false;
41990         }
41991         var time = value.getTime();
41992         if(this.minValue && time < this.minValue.getTime()){
41993             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41994             return false;
41995         }
41996         if(this.maxValue && time > this.maxValue.getTime()){
41997             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41998             return false;
41999         }
42000         if(this.disabledDays){
42001             var day = value.getDay();
42002             for(var i = 0; i < this.disabledDays.length; i++) {
42003                 if(day === this.disabledDays[i]){
42004                     this.markInvalid(this.disabledDaysText);
42005                     return false;
42006                 }
42007             }
42008         }
42009         var fvalue = this.formatDate(value);
42010         if(this.ddMatch && this.ddMatch.test(fvalue)){
42011             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42012             return false;
42013         }
42014         return true;
42015     },
42016
42017     // private
42018     // Provides logic to override the default TriggerField.validateBlur which just returns true
42019     validateBlur : function(){
42020         return !this.menu || !this.menu.isVisible();
42021     },
42022     
42023     getName: function()
42024     {
42025         // returns hidden if it's set..
42026         if (!this.rendered) {return ''};
42027         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42028         
42029     },
42030
42031     /**
42032      * Returns the current date value of the date field.
42033      * @return {Date} The date value
42034      */
42035     getValue : function(){
42036         
42037         return  this.hiddenField ?
42038                 this.hiddenField.value :
42039                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42040     },
42041
42042     /**
42043      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42044      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42045      * (the default format used is "m/d/y").
42046      * <br />Usage:
42047      * <pre><code>
42048 //All of these calls set the same date value (May 4, 2006)
42049
42050 //Pass a date object:
42051 var dt = new Date('5/4/06');
42052 dateField.setValue(dt);
42053
42054 //Pass a date string (default format):
42055 dateField.setValue('5/4/06');
42056
42057 //Pass a date string (custom format):
42058 dateField.format = 'Y-m-d';
42059 dateField.setValue('2006-5-4');
42060 </code></pre>
42061      * @param {String/Date} date The date or valid date string
42062      */
42063     setValue : function(date){
42064         if (this.hiddenField) {
42065             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42066         }
42067         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42068         // make sure the value field is always stored as a date..
42069         this.value = this.parseDate(date);
42070         
42071         
42072     },
42073
42074     // private
42075     parseDate : function(value){
42076                 
42077                 if (value instanceof Date) {
42078                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42079                                 return  '';
42080                         }
42081                         return value;
42082                 }
42083                 
42084                 
42085         if(!value || value instanceof Date){
42086             return value;
42087         }
42088         var v = Date.parseDate(value, this.format);
42089          if (!v && this.useIso) {
42090             v = Date.parseDate(value, 'Y-m-d');
42091         }
42092         if(!v && this.altFormats){
42093             if(!this.altFormatsArray){
42094                 this.altFormatsArray = this.altFormats.split("|");
42095             }
42096             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42097                 v = Date.parseDate(value, this.altFormatsArray[i]);
42098             }
42099         }
42100                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42101                         v = '';
42102                 }
42103         return v;
42104     },
42105
42106     // private
42107     formatDate : function(date, fmt){
42108         return (!date || !(date instanceof Date)) ?
42109                date : date.dateFormat(fmt || this.format);
42110     },
42111
42112     // private
42113     menuListeners : {
42114         select: function(m, d){
42115             
42116             this.setValue(d);
42117             this.fireEvent('select', this, d);
42118         },
42119         show : function(){ // retain focus styling
42120             this.onFocus();
42121         },
42122         hide : function(){
42123             this.focus.defer(10, this);
42124             var ml = this.menuListeners;
42125             this.menu.un("select", ml.select,  this);
42126             this.menu.un("show", ml.show,  this);
42127             this.menu.un("hide", ml.hide,  this);
42128         }
42129     },
42130
42131     // private
42132     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42133     onTriggerClick : function(){
42134         if(this.disabled){
42135             return;
42136         }
42137         if(this.menu == null){
42138             this.menu = new Roo.menu.DateMenu();
42139         }
42140         Roo.apply(this.menu.picker,  {
42141             showClear: this.allowBlank,
42142             minDate : this.minValue,
42143             maxDate : this.maxValue,
42144             disabledDatesRE : this.ddMatch,
42145             disabledDatesText : this.disabledDatesText,
42146             disabledDays : this.disabledDays,
42147             disabledDaysText : this.disabledDaysText,
42148             format : this.useIso ? 'Y-m-d' : this.format,
42149             minText : String.format(this.minText, this.formatDate(this.minValue)),
42150             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42151         });
42152         this.menu.on(Roo.apply({}, this.menuListeners, {
42153             scope:this
42154         }));
42155         this.menu.picker.setValue(this.getValue() || new Date());
42156         this.menu.show(this.el, "tl-bl?");
42157     },
42158
42159     beforeBlur : function(){
42160         var v = this.parseDate(this.getRawValue());
42161         if(v){
42162             this.setValue(v);
42163         }
42164     },
42165
42166     /*@
42167      * overide
42168      * 
42169      */
42170     isDirty : function() {
42171         if(this.disabled) {
42172             return false;
42173         }
42174         
42175         if(typeof(this.startValue) === 'undefined'){
42176             return false;
42177         }
42178         
42179         return String(this.getValue()) !== String(this.startValue);
42180         
42181     },
42182     // @overide
42183     cleanLeadingSpace : function(e)
42184     {
42185        return;
42186     }
42187     
42188 });/*
42189  * Based on:
42190  * Ext JS Library 1.1.1
42191  * Copyright(c) 2006-2007, Ext JS, LLC.
42192  *
42193  * Originally Released Under LGPL - original licence link has changed is not relivant.
42194  *
42195  * Fork - LGPL
42196  * <script type="text/javascript">
42197  */
42198  
42199 /**
42200  * @class Roo.form.MonthField
42201  * @extends Roo.form.TriggerField
42202  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42203 * @constructor
42204 * Create a new MonthField
42205 * @param {Object} config
42206  */
42207 Roo.form.MonthField = function(config){
42208     
42209     Roo.form.MonthField.superclass.constructor.call(this, config);
42210     
42211       this.addEvents({
42212          
42213         /**
42214          * @event select
42215          * Fires when a date is selected
42216              * @param {Roo.form.MonthFieeld} combo This combo box
42217              * @param {Date} date The date selected
42218              */
42219         'select' : true
42220          
42221     });
42222     
42223     
42224     if(typeof this.minValue == "string") {
42225         this.minValue = this.parseDate(this.minValue);
42226     }
42227     if(typeof this.maxValue == "string") {
42228         this.maxValue = this.parseDate(this.maxValue);
42229     }
42230     this.ddMatch = null;
42231     if(this.disabledDates){
42232         var dd = this.disabledDates;
42233         var re = "(?:";
42234         for(var i = 0; i < dd.length; i++){
42235             re += dd[i];
42236             if(i != dd.length-1) {
42237                 re += "|";
42238             }
42239         }
42240         this.ddMatch = new RegExp(re + ")");
42241     }
42242 };
42243
42244 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42245     /**
42246      * @cfg {String} format
42247      * The default date format string which can be overriden for localization support.  The format must be
42248      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42249      */
42250     format : "M Y",
42251     /**
42252      * @cfg {String} altFormats
42253      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42254      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42255      */
42256     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42257     /**
42258      * @cfg {Array} disabledDays
42259      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42260      */
42261     disabledDays : [0,1,2,3,4,5,6],
42262     /**
42263      * @cfg {String} disabledDaysText
42264      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42265      */
42266     disabledDaysText : "Disabled",
42267     /**
42268      * @cfg {Array} disabledDates
42269      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42270      * expression so they are very powerful. Some examples:
42271      * <ul>
42272      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42273      * <li>["03/08", "09/16"] would disable those days for every year</li>
42274      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42275      * <li>["03/../2006"] would disable every day in March 2006</li>
42276      * <li>["^03"] would disable every day in every March</li>
42277      * </ul>
42278      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42279      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42280      */
42281     disabledDates : null,
42282     /**
42283      * @cfg {String} disabledDatesText
42284      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42285      */
42286     disabledDatesText : "Disabled",
42287     /**
42288      * @cfg {Date/String} minValue
42289      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42290      * valid format (defaults to null).
42291      */
42292     minValue : null,
42293     /**
42294      * @cfg {Date/String} maxValue
42295      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42296      * valid format (defaults to null).
42297      */
42298     maxValue : null,
42299     /**
42300      * @cfg {String} minText
42301      * The error text to display when the date in the cell is before minValue (defaults to
42302      * 'The date in this field must be after {minValue}').
42303      */
42304     minText : "The date in this field must be equal to or after {0}",
42305     /**
42306      * @cfg {String} maxTextf
42307      * The error text to display when the date in the cell is after maxValue (defaults to
42308      * 'The date in this field must be before {maxValue}').
42309      */
42310     maxText : "The date in this field must be equal to or before {0}",
42311     /**
42312      * @cfg {String} invalidText
42313      * The error text to display when the date in the field is invalid (defaults to
42314      * '{value} is not a valid date - it must be in the format {format}').
42315      */
42316     invalidText : "{0} is not a valid date - it must be in the format {1}",
42317     /**
42318      * @cfg {String} triggerClass
42319      * An additional CSS class used to style the trigger button.  The trigger will always get the
42320      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42321      * which displays a calendar icon).
42322      */
42323     triggerClass : 'x-form-date-trigger',
42324     
42325
42326     /**
42327      * @cfg {Boolean} useIso
42328      * if enabled, then the date field will use a hidden field to store the 
42329      * real value as iso formated date. default (true)
42330      */ 
42331     useIso : true,
42332     /**
42333      * @cfg {String/Object} autoCreate
42334      * A DomHelper element spec, or true for a default element spec (defaults to
42335      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42336      */ 
42337     // private
42338     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42339     
42340     // private
42341     hiddenField: false,
42342     
42343     hideMonthPicker : false,
42344     
42345     onRender : function(ct, position)
42346     {
42347         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42348         if (this.useIso) {
42349             this.el.dom.removeAttribute('name'); 
42350             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42351                     'before', true);
42352             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42353             // prevent input submission
42354             this.hiddenName = this.name;
42355         }
42356             
42357             
42358     },
42359     
42360     // private
42361     validateValue : function(value)
42362     {
42363         value = this.formatDate(value);
42364         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42365             return false;
42366         }
42367         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42368              return true;
42369         }
42370         var svalue = value;
42371         value = this.parseDate(value);
42372         if(!value){
42373             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42374             return false;
42375         }
42376         var time = value.getTime();
42377         if(this.minValue && time < this.minValue.getTime()){
42378             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42379             return false;
42380         }
42381         if(this.maxValue && time > this.maxValue.getTime()){
42382             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42383             return false;
42384         }
42385         /*if(this.disabledDays){
42386             var day = value.getDay();
42387             for(var i = 0; i < this.disabledDays.length; i++) {
42388                 if(day === this.disabledDays[i]){
42389                     this.markInvalid(this.disabledDaysText);
42390                     return false;
42391                 }
42392             }
42393         }
42394         */
42395         var fvalue = this.formatDate(value);
42396         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42397             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42398             return false;
42399         }
42400         */
42401         return true;
42402     },
42403
42404     // private
42405     // Provides logic to override the default TriggerField.validateBlur which just returns true
42406     validateBlur : function(){
42407         return !this.menu || !this.menu.isVisible();
42408     },
42409
42410     /**
42411      * Returns the current date value of the date field.
42412      * @return {Date} The date value
42413      */
42414     getValue : function(){
42415         
42416         
42417         
42418         return  this.hiddenField ?
42419                 this.hiddenField.value :
42420                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42421     },
42422
42423     /**
42424      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42425      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42426      * (the default format used is "m/d/y").
42427      * <br />Usage:
42428      * <pre><code>
42429 //All of these calls set the same date value (May 4, 2006)
42430
42431 //Pass a date object:
42432 var dt = new Date('5/4/06');
42433 monthField.setValue(dt);
42434
42435 //Pass a date string (default format):
42436 monthField.setValue('5/4/06');
42437
42438 //Pass a date string (custom format):
42439 monthField.format = 'Y-m-d';
42440 monthField.setValue('2006-5-4');
42441 </code></pre>
42442      * @param {String/Date} date The date or valid date string
42443      */
42444     setValue : function(date){
42445         Roo.log('month setValue' + date);
42446         // can only be first of month..
42447         
42448         var val = this.parseDate(date);
42449         
42450         if (this.hiddenField) {
42451             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42452         }
42453         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42454         this.value = this.parseDate(date);
42455     },
42456
42457     // private
42458     parseDate : function(value){
42459         if(!value || value instanceof Date){
42460             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42461             return value;
42462         }
42463         var v = Date.parseDate(value, this.format);
42464         if (!v && this.useIso) {
42465             v = Date.parseDate(value, 'Y-m-d');
42466         }
42467         if (v) {
42468             // 
42469             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42470         }
42471         
42472         
42473         if(!v && this.altFormats){
42474             if(!this.altFormatsArray){
42475                 this.altFormatsArray = this.altFormats.split("|");
42476             }
42477             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42478                 v = Date.parseDate(value, this.altFormatsArray[i]);
42479             }
42480         }
42481         return v;
42482     },
42483
42484     // private
42485     formatDate : function(date, fmt){
42486         return (!date || !(date instanceof Date)) ?
42487                date : date.dateFormat(fmt || this.format);
42488     },
42489
42490     // private
42491     menuListeners : {
42492         select: function(m, d){
42493             this.setValue(d);
42494             this.fireEvent('select', this, d);
42495         },
42496         show : function(){ // retain focus styling
42497             this.onFocus();
42498         },
42499         hide : function(){
42500             this.focus.defer(10, this);
42501             var ml = this.menuListeners;
42502             this.menu.un("select", ml.select,  this);
42503             this.menu.un("show", ml.show,  this);
42504             this.menu.un("hide", ml.hide,  this);
42505         }
42506     },
42507     // private
42508     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42509     onTriggerClick : function(){
42510         if(this.disabled){
42511             return;
42512         }
42513         if(this.menu == null){
42514             this.menu = new Roo.menu.DateMenu();
42515            
42516         }
42517         
42518         Roo.apply(this.menu.picker,  {
42519             
42520             showClear: this.allowBlank,
42521             minDate : this.minValue,
42522             maxDate : this.maxValue,
42523             disabledDatesRE : this.ddMatch,
42524             disabledDatesText : this.disabledDatesText,
42525             
42526             format : this.useIso ? 'Y-m-d' : this.format,
42527             minText : String.format(this.minText, this.formatDate(this.minValue)),
42528             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42529             
42530         });
42531          this.menu.on(Roo.apply({}, this.menuListeners, {
42532             scope:this
42533         }));
42534        
42535         
42536         var m = this.menu;
42537         var p = m.picker;
42538         
42539         // hide month picker get's called when we called by 'before hide';
42540         
42541         var ignorehide = true;
42542         p.hideMonthPicker  = function(disableAnim){
42543             if (ignorehide) {
42544                 return;
42545             }
42546              if(this.monthPicker){
42547                 Roo.log("hideMonthPicker called");
42548                 if(disableAnim === true){
42549                     this.monthPicker.hide();
42550                 }else{
42551                     this.monthPicker.slideOut('t', {duration:.2});
42552                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42553                     p.fireEvent("select", this, this.value);
42554                     m.hide();
42555                 }
42556             }
42557         }
42558         
42559         Roo.log('picker set value');
42560         Roo.log(this.getValue());
42561         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42562         m.show(this.el, 'tl-bl?');
42563         ignorehide  = false;
42564         // this will trigger hideMonthPicker..
42565         
42566         
42567         // hidden the day picker
42568         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42569         
42570         
42571         
42572       
42573         
42574         p.showMonthPicker.defer(100, p);
42575     
42576         
42577        
42578     },
42579
42580     beforeBlur : function(){
42581         var v = this.parseDate(this.getRawValue());
42582         if(v){
42583             this.setValue(v);
42584         }
42585     }
42586
42587     /** @cfg {Boolean} grow @hide */
42588     /** @cfg {Number} growMin @hide */
42589     /** @cfg {Number} growMax @hide */
42590     /**
42591      * @hide
42592      * @method autoSize
42593      */
42594 });/*
42595  * Based on:
42596  * Ext JS Library 1.1.1
42597  * Copyright(c) 2006-2007, Ext JS, LLC.
42598  *
42599  * Originally Released Under LGPL - original licence link has changed is not relivant.
42600  *
42601  * Fork - LGPL
42602  * <script type="text/javascript">
42603  */
42604  
42605
42606 /**
42607  * @class Roo.form.ComboBox
42608  * @extends Roo.form.TriggerField
42609  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42610  * @constructor
42611  * Create a new ComboBox.
42612  * @param {Object} config Configuration options
42613  */
42614 Roo.form.ComboBox = function(config){
42615     Roo.form.ComboBox.superclass.constructor.call(this, config);
42616     this.addEvents({
42617         /**
42618          * @event expand
42619          * Fires when the dropdown list is expanded
42620              * @param {Roo.form.ComboBox} combo This combo box
42621              */
42622         'expand' : true,
42623         /**
42624          * @event collapse
42625          * Fires when the dropdown list is collapsed
42626              * @param {Roo.form.ComboBox} combo This combo box
42627              */
42628         'collapse' : true,
42629         /**
42630          * @event beforeselect
42631          * Fires before a list item is selected. Return false to cancel the selection.
42632              * @param {Roo.form.ComboBox} combo This combo box
42633              * @param {Roo.data.Record} record The data record returned from the underlying store
42634              * @param {Number} index The index of the selected item in the dropdown list
42635              */
42636         'beforeselect' : true,
42637         /**
42638          * @event select
42639          * Fires when a list item is selected
42640              * @param {Roo.form.ComboBox} combo This combo box
42641              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42642              * @param {Number} index The index of the selected item in the dropdown list
42643              */
42644         'select' : true,
42645         /**
42646          * @event beforequery
42647          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42648          * The event object passed has these properties:
42649              * @param {Roo.form.ComboBox} combo This combo box
42650              * @param {String} query The query
42651              * @param {Boolean} forceAll true to force "all" query
42652              * @param {Boolean} cancel true to cancel the query
42653              * @param {Object} e The query event object
42654              */
42655         'beforequery': true,
42656          /**
42657          * @event add
42658          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42659              * @param {Roo.form.ComboBox} combo This combo box
42660              */
42661         'add' : true,
42662         /**
42663          * @event edit
42664          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42665              * @param {Roo.form.ComboBox} combo This combo box
42666              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42667              */
42668         'edit' : true
42669         
42670         
42671     });
42672     if(this.transform){
42673         this.allowDomMove = false;
42674         var s = Roo.getDom(this.transform);
42675         if(!this.hiddenName){
42676             this.hiddenName = s.name;
42677         }
42678         if(!this.store){
42679             this.mode = 'local';
42680             var d = [], opts = s.options;
42681             for(var i = 0, len = opts.length;i < len; i++){
42682                 var o = opts[i];
42683                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42684                 if(o.selected) {
42685                     this.value = value;
42686                 }
42687                 d.push([value, o.text]);
42688             }
42689             this.store = new Roo.data.SimpleStore({
42690                 'id': 0,
42691                 fields: ['value', 'text'],
42692                 data : d
42693             });
42694             this.valueField = 'value';
42695             this.displayField = 'text';
42696         }
42697         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42698         if(!this.lazyRender){
42699             this.target = true;
42700             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42701             s.parentNode.removeChild(s); // remove it
42702             this.render(this.el.parentNode);
42703         }else{
42704             s.parentNode.removeChild(s); // remove it
42705         }
42706
42707     }
42708     if (this.store) {
42709         this.store = Roo.factory(this.store, Roo.data);
42710     }
42711     
42712     this.selectedIndex = -1;
42713     if(this.mode == 'local'){
42714         if(config.queryDelay === undefined){
42715             this.queryDelay = 10;
42716         }
42717         if(config.minChars === undefined){
42718             this.minChars = 0;
42719         }
42720     }
42721 };
42722
42723 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42724     /**
42725      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42726      */
42727     /**
42728      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42729      * rendering into an Roo.Editor, defaults to false)
42730      */
42731     /**
42732      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42733      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42734      */
42735     /**
42736      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42737      */
42738     /**
42739      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42740      * the dropdown list (defaults to undefined, with no header element)
42741      */
42742
42743      /**
42744      * @cfg {String/Roo.Template} tpl The template to use to render the output
42745      */
42746      
42747     // private
42748     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42749     /**
42750      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42751      */
42752     listWidth: undefined,
42753     /**
42754      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42755      * mode = 'remote' or 'text' if mode = 'local')
42756      */
42757     displayField: undefined,
42758     /**
42759      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42760      * mode = 'remote' or 'value' if mode = 'local'). 
42761      * Note: use of a valueField requires the user make a selection
42762      * in order for a value to be mapped.
42763      */
42764     valueField: undefined,
42765     
42766     
42767     /**
42768      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42769      * field's data value (defaults to the underlying DOM element's name)
42770      */
42771     hiddenName: undefined,
42772     /**
42773      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42774      */
42775     listClass: '',
42776     /**
42777      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42778      */
42779     selectedClass: 'x-combo-selected',
42780     /**
42781      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42782      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42783      * which displays a downward arrow icon).
42784      */
42785     triggerClass : 'x-form-arrow-trigger',
42786     /**
42787      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42788      */
42789     shadow:'sides',
42790     /**
42791      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42792      * anchor positions (defaults to 'tl-bl')
42793      */
42794     listAlign: 'tl-bl?',
42795     /**
42796      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42797      */
42798     maxHeight: 300,
42799     /**
42800      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42801      * query specified by the allQuery config option (defaults to 'query')
42802      */
42803     triggerAction: 'query',
42804     /**
42805      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42806      * (defaults to 4, does not apply if editable = false)
42807      */
42808     minChars : 4,
42809     /**
42810      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42811      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42812      */
42813     typeAhead: false,
42814     /**
42815      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42816      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42817      */
42818     queryDelay: 500,
42819     /**
42820      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42821      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42822      */
42823     pageSize: 0,
42824     /**
42825      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42826      * when editable = true (defaults to false)
42827      */
42828     selectOnFocus:false,
42829     /**
42830      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42831      */
42832     queryParam: 'query',
42833     /**
42834      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42835      * when mode = 'remote' (defaults to 'Loading...')
42836      */
42837     loadingText: 'Loading...',
42838     /**
42839      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42840      */
42841     resizable: false,
42842     /**
42843      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42844      */
42845     handleHeight : 8,
42846     /**
42847      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42848      * traditional select (defaults to true)
42849      */
42850     editable: true,
42851     /**
42852      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42853      */
42854     allQuery: '',
42855     /**
42856      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42857      */
42858     mode: 'remote',
42859     /**
42860      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42861      * listWidth has a higher value)
42862      */
42863     minListWidth : 70,
42864     /**
42865      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42866      * allow the user to set arbitrary text into the field (defaults to false)
42867      */
42868     forceSelection:false,
42869     /**
42870      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42871      * if typeAhead = true (defaults to 250)
42872      */
42873     typeAheadDelay : 250,
42874     /**
42875      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42876      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42877      */
42878     valueNotFoundText : undefined,
42879     /**
42880      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42881      */
42882     blockFocus : false,
42883     
42884     /**
42885      * @cfg {Boolean} disableClear Disable showing of clear button.
42886      */
42887     disableClear : false,
42888     /**
42889      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42890      */
42891     alwaysQuery : false,
42892     
42893     //private
42894     addicon : false,
42895     editicon: false,
42896     
42897     // element that contains real text value.. (when hidden is used..)
42898      
42899     // private
42900     onRender : function(ct, position)
42901     {
42902         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42903         
42904         if(this.hiddenName){
42905             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42906                     'before', true);
42907             this.hiddenField.value =
42908                 this.hiddenValue !== undefined ? this.hiddenValue :
42909                 this.value !== undefined ? this.value : '';
42910
42911             // prevent input submission
42912             this.el.dom.removeAttribute('name');
42913              
42914              
42915         }
42916         
42917         if(Roo.isGecko){
42918             this.el.dom.setAttribute('autocomplete', 'off');
42919         }
42920
42921         var cls = 'x-combo-list';
42922
42923         this.list = new Roo.Layer({
42924             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42925         });
42926
42927         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42928         this.list.setWidth(lw);
42929         this.list.swallowEvent('mousewheel');
42930         this.assetHeight = 0;
42931
42932         if(this.title){
42933             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42934             this.assetHeight += this.header.getHeight();
42935         }
42936
42937         this.innerList = this.list.createChild({cls:cls+'-inner'});
42938         this.innerList.on('mouseover', this.onViewOver, this);
42939         this.innerList.on('mousemove', this.onViewMove, this);
42940         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42941         
42942         if(this.allowBlank && !this.pageSize && !this.disableClear){
42943             this.footer = this.list.createChild({cls:cls+'-ft'});
42944             this.pageTb = new Roo.Toolbar(this.footer);
42945            
42946         }
42947         if(this.pageSize){
42948             this.footer = this.list.createChild({cls:cls+'-ft'});
42949             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42950                     {pageSize: this.pageSize});
42951             
42952         }
42953         
42954         if (this.pageTb && this.allowBlank && !this.disableClear) {
42955             var _this = this;
42956             this.pageTb.add(new Roo.Toolbar.Fill(), {
42957                 cls: 'x-btn-icon x-btn-clear',
42958                 text: '&#160;',
42959                 handler: function()
42960                 {
42961                     _this.collapse();
42962                     _this.clearValue();
42963                     _this.onSelect(false, -1);
42964                 }
42965             });
42966         }
42967         if (this.footer) {
42968             this.assetHeight += this.footer.getHeight();
42969         }
42970         
42971
42972         if(!this.tpl){
42973             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42974         }
42975
42976         this.view = new Roo.View(this.innerList, this.tpl, {
42977             singleSelect:true,
42978             store: this.store,
42979             selectedClass: this.selectedClass
42980         });
42981
42982         this.view.on('click', this.onViewClick, this);
42983
42984         this.store.on('beforeload', this.onBeforeLoad, this);
42985         this.store.on('load', this.onLoad, this);
42986         this.store.on('loadexception', this.onLoadException, this);
42987
42988         if(this.resizable){
42989             this.resizer = new Roo.Resizable(this.list,  {
42990                pinned:true, handles:'se'
42991             });
42992             this.resizer.on('resize', function(r, w, h){
42993                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42994                 this.listWidth = w;
42995                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42996                 this.restrictHeight();
42997             }, this);
42998             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42999         }
43000         if(!this.editable){
43001             this.editable = true;
43002             this.setEditable(false);
43003         }  
43004         
43005         
43006         if (typeof(this.events.add.listeners) != 'undefined') {
43007             
43008             this.addicon = this.wrap.createChild(
43009                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43010        
43011             this.addicon.on('click', function(e) {
43012                 this.fireEvent('add', this);
43013             }, this);
43014         }
43015         if (typeof(this.events.edit.listeners) != 'undefined') {
43016             
43017             this.editicon = this.wrap.createChild(
43018                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43019             if (this.addicon) {
43020                 this.editicon.setStyle('margin-left', '40px');
43021             }
43022             this.editicon.on('click', function(e) {
43023                 
43024                 // we fire even  if inothing is selected..
43025                 this.fireEvent('edit', this, this.lastData );
43026                 
43027             }, this);
43028         }
43029         
43030         
43031         
43032     },
43033
43034     // private
43035     initEvents : function(){
43036         Roo.form.ComboBox.superclass.initEvents.call(this);
43037
43038         this.keyNav = new Roo.KeyNav(this.el, {
43039             "up" : function(e){
43040                 this.inKeyMode = true;
43041                 this.selectPrev();
43042             },
43043
43044             "down" : function(e){
43045                 if(!this.isExpanded()){
43046                     this.onTriggerClick();
43047                 }else{
43048                     this.inKeyMode = true;
43049                     this.selectNext();
43050                 }
43051             },
43052
43053             "enter" : function(e){
43054                 this.onViewClick();
43055                 //return true;
43056             },
43057
43058             "esc" : function(e){
43059                 this.collapse();
43060             },
43061
43062             "tab" : function(e){
43063                 this.onViewClick(false);
43064                 this.fireEvent("specialkey", this, e);
43065                 return true;
43066             },
43067
43068             scope : this,
43069
43070             doRelay : function(foo, bar, hname){
43071                 if(hname == 'down' || this.scope.isExpanded()){
43072                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43073                 }
43074                 return true;
43075             },
43076
43077             forceKeyDown: true
43078         });
43079         this.queryDelay = Math.max(this.queryDelay || 10,
43080                 this.mode == 'local' ? 10 : 250);
43081         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43082         if(this.typeAhead){
43083             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43084         }
43085         if(this.editable !== false){
43086             this.el.on("keyup", this.onKeyUp, this);
43087         }
43088         if(this.forceSelection){
43089             this.on('blur', this.doForce, this);
43090         }
43091     },
43092
43093     onDestroy : function(){
43094         if(this.view){
43095             this.view.setStore(null);
43096             this.view.el.removeAllListeners();
43097             this.view.el.remove();
43098             this.view.purgeListeners();
43099         }
43100         if(this.list){
43101             this.list.destroy();
43102         }
43103         if(this.store){
43104             this.store.un('beforeload', this.onBeforeLoad, this);
43105             this.store.un('load', this.onLoad, this);
43106             this.store.un('loadexception', this.onLoadException, this);
43107         }
43108         Roo.form.ComboBox.superclass.onDestroy.call(this);
43109     },
43110
43111     // private
43112     fireKey : function(e){
43113         if(e.isNavKeyPress() && !this.list.isVisible()){
43114             this.fireEvent("specialkey", this, e);
43115         }
43116     },
43117
43118     // private
43119     onResize: function(w, h){
43120         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43121         
43122         if(typeof w != 'number'){
43123             // we do not handle it!?!?
43124             return;
43125         }
43126         var tw = this.trigger.getWidth();
43127         tw += this.addicon ? this.addicon.getWidth() : 0;
43128         tw += this.editicon ? this.editicon.getWidth() : 0;
43129         var x = w - tw;
43130         this.el.setWidth( this.adjustWidth('input', x));
43131             
43132         this.trigger.setStyle('left', x+'px');
43133         
43134         if(this.list && this.listWidth === undefined){
43135             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43136             this.list.setWidth(lw);
43137             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43138         }
43139         
43140     
43141         
43142     },
43143
43144     /**
43145      * Allow or prevent the user from directly editing the field text.  If false is passed,
43146      * the user will only be able to select from the items defined in the dropdown list.  This method
43147      * is the runtime equivalent of setting the 'editable' config option at config time.
43148      * @param {Boolean} value True to allow the user to directly edit the field text
43149      */
43150     setEditable : function(value){
43151         if(value == this.editable){
43152             return;
43153         }
43154         this.editable = value;
43155         if(!value){
43156             this.el.dom.setAttribute('readOnly', true);
43157             this.el.on('mousedown', this.onTriggerClick,  this);
43158             this.el.addClass('x-combo-noedit');
43159         }else{
43160             this.el.dom.setAttribute('readOnly', false);
43161             this.el.un('mousedown', this.onTriggerClick,  this);
43162             this.el.removeClass('x-combo-noedit');
43163         }
43164     },
43165
43166     // private
43167     onBeforeLoad : function(){
43168         if(!this.hasFocus){
43169             return;
43170         }
43171         this.innerList.update(this.loadingText ?
43172                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43173         this.restrictHeight();
43174         this.selectedIndex = -1;
43175     },
43176
43177     // private
43178     onLoad : function(){
43179         if(!this.hasFocus){
43180             return;
43181         }
43182         if(this.store.getCount() > 0){
43183             this.expand();
43184             this.restrictHeight();
43185             if(this.lastQuery == this.allQuery){
43186                 if(this.editable){
43187                     this.el.dom.select();
43188                 }
43189                 if(!this.selectByValue(this.value, true)){
43190                     this.select(0, true);
43191                 }
43192             }else{
43193                 this.selectNext();
43194                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43195                     this.taTask.delay(this.typeAheadDelay);
43196                 }
43197             }
43198         }else{
43199             this.onEmptyResults();
43200         }
43201         //this.el.focus();
43202     },
43203     // private
43204     onLoadException : function()
43205     {
43206         this.collapse();
43207         Roo.log(this.store.reader.jsonData);
43208         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43209             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43210         }
43211         
43212         
43213     },
43214     // private
43215     onTypeAhead : function(){
43216         if(this.store.getCount() > 0){
43217             var r = this.store.getAt(0);
43218             var newValue = r.data[this.displayField];
43219             var len = newValue.length;
43220             var selStart = this.getRawValue().length;
43221             if(selStart != len){
43222                 this.setRawValue(newValue);
43223                 this.selectText(selStart, newValue.length);
43224             }
43225         }
43226     },
43227
43228     // private
43229     onSelect : function(record, index){
43230         if(this.fireEvent('beforeselect', this, record, index) !== false){
43231             this.setFromData(index > -1 ? record.data : false);
43232             this.collapse();
43233             this.fireEvent('select', this, record, index);
43234         }
43235     },
43236
43237     /**
43238      * Returns the currently selected field value or empty string if no value is set.
43239      * @return {String} value The selected value
43240      */
43241     getValue : function(){
43242         if(this.valueField){
43243             return typeof this.value != 'undefined' ? this.value : '';
43244         }
43245         return Roo.form.ComboBox.superclass.getValue.call(this);
43246     },
43247
43248     /**
43249      * Clears any text/value currently set in the field
43250      */
43251     clearValue : function(){
43252         if(this.hiddenField){
43253             this.hiddenField.value = '';
43254         }
43255         this.value = '';
43256         this.setRawValue('');
43257         this.lastSelectionText = '';
43258         
43259     },
43260
43261     /**
43262      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43263      * will be displayed in the field.  If the value does not match the data value of an existing item,
43264      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43265      * Otherwise the field will be blank (although the value will still be set).
43266      * @param {String} value The value to match
43267      */
43268     setValue : function(v){
43269         var text = v;
43270         if(this.valueField){
43271             var r = this.findRecord(this.valueField, v);
43272             if(r){
43273                 text = r.data[this.displayField];
43274             }else if(this.valueNotFoundText !== undefined){
43275                 text = this.valueNotFoundText;
43276             }
43277         }
43278         this.lastSelectionText = text;
43279         if(this.hiddenField){
43280             this.hiddenField.value = v;
43281         }
43282         Roo.form.ComboBox.superclass.setValue.call(this, text);
43283         this.value = v;
43284     },
43285     /**
43286      * @property {Object} the last set data for the element
43287      */
43288     
43289     lastData : false,
43290     /**
43291      * Sets the value of the field based on a object which is related to the record format for the store.
43292      * @param {Object} value the value to set as. or false on reset?
43293      */
43294     setFromData : function(o){
43295         var dv = ''; // display value
43296         var vv = ''; // value value..
43297         this.lastData = o;
43298         if (this.displayField) {
43299             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43300         } else {
43301             // this is an error condition!!!
43302             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43303         }
43304         
43305         if(this.valueField){
43306             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43307         }
43308         if(this.hiddenField){
43309             this.hiddenField.value = vv;
43310             
43311             this.lastSelectionText = dv;
43312             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43313             this.value = vv;
43314             return;
43315         }
43316         // no hidden field.. - we store the value in 'value', but still display
43317         // display field!!!!
43318         this.lastSelectionText = dv;
43319         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43320         this.value = vv;
43321         
43322         
43323     },
43324     // private
43325     reset : function(){
43326         // overridden so that last data is reset..
43327         this.setValue(this.resetValue);
43328         this.originalValue = this.getValue();
43329         this.clearInvalid();
43330         this.lastData = false;
43331         if (this.view) {
43332             this.view.clearSelections();
43333         }
43334     },
43335     // private
43336     findRecord : function(prop, value){
43337         var record;
43338         if(this.store.getCount() > 0){
43339             this.store.each(function(r){
43340                 if(r.data[prop] == value){
43341                     record = r;
43342                     return false;
43343                 }
43344                 return true;
43345             });
43346         }
43347         return record;
43348     },
43349     
43350     getName: function()
43351     {
43352         // returns hidden if it's set..
43353         if (!this.rendered) {return ''};
43354         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43355         
43356     },
43357     // private
43358     onViewMove : function(e, t){
43359         this.inKeyMode = false;
43360     },
43361
43362     // private
43363     onViewOver : function(e, t){
43364         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43365             return;
43366         }
43367         var item = this.view.findItemFromChild(t);
43368         if(item){
43369             var index = this.view.indexOf(item);
43370             this.select(index, false);
43371         }
43372     },
43373
43374     // private
43375     onViewClick : function(doFocus)
43376     {
43377         var index = this.view.getSelectedIndexes()[0];
43378         var r = this.store.getAt(index);
43379         if(r){
43380             this.onSelect(r, index);
43381         }
43382         if(doFocus !== false && !this.blockFocus){
43383             this.el.focus();
43384         }
43385     },
43386
43387     // private
43388     restrictHeight : function(){
43389         this.innerList.dom.style.height = '';
43390         var inner = this.innerList.dom;
43391         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43392         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43393         this.list.beginUpdate();
43394         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43395         this.list.alignTo(this.el, this.listAlign);
43396         this.list.endUpdate();
43397     },
43398
43399     // private
43400     onEmptyResults : function(){
43401         this.collapse();
43402     },
43403
43404     /**
43405      * Returns true if the dropdown list is expanded, else false.
43406      */
43407     isExpanded : function(){
43408         return this.list.isVisible();
43409     },
43410
43411     /**
43412      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43414      * @param {String} value The data value of the item to select
43415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43416      * selected item if it is not currently in view (defaults to true)
43417      * @return {Boolean} True if the value matched an item in the list, else false
43418      */
43419     selectByValue : function(v, scrollIntoView){
43420         if(v !== undefined && v !== null){
43421             var r = this.findRecord(this.valueField || this.displayField, v);
43422             if(r){
43423                 this.select(this.store.indexOf(r), scrollIntoView);
43424                 return true;
43425             }
43426         }
43427         return false;
43428     },
43429
43430     /**
43431      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43432      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43433      * @param {Number} index The zero-based index of the list item to select
43434      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43435      * selected item if it is not currently in view (defaults to true)
43436      */
43437     select : function(index, scrollIntoView){
43438         this.selectedIndex = index;
43439         this.view.select(index);
43440         if(scrollIntoView !== false){
43441             var el = this.view.getNode(index);
43442             if(el){
43443                 this.innerList.scrollChildIntoView(el, false);
43444             }
43445         }
43446     },
43447
43448     // private
43449     selectNext : function(){
43450         var ct = this.store.getCount();
43451         if(ct > 0){
43452             if(this.selectedIndex == -1){
43453                 this.select(0);
43454             }else if(this.selectedIndex < ct-1){
43455                 this.select(this.selectedIndex+1);
43456             }
43457         }
43458     },
43459
43460     // private
43461     selectPrev : function(){
43462         var ct = this.store.getCount();
43463         if(ct > 0){
43464             if(this.selectedIndex == -1){
43465                 this.select(0);
43466             }else if(this.selectedIndex != 0){
43467                 this.select(this.selectedIndex-1);
43468             }
43469         }
43470     },
43471
43472     // private
43473     onKeyUp : function(e){
43474         if(this.editable !== false && !e.isSpecialKey()){
43475             this.lastKey = e.getKey();
43476             this.dqTask.delay(this.queryDelay);
43477         }
43478     },
43479
43480     // private
43481     validateBlur : function(){
43482         return !this.list || !this.list.isVisible();   
43483     },
43484
43485     // private
43486     initQuery : function(){
43487         this.doQuery(this.getRawValue());
43488     },
43489
43490     // private
43491     doForce : function(){
43492         if(this.el.dom.value.length > 0){
43493             this.el.dom.value =
43494                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43495              
43496         }
43497     },
43498
43499     /**
43500      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43501      * query allowing the query action to be canceled if needed.
43502      * @param {String} query The SQL query to execute
43503      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43504      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43505      * saved in the current store (defaults to false)
43506      */
43507     doQuery : function(q, forceAll){
43508         if(q === undefined || q === null){
43509             q = '';
43510         }
43511         var qe = {
43512             query: q,
43513             forceAll: forceAll,
43514             combo: this,
43515             cancel:false
43516         };
43517         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43518             return false;
43519         }
43520         q = qe.query;
43521         forceAll = qe.forceAll;
43522         if(forceAll === true || (q.length >= this.minChars)){
43523             if(this.lastQuery != q || this.alwaysQuery){
43524                 this.lastQuery = q;
43525                 if(this.mode == 'local'){
43526                     this.selectedIndex = -1;
43527                     if(forceAll){
43528                         this.store.clearFilter();
43529                     }else{
43530                         this.store.filter(this.displayField, q);
43531                     }
43532                     this.onLoad();
43533                 }else{
43534                     this.store.baseParams[this.queryParam] = q;
43535                     this.store.load({
43536                         params: this.getParams(q)
43537                     });
43538                     this.expand();
43539                 }
43540             }else{
43541                 this.selectedIndex = -1;
43542                 this.onLoad();   
43543             }
43544         }
43545     },
43546
43547     // private
43548     getParams : function(q){
43549         var p = {};
43550         //p[this.queryParam] = q;
43551         if(this.pageSize){
43552             p.start = 0;
43553             p.limit = this.pageSize;
43554         }
43555         return p;
43556     },
43557
43558     /**
43559      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43560      */
43561     collapse : function(){
43562         if(!this.isExpanded()){
43563             return;
43564         }
43565         this.list.hide();
43566         Roo.get(document).un('mousedown', this.collapseIf, this);
43567         Roo.get(document).un('mousewheel', this.collapseIf, this);
43568         if (!this.editable) {
43569             Roo.get(document).un('keydown', this.listKeyPress, this);
43570         }
43571         this.fireEvent('collapse', this);
43572     },
43573
43574     // private
43575     collapseIf : function(e){
43576         if(!e.within(this.wrap) && !e.within(this.list)){
43577             this.collapse();
43578         }
43579     },
43580
43581     /**
43582      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43583      */
43584     expand : function(){
43585         if(this.isExpanded() || !this.hasFocus){
43586             return;
43587         }
43588         this.list.alignTo(this.el, this.listAlign);
43589         this.list.show();
43590         Roo.get(document).on('mousedown', this.collapseIf, this);
43591         Roo.get(document).on('mousewheel', this.collapseIf, this);
43592         if (!this.editable) {
43593             Roo.get(document).on('keydown', this.listKeyPress, this);
43594         }
43595         
43596         this.fireEvent('expand', this);
43597     },
43598
43599     // private
43600     // Implements the default empty TriggerField.onTriggerClick function
43601     onTriggerClick : function(){
43602         if(this.disabled){
43603             return;
43604         }
43605         if(this.isExpanded()){
43606             this.collapse();
43607             if (!this.blockFocus) {
43608                 this.el.focus();
43609             }
43610             
43611         }else {
43612             this.hasFocus = true;
43613             if(this.triggerAction == 'all') {
43614                 this.doQuery(this.allQuery, true);
43615             } else {
43616                 this.doQuery(this.getRawValue());
43617             }
43618             if (!this.blockFocus) {
43619                 this.el.focus();
43620             }
43621         }
43622     },
43623     listKeyPress : function(e)
43624     {
43625         //Roo.log('listkeypress');
43626         // scroll to first matching element based on key pres..
43627         if (e.isSpecialKey()) {
43628             return false;
43629         }
43630         var k = String.fromCharCode(e.getKey()).toUpperCase();
43631         //Roo.log(k);
43632         var match  = false;
43633         var csel = this.view.getSelectedNodes();
43634         var cselitem = false;
43635         if (csel.length) {
43636             var ix = this.view.indexOf(csel[0]);
43637             cselitem  = this.store.getAt(ix);
43638             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43639                 cselitem = false;
43640             }
43641             
43642         }
43643         
43644         this.store.each(function(v) { 
43645             if (cselitem) {
43646                 // start at existing selection.
43647                 if (cselitem.id == v.id) {
43648                     cselitem = false;
43649                 }
43650                 return;
43651             }
43652                 
43653             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43654                 match = this.store.indexOf(v);
43655                 return false;
43656             }
43657         }, this);
43658         
43659         if (match === false) {
43660             return true; // no more action?
43661         }
43662         // scroll to?
43663         this.view.select(match);
43664         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43665         sn.scrollIntoView(sn.dom.parentNode, false);
43666     } 
43667
43668     /** 
43669     * @cfg {Boolean} grow 
43670     * @hide 
43671     */
43672     /** 
43673     * @cfg {Number} growMin 
43674     * @hide 
43675     */
43676     /** 
43677     * @cfg {Number} growMax 
43678     * @hide 
43679     */
43680     /**
43681      * @hide
43682      * @method autoSize
43683      */
43684 });/*
43685  * Copyright(c) 2010-2012, Roo J Solutions Limited
43686  *
43687  * Licence LGPL
43688  *
43689  */
43690
43691 /**
43692  * @class Roo.form.ComboBoxArray
43693  * @extends Roo.form.TextField
43694  * A facebook style adder... for lists of email / people / countries  etc...
43695  * pick multiple items from a combo box, and shows each one.
43696  *
43697  *  Fred [x]  Brian [x]  [Pick another |v]
43698  *
43699  *
43700  *  For this to work: it needs various extra information
43701  *    - normal combo problay has
43702  *      name, hiddenName
43703  *    + displayField, valueField
43704  *
43705  *    For our purpose...
43706  *
43707  *
43708  *   If we change from 'extends' to wrapping...
43709  *   
43710  *  
43711  *
43712  
43713  
43714  * @constructor
43715  * Create a new ComboBoxArray.
43716  * @param {Object} config Configuration options
43717  */
43718  
43719
43720 Roo.form.ComboBoxArray = function(config)
43721 {
43722     this.addEvents({
43723         /**
43724          * @event beforeremove
43725          * Fires before remove the value from the list
43726              * @param {Roo.form.ComboBoxArray} _self This combo box array
43727              * @param {Roo.form.ComboBoxArray.Item} item removed item
43728              */
43729         'beforeremove' : true,
43730         /**
43731          * @event remove
43732          * Fires when remove the value from the list
43733              * @param {Roo.form.ComboBoxArray} _self This combo box array
43734              * @param {Roo.form.ComboBoxArray.Item} item removed item
43735              */
43736         'remove' : true
43737         
43738         
43739     });
43740     
43741     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43742     
43743     this.items = new Roo.util.MixedCollection(false);
43744     
43745     // construct the child combo...
43746     
43747     
43748     
43749     
43750    
43751     
43752 }
43753
43754  
43755 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43756
43757     /**
43758      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43759      */
43760     
43761     lastData : false,
43762     
43763     // behavies liek a hiddne field
43764     inputType:      'hidden',
43765     /**
43766      * @cfg {Number} width The width of the box that displays the selected element
43767      */ 
43768     width:          300,
43769
43770     
43771     
43772     /**
43773      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43774      */
43775     name : false,
43776     /**
43777      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43778      */
43779     hiddenName : false,
43780       /**
43781      * @cfg {String} seperator    The value seperator normally ',' 
43782      */
43783     seperator : ',',
43784     
43785     // private the array of items that are displayed..
43786     items  : false,
43787     // private - the hidden field el.
43788     hiddenEl : false,
43789     // private - the filed el..
43790     el : false,
43791     
43792     //validateValue : function() { return true; }, // all values are ok!
43793     //onAddClick: function() { },
43794     
43795     onRender : function(ct, position) 
43796     {
43797         
43798         // create the standard hidden element
43799         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43800         
43801         
43802         // give fake names to child combo;
43803         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43804         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43805         
43806         this.combo = Roo.factory(this.combo, Roo.form);
43807         this.combo.onRender(ct, position);
43808         if (typeof(this.combo.width) != 'undefined') {
43809             this.combo.onResize(this.combo.width,0);
43810         }
43811         
43812         this.combo.initEvents();
43813         
43814         // assigned so form know we need to do this..
43815         this.store          = this.combo.store;
43816         this.valueField     = this.combo.valueField;
43817         this.displayField   = this.combo.displayField ;
43818         
43819         
43820         this.combo.wrap.addClass('x-cbarray-grp');
43821         
43822         var cbwrap = this.combo.wrap.createChild(
43823             {tag: 'div', cls: 'x-cbarray-cb'},
43824             this.combo.el.dom
43825         );
43826         
43827              
43828         this.hiddenEl = this.combo.wrap.createChild({
43829             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43830         });
43831         this.el = this.combo.wrap.createChild({
43832             tag: 'input',  type:'hidden' , name: this.name, value : ''
43833         });
43834          //   this.el.dom.removeAttribute("name");
43835         
43836         
43837         this.outerWrap = this.combo.wrap;
43838         this.wrap = cbwrap;
43839         
43840         this.outerWrap.setWidth(this.width);
43841         this.outerWrap.dom.removeChild(this.el.dom);
43842         
43843         this.wrap.dom.appendChild(this.el.dom);
43844         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43845         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43846         
43847         this.combo.trigger.setStyle('position','relative');
43848         this.combo.trigger.setStyle('left', '0px');
43849         this.combo.trigger.setStyle('top', '2px');
43850         
43851         this.combo.el.setStyle('vertical-align', 'text-bottom');
43852         
43853         //this.trigger.setStyle('vertical-align', 'top');
43854         
43855         // this should use the code from combo really... on('add' ....)
43856         if (this.adder) {
43857             
43858         
43859             this.adder = this.outerWrap.createChild(
43860                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43861             var _t = this;
43862             this.adder.on('click', function(e) {
43863                 _t.fireEvent('adderclick', this, e);
43864             }, _t);
43865         }
43866         //var _t = this;
43867         //this.adder.on('click', this.onAddClick, _t);
43868         
43869         
43870         this.combo.on('select', function(cb, rec, ix) {
43871             this.addItem(rec.data);
43872             
43873             cb.setValue('');
43874             cb.el.dom.value = '';
43875             //cb.lastData = rec.data;
43876             // add to list
43877             
43878         }, this);
43879         
43880         
43881     },
43882     
43883     
43884     getName: function()
43885     {
43886         // returns hidden if it's set..
43887         if (!this.rendered) {return ''};
43888         return  this.hiddenName ? this.hiddenName : this.name;
43889         
43890     },
43891     
43892     
43893     onResize: function(w, h){
43894         
43895         return;
43896         // not sure if this is needed..
43897         //this.combo.onResize(w,h);
43898         
43899         if(typeof w != 'number'){
43900             // we do not handle it!?!?
43901             return;
43902         }
43903         var tw = this.combo.trigger.getWidth();
43904         tw += this.addicon ? this.addicon.getWidth() : 0;
43905         tw += this.editicon ? this.editicon.getWidth() : 0;
43906         var x = w - tw;
43907         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43908             
43909         this.combo.trigger.setStyle('left', '0px');
43910         
43911         if(this.list && this.listWidth === undefined){
43912             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43913             this.list.setWidth(lw);
43914             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43915         }
43916         
43917     
43918         
43919     },
43920     
43921     addItem: function(rec)
43922     {
43923         var valueField = this.combo.valueField;
43924         var displayField = this.combo.displayField;
43925         
43926         if (this.items.indexOfKey(rec[valueField]) > -1) {
43927             //console.log("GOT " + rec.data.id);
43928             return;
43929         }
43930         
43931         var x = new Roo.form.ComboBoxArray.Item({
43932             //id : rec[this.idField],
43933             data : rec,
43934             displayField : displayField ,
43935             tipField : displayField ,
43936             cb : this
43937         });
43938         // use the 
43939         this.items.add(rec[valueField],x);
43940         // add it before the element..
43941         this.updateHiddenEl();
43942         x.render(this.outerWrap, this.wrap.dom);
43943         // add the image handler..
43944     },
43945     
43946     updateHiddenEl : function()
43947     {
43948         this.validate();
43949         if (!this.hiddenEl) {
43950             return;
43951         }
43952         var ar = [];
43953         var idField = this.combo.valueField;
43954         
43955         this.items.each(function(f) {
43956             ar.push(f.data[idField]);
43957         });
43958         this.hiddenEl.dom.value = ar.join(this.seperator);
43959         this.validate();
43960     },
43961     
43962     reset : function()
43963     {
43964         this.items.clear();
43965         
43966         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43967            el.remove();
43968         });
43969         
43970         this.el.dom.value = '';
43971         if (this.hiddenEl) {
43972             this.hiddenEl.dom.value = '';
43973         }
43974         
43975     },
43976     getValue: function()
43977     {
43978         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43979     },
43980     setValue: function(v) // not a valid action - must use addItems..
43981     {
43982         
43983         this.reset();
43984          
43985         if (this.store.isLocal && (typeof(v) == 'string')) {
43986             // then we can use the store to find the values..
43987             // comma seperated at present.. this needs to allow JSON based encoding..
43988             this.hiddenEl.value  = v;
43989             var v_ar = [];
43990             Roo.each(v.split(this.seperator), function(k) {
43991                 Roo.log("CHECK " + this.valueField + ',' + k);
43992                 var li = this.store.query(this.valueField, k);
43993                 if (!li.length) {
43994                     return;
43995                 }
43996                 var add = {};
43997                 add[this.valueField] = k;
43998                 add[this.displayField] = li.item(0).data[this.displayField];
43999                 
44000                 this.addItem(add);
44001             }, this) 
44002              
44003         }
44004         if (typeof(v) == 'object' ) {
44005             // then let's assume it's an array of objects..
44006             Roo.each(v, function(l) {
44007                 var add = l;
44008                 if (typeof(l) == 'string') {
44009                     add = {};
44010                     add[this.valueField] = l;
44011                     add[this.displayField] = l
44012                 }
44013                 this.addItem(add);
44014             }, this);
44015              
44016         }
44017         
44018         
44019     },
44020     setFromData: function(v)
44021     {
44022         // this recieves an object, if setValues is called.
44023         this.reset();
44024         this.el.dom.value = v[this.displayField];
44025         this.hiddenEl.dom.value = v[this.valueField];
44026         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44027             return;
44028         }
44029         var kv = v[this.valueField];
44030         var dv = v[this.displayField];
44031         kv = typeof(kv) != 'string' ? '' : kv;
44032         dv = typeof(dv) != 'string' ? '' : dv;
44033         
44034         
44035         var keys = kv.split(this.seperator);
44036         var display = dv.split(this.seperator);
44037         for (var i = 0 ; i < keys.length; i++) {
44038             add = {};
44039             add[this.valueField] = keys[i];
44040             add[this.displayField] = display[i];
44041             this.addItem(add);
44042         }
44043       
44044         
44045     },
44046     
44047     /**
44048      * Validates the combox array value
44049      * @return {Boolean} True if the value is valid, else false
44050      */
44051     validate : function(){
44052         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44053             this.clearInvalid();
44054             return true;
44055         }
44056         return false;
44057     },
44058     
44059     validateValue : function(value){
44060         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44061         
44062     },
44063     
44064     /*@
44065      * overide
44066      * 
44067      */
44068     isDirty : function() {
44069         if(this.disabled) {
44070             return false;
44071         }
44072         
44073         try {
44074             var d = Roo.decode(String(this.originalValue));
44075         } catch (e) {
44076             return String(this.getValue()) !== String(this.originalValue);
44077         }
44078         
44079         var originalValue = [];
44080         
44081         for (var i = 0; i < d.length; i++){
44082             originalValue.push(d[i][this.valueField]);
44083         }
44084         
44085         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44086         
44087     }
44088     
44089 });
44090
44091
44092
44093 /**
44094  * @class Roo.form.ComboBoxArray.Item
44095  * @extends Roo.BoxComponent
44096  * A selected item in the list
44097  *  Fred [x]  Brian [x]  [Pick another |v]
44098  * 
44099  * @constructor
44100  * Create a new item.
44101  * @param {Object} config Configuration options
44102  */
44103  
44104 Roo.form.ComboBoxArray.Item = function(config) {
44105     config.id = Roo.id();
44106     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44107 }
44108
44109 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44110     data : {},
44111     cb: false,
44112     displayField : false,
44113     tipField : false,
44114     
44115     
44116     defaultAutoCreate : {
44117         tag: 'div',
44118         cls: 'x-cbarray-item',
44119         cn : [ 
44120             { tag: 'div' },
44121             {
44122                 tag: 'img',
44123                 width:16,
44124                 height : 16,
44125                 src : Roo.BLANK_IMAGE_URL ,
44126                 align: 'center'
44127             }
44128         ]
44129         
44130     },
44131     
44132  
44133     onRender : function(ct, position)
44134     {
44135         Roo.form.Field.superclass.onRender.call(this, ct, position);
44136         
44137         if(!this.el){
44138             var cfg = this.getAutoCreate();
44139             this.el = ct.createChild(cfg, position);
44140         }
44141         
44142         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44143         
44144         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44145             this.cb.renderer(this.data) :
44146             String.format('{0}',this.data[this.displayField]);
44147         
44148             
44149         this.el.child('div').dom.setAttribute('qtip',
44150                         String.format('{0}',this.data[this.tipField])
44151         );
44152         
44153         this.el.child('img').on('click', this.remove, this);
44154         
44155     },
44156    
44157     remove : function()
44158     {
44159         if(this.cb.disabled){
44160             return;
44161         }
44162         
44163         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44164             this.cb.items.remove(this);
44165             this.el.child('img').un('click', this.remove, this);
44166             this.el.remove();
44167             this.cb.updateHiddenEl();
44168
44169             this.cb.fireEvent('remove', this.cb, this);
44170         }
44171         
44172     }
44173 });/*
44174  * RooJS Library 1.1.1
44175  * Copyright(c) 2008-2011  Alan Knowles
44176  *
44177  * License - LGPL
44178  */
44179  
44180
44181 /**
44182  * @class Roo.form.ComboNested
44183  * @extends Roo.form.ComboBox
44184  * A combobox for that allows selection of nested items in a list,
44185  * eg.
44186  *
44187  *  Book
44188  *    -> red
44189  *    -> green
44190  *  Table
44191  *    -> square
44192  *      ->red
44193  *      ->green
44194  *    -> rectangle
44195  *      ->green
44196  *      
44197  * 
44198  * @constructor
44199  * Create a new ComboNested
44200  * @param {Object} config Configuration options
44201  */
44202 Roo.form.ComboNested = function(config){
44203     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44204     // should verify some data...
44205     // like
44206     // hiddenName = required..
44207     // displayField = required
44208     // valudField == required
44209     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44210     var _t = this;
44211     Roo.each(req, function(e) {
44212         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44213             throw "Roo.form.ComboNested : missing value for: " + e;
44214         }
44215     });
44216      
44217     
44218 };
44219
44220 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44221    
44222     /*
44223      * @config {Number} max Number of columns to show
44224      */
44225     
44226     maxColumns : 3,
44227    
44228     list : null, // the outermost div..
44229     innerLists : null, // the
44230     views : null,
44231     stores : null,
44232     // private
44233     loadingChildren : false,
44234     
44235     onRender : function(ct, position)
44236     {
44237         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44238         
44239         if(this.hiddenName){
44240             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44241                     'before', true);
44242             this.hiddenField.value =
44243                 this.hiddenValue !== undefined ? this.hiddenValue :
44244                 this.value !== undefined ? this.value : '';
44245
44246             // prevent input submission
44247             this.el.dom.removeAttribute('name');
44248              
44249              
44250         }
44251         
44252         if(Roo.isGecko){
44253             this.el.dom.setAttribute('autocomplete', 'off');
44254         }
44255
44256         var cls = 'x-combo-list';
44257
44258         this.list = new Roo.Layer({
44259             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44260         });
44261
44262         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44263         this.list.setWidth(lw);
44264         this.list.swallowEvent('mousewheel');
44265         this.assetHeight = 0;
44266
44267         if(this.title){
44268             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44269             this.assetHeight += this.header.getHeight();
44270         }
44271         this.innerLists = [];
44272         this.views = [];
44273         this.stores = [];
44274         for (var i =0 ; i < this.maxColumns; i++) {
44275             this.onRenderList( cls, i);
44276         }
44277         
44278         // always needs footer, as we are going to have an 'OK' button.
44279         this.footer = this.list.createChild({cls:cls+'-ft'});
44280         this.pageTb = new Roo.Toolbar(this.footer);  
44281         var _this = this;
44282         this.pageTb.add(  {
44283             
44284             text: 'Done',
44285             handler: function()
44286             {
44287                 _this.collapse();
44288             }
44289         });
44290         
44291         if ( this.allowBlank && !this.disableClear) {
44292             
44293             this.pageTb.add(new Roo.Toolbar.Fill(), {
44294                 cls: 'x-btn-icon x-btn-clear',
44295                 text: '&#160;',
44296                 handler: function()
44297                 {
44298                     _this.collapse();
44299                     _this.clearValue();
44300                     _this.onSelect(false, -1);
44301                 }
44302             });
44303         }
44304         if (this.footer) {
44305             this.assetHeight += this.footer.getHeight();
44306         }
44307         
44308     },
44309     onRenderList : function (  cls, i)
44310     {
44311         
44312         var lw = Math.floor(
44313                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44314         );
44315         
44316         this.list.setWidth(lw); // default to '1'
44317
44318         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44319         //il.on('mouseover', this.onViewOver, this, { list:  i });
44320         //il.on('mousemove', this.onViewMove, this, { list:  i });
44321         il.setWidth(lw);
44322         il.setStyle({ 'overflow-x' : 'hidden'});
44323
44324         if(!this.tpl){
44325             this.tpl = new Roo.Template({
44326                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44327                 isEmpty: function (value, allValues) {
44328                     //Roo.log(value);
44329                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44330                     return dl ? 'has-children' : 'no-children'
44331                 }
44332             });
44333         }
44334         
44335         var store  = this.store;
44336         if (i > 0) {
44337             store  = new Roo.data.SimpleStore({
44338                 //fields : this.store.reader.meta.fields,
44339                 reader : this.store.reader,
44340                 data : [ ]
44341             });
44342         }
44343         this.stores[i]  = store;
44344                   
44345         var view = this.views[i] = new Roo.View(
44346             il,
44347             this.tpl,
44348             {
44349                 singleSelect:true,
44350                 store: store,
44351                 selectedClass: this.selectedClass
44352             }
44353         );
44354         view.getEl().setWidth(lw);
44355         view.getEl().setStyle({
44356             position: i < 1 ? 'relative' : 'absolute',
44357             top: 0,
44358             left: (i * lw ) + 'px',
44359             display : i > 0 ? 'none' : 'block'
44360         });
44361         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44362         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44363         //view.on('click', this.onViewClick, this, { list : i });
44364
44365         store.on('beforeload', this.onBeforeLoad, this);
44366         store.on('load',  this.onLoad, this, { list  : i});
44367         store.on('loadexception', this.onLoadException, this);
44368
44369         // hide the other vies..
44370         
44371         
44372         
44373     },
44374       
44375     restrictHeight : function()
44376     {
44377         var mh = 0;
44378         Roo.each(this.innerLists, function(il,i) {
44379             var el = this.views[i].getEl();
44380             el.dom.style.height = '';
44381             var inner = el.dom;
44382             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44383             // only adjust heights on other ones..
44384             mh = Math.max(h, mh);
44385             if (i < 1) {
44386                 
44387                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44388                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44389                
44390             }
44391             
44392             
44393         }, this);
44394         
44395         this.list.beginUpdate();
44396         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44397         this.list.alignTo(this.el, this.listAlign);
44398         this.list.endUpdate();
44399         
44400     },
44401      
44402     
44403     // -- store handlers..
44404     // private
44405     onBeforeLoad : function()
44406     {
44407         if(!this.hasFocus){
44408             return;
44409         }
44410         this.innerLists[0].update(this.loadingText ?
44411                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44412         this.restrictHeight();
44413         this.selectedIndex = -1;
44414     },
44415     // private
44416     onLoad : function(a,b,c,d)
44417     {
44418         if (!this.loadingChildren) {
44419             // then we are loading the top level. - hide the children
44420             for (var i = 1;i < this.views.length; i++) {
44421                 this.views[i].getEl().setStyle({ display : 'none' });
44422             }
44423             var lw = Math.floor(
44424                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44425             );
44426         
44427              this.list.setWidth(lw); // default to '1'
44428
44429             
44430         }
44431         if(!this.hasFocus){
44432             return;
44433         }
44434         
44435         if(this.store.getCount() > 0) {
44436             this.expand();
44437             this.restrictHeight();   
44438         } else {
44439             this.onEmptyResults();
44440         }
44441         
44442         if (!this.loadingChildren) {
44443             this.selectActive();
44444         }
44445         /*
44446         this.stores[1].loadData([]);
44447         this.stores[2].loadData([]);
44448         this.views
44449         */    
44450     
44451         //this.el.focus();
44452     },
44453     
44454     
44455     // private
44456     onLoadException : function()
44457     {
44458         this.collapse();
44459         Roo.log(this.store.reader.jsonData);
44460         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44461             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44462         }
44463         
44464         
44465     },
44466     // no cleaning of leading spaces on blur here.
44467     cleanLeadingSpace : function(e) { },
44468     
44469
44470     onSelectChange : function (view, sels, opts )
44471     {
44472         var ix = view.getSelectedIndexes();
44473          
44474         if (opts.list > this.maxColumns - 2) {
44475             if (view.store.getCount()<  1) {
44476                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44477
44478             } else  {
44479                 if (ix.length) {
44480                     // used to clear ?? but if we are loading unselected 
44481                     this.setFromData(view.store.getAt(ix[0]).data);
44482                 }
44483                 
44484             }
44485             
44486             return;
44487         }
44488         
44489         if (!ix.length) {
44490             // this get's fired when trigger opens..
44491            // this.setFromData({});
44492             var str = this.stores[opts.list+1];
44493             str.data.clear(); // removeall wihtout the fire events..
44494             return;
44495         }
44496         
44497         var rec = view.store.getAt(ix[0]);
44498          
44499         this.setFromData(rec.data);
44500         this.fireEvent('select', this, rec, ix[0]);
44501         
44502         var lw = Math.floor(
44503              (
44504                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44505              ) / this.maxColumns
44506         );
44507         this.loadingChildren = true;
44508         this.stores[opts.list+1].loadDataFromChildren( rec );
44509         this.loadingChildren = false;
44510         var dl = this.stores[opts.list+1]. getTotalCount();
44511         
44512         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44513         
44514         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44515         for (var i = opts.list+2; i < this.views.length;i++) {
44516             this.views[i].getEl().setStyle({ display : 'none' });
44517         }
44518         
44519         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44520         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44521         
44522         if (this.isLoading) {
44523            // this.selectActive(opts.list);
44524         }
44525          
44526     },
44527     
44528     
44529     
44530     
44531     onDoubleClick : function()
44532     {
44533         this.collapse(); //??
44534     },
44535     
44536      
44537     
44538     
44539     
44540     // private
44541     recordToStack : function(store, prop, value, stack)
44542     {
44543         var cstore = new Roo.data.SimpleStore({
44544             //fields : this.store.reader.meta.fields, // we need array reader.. for
44545             reader : this.store.reader,
44546             data : [ ]
44547         });
44548         var _this = this;
44549         var record  = false;
44550         var srec = false;
44551         if(store.getCount() < 1){
44552             return false;
44553         }
44554         store.each(function(r){
44555             if(r.data[prop] == value){
44556                 record = r;
44557             srec = r;
44558                 return false;
44559             }
44560             if (r.data.cn && r.data.cn.length) {
44561                 cstore.loadDataFromChildren( r);
44562                 var cret = _this.recordToStack(cstore, prop, value, stack);
44563                 if (cret !== false) {
44564                     record = cret;
44565                     srec = r;
44566                     return false;
44567                 }
44568             }
44569              
44570             return true;
44571         });
44572         if (record == false) {
44573             return false
44574         }
44575         stack.unshift(srec);
44576         return record;
44577     },
44578     
44579     /*
44580      * find the stack of stores that match our value.
44581      *
44582      * 
44583      */
44584     
44585     selectActive : function ()
44586     {
44587         // if store is not loaded, then we will need to wait for that to happen first.
44588         var stack = [];
44589         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44590         for (var i = 0; i < stack.length; i++ ) {
44591             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44592         }
44593         
44594     }
44595         
44596          
44597     
44598     
44599     
44600     
44601 });/*
44602  * Based on:
44603  * Ext JS Library 1.1.1
44604  * Copyright(c) 2006-2007, Ext JS, LLC.
44605  *
44606  * Originally Released Under LGPL - original licence link has changed is not relivant.
44607  *
44608  * Fork - LGPL
44609  * <script type="text/javascript">
44610  */
44611 /**
44612  * @class Roo.form.Checkbox
44613  * @extends Roo.form.Field
44614  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44615  * @constructor
44616  * Creates a new Checkbox
44617  * @param {Object} config Configuration options
44618  */
44619 Roo.form.Checkbox = function(config){
44620     Roo.form.Checkbox.superclass.constructor.call(this, config);
44621     this.addEvents({
44622         /**
44623          * @event check
44624          * Fires when the checkbox is checked or unchecked.
44625              * @param {Roo.form.Checkbox} this This checkbox
44626              * @param {Boolean} checked The new checked value
44627              */
44628         check : true
44629     });
44630 };
44631
44632 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44633     /**
44634      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44635      */
44636     focusClass : undefined,
44637     /**
44638      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44639      */
44640     fieldClass: "x-form-field",
44641     /**
44642      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44643      */
44644     checked: false,
44645     /**
44646      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44647      * {tag: "input", type: "checkbox", autocomplete: "off"})
44648      */
44649     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44650     /**
44651      * @cfg {String} boxLabel The text that appears beside the checkbox
44652      */
44653     boxLabel : "",
44654     /**
44655      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44656      */  
44657     inputValue : '1',
44658     /**
44659      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44660      */
44661      valueOff: '0', // value when not checked..
44662
44663     actionMode : 'viewEl', 
44664     //
44665     // private
44666     itemCls : 'x-menu-check-item x-form-item',
44667     groupClass : 'x-menu-group-item',
44668     inputType : 'hidden',
44669     
44670     
44671     inSetChecked: false, // check that we are not calling self...
44672     
44673     inputElement: false, // real input element?
44674     basedOn: false, // ????
44675     
44676     isFormField: true, // not sure where this is needed!!!!
44677
44678     onResize : function(){
44679         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44680         if(!this.boxLabel){
44681             this.el.alignTo(this.wrap, 'c-c');
44682         }
44683     },
44684
44685     initEvents : function(){
44686         Roo.form.Checkbox.superclass.initEvents.call(this);
44687         this.el.on("click", this.onClick,  this);
44688         this.el.on("change", this.onClick,  this);
44689     },
44690
44691
44692     getResizeEl : function(){
44693         return this.wrap;
44694     },
44695
44696     getPositionEl : function(){
44697         return this.wrap;
44698     },
44699
44700     // private
44701     onRender : function(ct, position){
44702         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44703         /*
44704         if(this.inputValue !== undefined){
44705             this.el.dom.value = this.inputValue;
44706         }
44707         */
44708         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44709         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44710         var viewEl = this.wrap.createChild({ 
44711             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44712         this.viewEl = viewEl;   
44713         this.wrap.on('click', this.onClick,  this); 
44714         
44715         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44716         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44717         
44718         
44719         
44720         if(this.boxLabel){
44721             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44722         //    viewEl.on('click', this.onClick,  this); 
44723         }
44724         //if(this.checked){
44725             this.setChecked(this.checked);
44726         //}else{
44727             //this.checked = this.el.dom;
44728         //}
44729
44730     },
44731
44732     // private
44733     initValue : Roo.emptyFn,
44734
44735     /**
44736      * Returns the checked state of the checkbox.
44737      * @return {Boolean} True if checked, else false
44738      */
44739     getValue : function(){
44740         if(this.el){
44741             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44742         }
44743         return this.valueOff;
44744         
44745     },
44746
44747         // private
44748     onClick : function(){ 
44749         if (this.disabled) {
44750             return;
44751         }
44752         this.setChecked(!this.checked);
44753
44754         //if(this.el.dom.checked != this.checked){
44755         //    this.setValue(this.el.dom.checked);
44756        // }
44757     },
44758
44759     /**
44760      * Sets the checked state of the checkbox.
44761      * On is always based on a string comparison between inputValue and the param.
44762      * @param {Boolean/String} value - the value to set 
44763      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44764      */
44765     setValue : function(v,suppressEvent){
44766         
44767         
44768         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44769         //if(this.el && this.el.dom){
44770         //    this.el.dom.checked = this.checked;
44771         //    this.el.dom.defaultChecked = this.checked;
44772         //}
44773         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44774         //this.fireEvent("check", this, this.checked);
44775     },
44776     // private..
44777     setChecked : function(state,suppressEvent)
44778     {
44779         if (this.inSetChecked) {
44780             this.checked = state;
44781             return;
44782         }
44783         
44784     
44785         if(this.wrap){
44786             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44787         }
44788         this.checked = state;
44789         if(suppressEvent !== true){
44790             this.fireEvent('check', this, state);
44791         }
44792         this.inSetChecked = true;
44793         this.el.dom.value = state ? this.inputValue : this.valueOff;
44794         this.inSetChecked = false;
44795         
44796     },
44797     // handle setting of hidden value by some other method!!?!?
44798     setFromHidden: function()
44799     {
44800         if(!this.el){
44801             return;
44802         }
44803         //console.log("SET FROM HIDDEN");
44804         //alert('setFrom hidden');
44805         this.setValue(this.el.dom.value);
44806     },
44807     
44808     onDestroy : function()
44809     {
44810         if(this.viewEl){
44811             Roo.get(this.viewEl).remove();
44812         }
44813          
44814         Roo.form.Checkbox.superclass.onDestroy.call(this);
44815     },
44816     
44817     setBoxLabel : function(str)
44818     {
44819         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44820     }
44821
44822 });/*
44823  * Based on:
44824  * Ext JS Library 1.1.1
44825  * Copyright(c) 2006-2007, Ext JS, LLC.
44826  *
44827  * Originally Released Under LGPL - original licence link has changed is not relivant.
44828  *
44829  * Fork - LGPL
44830  * <script type="text/javascript">
44831  */
44832  
44833 /**
44834  * @class Roo.form.Radio
44835  * @extends Roo.form.Checkbox
44836  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44837  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44838  * @constructor
44839  * Creates a new Radio
44840  * @param {Object} config Configuration options
44841  */
44842 Roo.form.Radio = function(){
44843     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44844 };
44845 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44846     inputType: 'radio',
44847
44848     /**
44849      * If this radio is part of a group, it will return the selected value
44850      * @return {String}
44851      */
44852     getGroupValue : function(){
44853         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44854     },
44855     
44856     
44857     onRender : function(ct, position){
44858         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44859         
44860         if(this.inputValue !== undefined){
44861             this.el.dom.value = this.inputValue;
44862         }
44863          
44864         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44865         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44866         //var viewEl = this.wrap.createChild({ 
44867         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44868         //this.viewEl = viewEl;   
44869         //this.wrap.on('click', this.onClick,  this); 
44870         
44871         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44872         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44873         
44874         
44875         
44876         if(this.boxLabel){
44877             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44878         //    viewEl.on('click', this.onClick,  this); 
44879         }
44880          if(this.checked){
44881             this.el.dom.checked =   'checked' ;
44882         }
44883          
44884     } 
44885     
44886     
44887 });Roo.rtf = {}; // namespace
44888 Roo.rtf.Hex = function(hex)
44889 {
44890     this.hexstr = hex;
44891 };
44892 Roo.rtf.Paragraph = function(opts)
44893 {
44894     this.content = []; ///??? is that used?
44895 };Roo.rtf.Span = function(opts)
44896 {
44897     this.value = opts.value;
44898 };
44899
44900 Roo.rtf.Group = function(parent)
44901 {
44902     // we dont want to acutally store parent - it will make debug a nightmare..
44903     this.content = [];
44904     this.cn  = [];
44905      
44906        
44907     
44908 };
44909
44910 Roo.rtf.Group.prototype = {
44911     ignorable : false,
44912     content: false,
44913     cn: false,
44914     addContent : function(node) {
44915         // could set styles...
44916         this.content.push(node);
44917     },
44918     addChild : function(cn)
44919     {
44920         this.cn.push(cn);
44921     },
44922     // only for images really...
44923     toDataURL : function()
44924     {
44925         var mimetype = false;
44926         switch(true) {
44927             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44928                 mimetype = "image/png";
44929                 break;
44930              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44931                 mimetype = "image/jpeg";
44932                 break;
44933             default :
44934                 return 'about:blank'; // ?? error?
44935         }
44936         
44937         
44938         var hexstring = this.content[this.content.length-1].value;
44939         
44940         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44941             return String.fromCharCode(parseInt(a, 16));
44942         }).join(""));
44943     }
44944     
44945 };
44946 // this looks like it's normally the {rtf{ .... }}
44947 Roo.rtf.Document = function()
44948 {
44949     // we dont want to acutally store parent - it will make debug a nightmare..
44950     this.rtlch  = [];
44951     this.content = [];
44952     this.cn = [];
44953     
44954 };
44955 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44956     addChild : function(cn)
44957     {
44958         this.cn.push(cn);
44959         switch(cn.type) {
44960             case 'rtlch': // most content seems to be inside this??
44961             case 'listtext':
44962             case 'shpinst':
44963                 this.rtlch.push(cn);
44964                 return;
44965             default:
44966                 this[cn.type] = cn;
44967         }
44968         
44969     },
44970     
44971     getElementsByType : function(type)
44972     {
44973         var ret =  [];
44974         this._getElementsByType(type, ret, this.cn, 'rtf');
44975         return ret;
44976     },
44977     _getElementsByType : function (type, ret, search_array, path)
44978     {
44979         search_array.forEach(function(n,i) {
44980             if (n.type == type) {
44981                 n.path = path + '/' + n.type + ':' + i;
44982                 ret.push(n);
44983             }
44984             if (n.cn.length > 0) {
44985                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44986             }
44987         },this);
44988     }
44989     
44990 });
44991  
44992 Roo.rtf.Ctrl = function(opts)
44993 {
44994     this.value = opts.value;
44995     this.param = opts.param;
44996 };
44997 /**
44998  *
44999  *
45000  * based on this https://github.com/iarna/rtf-parser
45001  * it's really only designed to extract pict from pasted RTF 
45002  *
45003  * usage:
45004  *
45005  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45006  *  
45007  *
45008  */
45009
45010  
45011
45012
45013
45014 Roo.rtf.Parser = function(text) {
45015     //super({objectMode: true})
45016     this.text = '';
45017     this.parserState = this.parseText;
45018     
45019     // these are for interpeter...
45020     this.doc = {};
45021     ///this.parserState = this.parseTop
45022     this.groupStack = [];
45023     this.hexStore = [];
45024     this.doc = false;
45025     
45026     this.groups = []; // where we put the return.
45027     
45028     for (var ii = 0; ii < text.length; ++ii) {
45029         ++this.cpos;
45030         
45031         if (text[ii] === '\n') {
45032             ++this.row;
45033             this.col = 1;
45034         } else {
45035             ++this.col;
45036         }
45037         this.parserState(text[ii]);
45038     }
45039     
45040     
45041     
45042 };
45043 Roo.rtf.Parser.prototype = {
45044     text : '', // string being parsed..
45045     controlWord : '',
45046     controlWordParam :  '',
45047     hexChar : '',
45048     doc : false,
45049     group: false,
45050     groupStack : false,
45051     hexStore : false,
45052     
45053     
45054     cpos : 0, 
45055     row : 1, // reportin?
45056     col : 1, //
45057
45058      
45059     push : function (el)
45060     {
45061         var m = 'cmd'+ el.type;
45062         if (typeof(this[m]) == 'undefined') {
45063             Roo.log('invalid cmd:' + el.type);
45064             return;
45065         }
45066         this[m](el);
45067         //Roo.log(el);
45068     },
45069     flushHexStore : function()
45070     {
45071         if (this.hexStore.length < 1) {
45072             return;
45073         }
45074         var hexstr = this.hexStore.map(
45075             function(cmd) {
45076                 return cmd.value;
45077         }).join('');
45078         
45079         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45080               
45081             
45082         this.hexStore.splice(0)
45083         
45084     },
45085     
45086     cmdgroupstart : function()
45087     {
45088         this.flushHexStore();
45089         if (this.group) {
45090             this.groupStack.push(this.group);
45091         }
45092          // parent..
45093         if (this.doc === false) {
45094             this.group = this.doc = new Roo.rtf.Document();
45095             return;
45096             
45097         }
45098         this.group = new Roo.rtf.Group(this.group);
45099     },
45100     cmdignorable : function()
45101     {
45102         this.flushHexStore();
45103         this.group.ignorable = true;
45104     },
45105     cmdendparagraph : function()
45106     {
45107         this.flushHexStore();
45108         this.group.addContent(new Roo.rtf.Paragraph());
45109     },
45110     cmdgroupend : function ()
45111     {
45112         this.flushHexStore();
45113         var endingGroup = this.group;
45114         
45115         
45116         this.group = this.groupStack.pop();
45117         if (this.group) {
45118             this.group.addChild(endingGroup);
45119         }
45120         
45121         
45122         
45123         var doc = this.group || this.doc;
45124         //if (endingGroup instanceof FontTable) {
45125         //  doc.fonts = endingGroup.table
45126         //} else if (endingGroup instanceof ColorTable) {
45127         //  doc.colors = endingGroup.table
45128         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45129         if (endingGroup.ignorable === false) {
45130             //code
45131             this.groups.push(endingGroup);
45132            // Roo.log( endingGroup );
45133         }
45134             //Roo.each(endingGroup.content, function(item)) {
45135             //    doc.addContent(item);
45136             //}
45137             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45138         //}
45139     },
45140     cmdtext : function (cmd)
45141     {
45142         this.flushHexStore();
45143         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45144             //this.group = this.doc
45145         }
45146         this.group.addContent(new Roo.rtf.Span(cmd));
45147     },
45148     cmdcontrolword : function (cmd)
45149     {
45150         this.flushHexStore();
45151         if (!this.group.type) {
45152             this.group.type = cmd.value;
45153             return;
45154         }
45155         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45156         // we actually don't care about ctrl words...
45157         return ;
45158         /*
45159         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45160         if (this[method]) {
45161             this[method](cmd.param)
45162         } else {
45163             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45164         }
45165         */
45166     },
45167     cmdhexchar : function(cmd) {
45168         this.hexStore.push(cmd);
45169     },
45170     cmderror : function(cmd) {
45171         throw new Exception (cmd.value);
45172     },
45173     
45174     /*
45175       _flush (done) {
45176         if (this.text !== '\u0000') this.emitText()
45177         done()
45178       }
45179       */
45180       
45181       
45182     parseText : function(c)
45183     {
45184         if (c === '\\') {
45185             this.parserState = this.parseEscapes;
45186         } else if (c === '{') {
45187             this.emitStartGroup();
45188         } else if (c === '}') {
45189             this.emitEndGroup();
45190         } else if (c === '\x0A' || c === '\x0D') {
45191             // cr/lf are noise chars
45192         } else {
45193             this.text += c;
45194         }
45195     },
45196     
45197     parseEscapes: function (c)
45198     {
45199         if (c === '\\' || c === '{' || c === '}') {
45200             this.text += c;
45201             this.parserState = this.parseText;
45202         } else {
45203             this.parserState = this.parseControlSymbol;
45204             this.parseControlSymbol(c);
45205         }
45206     },
45207     parseControlSymbol: function(c)
45208     {
45209         if (c === '~') {
45210             this.text += '\u00a0'; // nbsp
45211             this.parserState = this.parseText
45212         } else if (c === '-') {
45213              this.text += '\u00ad'; // soft hyphen
45214         } else if (c === '_') {
45215             this.text += '\u2011'; // non-breaking hyphen
45216         } else if (c === '*') {
45217             this.emitIgnorable();
45218             this.parserState = this.parseText;
45219         } else if (c === "'") {
45220             this.parserState = this.parseHexChar;
45221         } else if (c === '|') { // formula cacter
45222             this.emitFormula();
45223             this.parserState = this.parseText;
45224         } else if (c === ':') { // subentry in an index entry
45225             this.emitIndexSubEntry();
45226             this.parserState = this.parseText;
45227         } else if (c === '\x0a') {
45228             this.emitEndParagraph();
45229             this.parserState = this.parseText;
45230         } else if (c === '\x0d') {
45231             this.emitEndParagraph();
45232             this.parserState = this.parseText;
45233         } else {
45234             this.parserState = this.parseControlWord;
45235             this.parseControlWord(c);
45236         }
45237     },
45238     parseHexChar: function (c)
45239     {
45240         if (/^[A-Fa-f0-9]$/.test(c)) {
45241             this.hexChar += c;
45242             if (this.hexChar.length >= 2) {
45243               this.emitHexChar();
45244               this.parserState = this.parseText;
45245             }
45246             return;
45247         }
45248         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45249         this.parserState = this.parseText;
45250         
45251     },
45252     parseControlWord : function(c)
45253     {
45254         if (c === ' ') {
45255             this.emitControlWord();
45256             this.parserState = this.parseText;
45257         } else if (/^[-\d]$/.test(c)) {
45258             this.parserState = this.parseControlWordParam;
45259             this.controlWordParam += c;
45260         } else if (/^[A-Za-z]$/.test(c)) {
45261           this.controlWord += c;
45262         } else {
45263           this.emitControlWord();
45264           this.parserState = this.parseText;
45265           this.parseText(c);
45266         }
45267     },
45268     parseControlWordParam : function (c) {
45269         if (/^\d$/.test(c)) {
45270           this.controlWordParam += c;
45271         } else if (c === ' ') {
45272           this.emitControlWord();
45273           this.parserState = this.parseText;
45274         } else {
45275           this.emitControlWord();
45276           this.parserState = this.parseText;
45277           this.parseText(c);
45278         }
45279     },
45280     
45281     
45282     
45283     
45284     emitText : function () {
45285         if (this.text === '') {
45286             return;
45287         }
45288         this.push({
45289             type: 'text',
45290             value: this.text,
45291             pos: this.cpos,
45292             row: this.row,
45293             col: this.col
45294         });
45295         this.text = ''
45296     },
45297     emitControlWord : function ()
45298     {
45299         this.emitText();
45300         if (this.controlWord === '') {
45301             this.emitError('empty control word');
45302         } else {
45303             this.push({
45304                   type: 'controlword',
45305                   value: this.controlWord,
45306                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45307                   pos: this.cpos,
45308                   row: this.row,
45309                   col: this.col
45310             });
45311         }
45312         this.controlWord = '';
45313         this.controlWordParam = '';
45314     },
45315     emitStartGroup : function ()
45316     {
45317         this.emitText();
45318         this.push({
45319             type: 'groupstart',
45320             pos: this.cpos,
45321             row: this.row,
45322             col: this.col
45323         });
45324     },
45325     emitEndGroup : function ()
45326     {
45327         this.emitText();
45328         this.push({
45329             type: 'groupend',
45330             pos: this.cpos,
45331             row: this.row,
45332             col: this.col
45333         });
45334     },
45335     emitIgnorable : function ()
45336     {
45337         this.emitText();
45338         this.push({
45339             type: 'ignorable',
45340             pos: this.cpos,
45341             row: this.row,
45342             col: this.col
45343         });
45344     },
45345     emitHexChar : function ()
45346     {
45347         this.emitText();
45348         this.push({
45349             type: 'hexchar',
45350             value: this.hexChar,
45351             pos: this.cpos,
45352             row: this.row,
45353             col: this.col
45354         });
45355         this.hexChar = ''
45356     },
45357     emitError : function (message)
45358     {
45359       this.emitText();
45360       this.push({
45361             type: 'error',
45362             value: message,
45363             row: this.row,
45364             col: this.col,
45365             char: this.cpos //,
45366             //stack: new Error().stack
45367         });
45368     },
45369     emitEndParagraph : function () {
45370         this.emitText();
45371         this.push({
45372             type: 'endparagraph',
45373             pos: this.cpos,
45374             row: this.row,
45375             col: this.col
45376         });
45377     }
45378      
45379 } ;
45380 Roo.htmleditor = {};
45381  
45382 /**
45383  * @class Roo.htmleditor.Filter
45384  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45385  * @cfg {DomElement} node The node to iterate and filter
45386  * @cfg {boolean|String|Array} tag Tags to replace 
45387  * @constructor
45388  * Create a new Filter.
45389  * @param {Object} config Configuration options
45390  */
45391
45392
45393
45394 Roo.htmleditor.Filter = function(cfg) {
45395     Roo.apply(this.cfg);
45396     // this does not actually call walk as it's really just a abstract class
45397 }
45398
45399
45400 Roo.htmleditor.Filter.prototype = {
45401     
45402     node: false,
45403     
45404     tag: false,
45405
45406     // overrride to do replace comments.
45407     replaceComment : false,
45408     
45409     // overrride to do replace or do stuff with tags..
45410     replaceTag : false,
45411     
45412     walk : function(dom)
45413     {
45414         Roo.each( Array.from(dom.childNodes), function( e ) {
45415             switch(true) {
45416                 
45417                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45418                     this.replaceComment(e);
45419                     return;
45420                 
45421                 case e.nodeType != 1: //not a node.
45422                     return;
45423                 
45424                 case this.tag === true: // everything
45425                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45426                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45427                     if (this.replaceTag && false === this.replaceTag(e)) {
45428                         return;
45429                     }
45430                     if (e.hasChildNodes()) {
45431                         this.walk(e);
45432                     }
45433                     return;
45434                 
45435                 default:    // tags .. that do not match.
45436                     if (e.hasChildNodes()) {
45437                         this.walk(e);
45438                     }
45439             }
45440             
45441         }, this);
45442         
45443     }
45444 }; 
45445
45446 /**
45447  * @class Roo.htmleditor.FilterAttributes
45448  * clean attributes and  styles including http:// etc.. in attribute
45449  * @constructor
45450 * Run a new Attribute Filter
45451 * @param {Object} config Configuration options
45452  */
45453 Roo.htmleditor.FilterAttributes = function(cfg)
45454 {
45455     Roo.apply(this, cfg);
45456     this.attrib_black = this.attrib_black || [];
45457     this.attrib_white = this.attrib_white || [];
45458
45459     this.attrib_clean = this.attrib_clean || [];
45460     this.style_white = this.style_white || [];
45461     this.style_black = this.style_black || [];
45462     this.walk(cfg.node);
45463 }
45464
45465 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45466 {
45467     tag: true, // all tags
45468     
45469     attrib_black : false, // array
45470     attrib_clean : false,
45471     attrib_white : false,
45472
45473     style_white : false,
45474     style_black : false,
45475      
45476      
45477     replaceTag : function(node)
45478     {
45479         if (!node.attributes || !node.attributes.length) {
45480             return true;
45481         }
45482         
45483         for (var i = node.attributes.length-1; i > -1 ; i--) {
45484             var a = node.attributes[i];
45485             //console.log(a);
45486             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45487                 node.removeAttribute(a.name);
45488                 continue;
45489             }
45490             
45491             
45492             
45493             if (a.name.toLowerCase().substr(0,2)=='on')  {
45494                 node.removeAttribute(a.name);
45495                 continue;
45496             }
45497             
45498             
45499             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45500                 node.removeAttribute(a.name);
45501                 continue;
45502             }
45503             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45504                 this.cleanAttr(node,a.name,a.value); // fixme..
45505                 continue;
45506             }
45507             if (a.name == 'style') {
45508                 this.cleanStyle(node,a.name,a.value);
45509                 continue;
45510             }
45511             /// clean up MS crap..
45512             // tecnically this should be a list of valid class'es..
45513             
45514             
45515             if (a.name == 'class') {
45516                 if (a.value.match(/^Mso/)) {
45517                     node.removeAttribute('class');
45518                 }
45519                 
45520                 if (a.value.match(/^body$/)) {
45521                     node.removeAttribute('class');
45522                 }
45523                 continue;
45524             }
45525             
45526             
45527             // style cleanup!?
45528             // class cleanup?
45529             
45530         }
45531         return true; // clean children
45532     },
45533         
45534     cleanAttr: function(node, n,v)
45535     {
45536         
45537         if (v.match(/^\./) || v.match(/^\//)) {
45538             return;
45539         }
45540         if (v.match(/^(http|https):\/\//)
45541             || v.match(/^mailto:/) 
45542             || v.match(/^ftp:/)
45543             || v.match(/^data:/)
45544             ) {
45545             return;
45546         }
45547         if (v.match(/^#/)) {
45548             return;
45549         }
45550         if (v.match(/^\{/)) { // allow template editing.
45551             return;
45552         }
45553 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45554         node.removeAttribute(n);
45555         
45556     },
45557     cleanStyle : function(node,  n,v)
45558     {
45559         if (v.match(/expression/)) { //XSS?? should we even bother..
45560             node.removeAttribute(n);
45561             return;
45562         }
45563         
45564         var parts = v.split(/;/);
45565         var clean = [];
45566         
45567         Roo.each(parts, function(p) {
45568             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45569             if (!p.length) {
45570                 return true;
45571             }
45572             var l = p.split(':').shift().replace(/\s+/g,'');
45573             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45574             
45575             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45576                 return true;
45577             }
45578             //Roo.log()
45579             // only allow 'c whitelisted system attributes'
45580             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45581                 return true;
45582             }
45583             
45584             
45585             clean.push(p);
45586             return true;
45587         },this);
45588         if (clean.length) { 
45589             node.setAttribute(n, clean.join(';'));
45590         } else {
45591             node.removeAttribute(n);
45592         }
45593         
45594     }
45595         
45596         
45597         
45598     
45599 });/**
45600  * @class Roo.htmleditor.FilterBlack
45601  * remove blacklisted elements.
45602  * @constructor
45603  * Run a new Blacklisted Filter
45604  * @param {Object} config Configuration options
45605  */
45606
45607 Roo.htmleditor.FilterBlack = function(cfg)
45608 {
45609     Roo.apply(this, cfg);
45610     this.walk(cfg.node);
45611 }
45612
45613 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45614 {
45615     tag : true, // all elements.
45616    
45617     replace : function(n)
45618     {
45619         n.parentNode.removeChild(n);
45620     }
45621 });
45622 /**
45623  * @class Roo.htmleditor.FilterComment
45624  * remove comments.
45625  * @constructor
45626 * Run a new Comments Filter
45627 * @param {Object} config Configuration options
45628  */
45629 Roo.htmleditor.FilterComment = function(cfg)
45630 {
45631     this.walk(cfg.node);
45632 }
45633
45634 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45635 {
45636   
45637     replaceComment : function(n)
45638     {
45639         n.parentNode.removeChild(n);
45640     }
45641 });/**
45642  * @class Roo.htmleditor.FilterKeepChildren
45643  * remove tags but keep children
45644  * @constructor
45645  * Run a new Keep Children Filter
45646  * @param {Object} config Configuration options
45647  */
45648
45649 Roo.htmleditor.FilterKeepChildren = function(cfg)
45650 {
45651     Roo.apply(this, cfg);
45652     if (this.tag === false) {
45653         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45654     }
45655     this.walk(cfg.node);
45656 }
45657
45658 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45659 {
45660     
45661   
45662     replaceTag : function(node)
45663     {
45664         // walk children...
45665         //Roo.log(node);
45666         var ar = Array.from(node.childNodes);
45667         //remove first..
45668         for (var i = 0; i < ar.length; i++) {
45669             if (ar[i].nodeType == 1) {
45670                 if (
45671                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45672                     || // array and it matches
45673                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45674                 ) {
45675                     this.replaceTag(ar[i]); // child is blacklisted as well...
45676                     continue;
45677                 }
45678             }
45679         }  
45680         ar = Array.from(node.childNodes);
45681         for (var i = 0; i < ar.length; i++) {
45682          
45683             node.removeChild(ar[i]);
45684             // what if we need to walk these???
45685             node.parentNode.insertBefore(ar[i], node);
45686             if (this.tag !== false) {
45687                 this.walk(ar[i]);
45688                 
45689             }
45690         }
45691         node.parentNode.removeChild(node);
45692         return false; // don't walk children
45693         
45694         
45695     }
45696 });/**
45697  * @class Roo.htmleditor.FilterParagraph
45698  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45699  * like on 'push' to remove the <p> tags and replace them with line breaks.
45700  * @constructor
45701  * Run a new Paragraph Filter
45702  * @param {Object} config Configuration options
45703  */
45704
45705 Roo.htmleditor.FilterParagraph = function(cfg)
45706 {
45707     // no need to apply config.
45708     this.walk(cfg.node);
45709 }
45710
45711 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45712 {
45713     
45714      
45715     tag : 'P',
45716     
45717      
45718     replaceTag : function(node)
45719     {
45720         
45721         if (node.childNodes.length == 1 &&
45722             node.childNodes[0].nodeType == 3 &&
45723             node.childNodes[0].textContent.trim().length < 1
45724             ) {
45725             // remove and replace with '<BR>';
45726             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45727             return false; // no need to walk..
45728         }
45729         var ar = Array.from(node.childNodes);
45730         for (var i = 0; i < ar.length; i++) {
45731             node.removeChild(ar[i]);
45732             // what if we need to walk these???
45733             node.parentNode.insertBefore(ar[i], node);
45734         }
45735         // now what about this?
45736         // <p> &nbsp; </p>
45737         
45738         // double BR.
45739         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45740         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45741         node.parentNode.removeChild(node);
45742         
45743         return false;
45744
45745     }
45746     
45747 });/**
45748  * @class Roo.htmleditor.FilterSpan
45749  * filter span's with no attributes out..
45750  * @constructor
45751  * Run a new Span Filter
45752  * @param {Object} config Configuration options
45753  */
45754
45755 Roo.htmleditor.FilterSpan = function(cfg)
45756 {
45757     // no need to apply config.
45758     this.walk(cfg.node);
45759 }
45760
45761 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45762 {
45763      
45764     tag : 'SPAN',
45765      
45766  
45767     replaceTag : function(node)
45768     {
45769         if (node.attributes && node.attributes.length > 0) {
45770             return true; // walk if there are any.
45771         }
45772         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45773         return false;
45774      
45775     }
45776     
45777 });/**
45778  * @class Roo.htmleditor.FilterTableWidth
45779   try and remove table width data - as that frequently messes up other stuff.
45780  * 
45781  *      was cleanTableWidths.
45782  *
45783  * Quite often pasting from word etc.. results in tables with column and widths.
45784  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45785  *
45786  * @constructor
45787  * Run a new Table Filter
45788  * @param {Object} config Configuration options
45789  */
45790
45791 Roo.htmleditor.FilterTableWidth = function(cfg)
45792 {
45793     // no need to apply config.
45794     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45795     this.walk(cfg.node);
45796 }
45797
45798 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45799 {
45800      
45801      
45802     
45803     replaceTag: function(node) {
45804         
45805         
45806       
45807         if (node.hasAttribute('width')) {
45808             node.removeAttribute('width');
45809         }
45810         
45811          
45812         if (node.hasAttribute("style")) {
45813             // pretty basic...
45814             
45815             var styles = node.getAttribute("style").split(";");
45816             var nstyle = [];
45817             Roo.each(styles, function(s) {
45818                 if (!s.match(/:/)) {
45819                     return;
45820                 }
45821                 var kv = s.split(":");
45822                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45823                     return;
45824                 }
45825                 // what ever is left... we allow.
45826                 nstyle.push(s);
45827             });
45828             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45829             if (!nstyle.length) {
45830                 node.removeAttribute('style');
45831             }
45832         }
45833         
45834         return true; // continue doing children..
45835     }
45836 });/**
45837  * @class Roo.htmleditor.FilterWord
45838  * try and clean up all the mess that Word generates.
45839  * 
45840  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45841  
45842  * @constructor
45843  * Run a new Span Filter
45844  * @param {Object} config Configuration options
45845  */
45846
45847 Roo.htmleditor.FilterWord = function(cfg)
45848 {
45849     // no need to apply config.
45850     this.walk(cfg.node);
45851 }
45852
45853 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45854 {
45855     tag: true,
45856      
45857     
45858     /**
45859      * Clean up MS wordisms...
45860      */
45861     replaceTag : function(node)
45862     {
45863          
45864         // no idea what this does - span with text, replaceds with just text.
45865         if(
45866                 node.nodeName == 'SPAN' &&
45867                 !node.hasAttributes() &&
45868                 node.childNodes.length == 1 &&
45869                 node.firstChild.nodeName == "#text"  
45870         ) {
45871             var textNode = node.firstChild;
45872             node.removeChild(textNode);
45873             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45874                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45875             }
45876             node.parentNode.insertBefore(textNode, node);
45877             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45878                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45879             }
45880             
45881             node.parentNode.removeChild(node);
45882             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45883         }
45884         
45885    
45886         
45887         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45888             node.parentNode.removeChild(node);
45889             return false; // dont do chidlren
45890         }
45891         //Roo.log(node.tagName);
45892         // remove - but keep children..
45893         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45894             //Roo.log('-- removed');
45895             while (node.childNodes.length) {
45896                 var cn = node.childNodes[0];
45897                 node.removeChild(cn);
45898                 node.parentNode.insertBefore(cn, node);
45899                 // move node to parent - and clean it..
45900                 this.replaceTag(cn);
45901             }
45902             node.parentNode.removeChild(node);
45903             /// no need to iterate chidlren = it's got none..
45904             //this.iterateChildren(node, this.cleanWord);
45905             return false; // no need to iterate children.
45906         }
45907         // clean styles
45908         if (node.className.length) {
45909             
45910             var cn = node.className.split(/\W+/);
45911             var cna = [];
45912             Roo.each(cn, function(cls) {
45913                 if (cls.match(/Mso[a-zA-Z]+/)) {
45914                     return;
45915                 }
45916                 cna.push(cls);
45917             });
45918             node.className = cna.length ? cna.join(' ') : '';
45919             if (!cna.length) {
45920                 node.removeAttribute("class");
45921             }
45922         }
45923         
45924         if (node.hasAttribute("lang")) {
45925             node.removeAttribute("lang");
45926         }
45927         
45928         if (node.hasAttribute("style")) {
45929             
45930             var styles = node.getAttribute("style").split(";");
45931             var nstyle = [];
45932             Roo.each(styles, function(s) {
45933                 if (!s.match(/:/)) {
45934                     return;
45935                 }
45936                 var kv = s.split(":");
45937                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45938                     return;
45939                 }
45940                 // what ever is left... we allow.
45941                 nstyle.push(s);
45942             });
45943             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45944             if (!nstyle.length) {
45945                 node.removeAttribute('style');
45946             }
45947         }
45948         return true; // do children
45949         
45950         
45951         
45952     }
45953 });
45954 /**
45955  * @class Roo.htmleditor.FilterStyleToTag
45956  * part of the word stuff... - certain 'styles' should be converted to tags.
45957  * eg.
45958  *   font-weight: bold -> bold
45959  *   ?? super / subscrit etc..
45960  * 
45961  * @constructor
45962 * Run a new style to tag filter.
45963 * @param {Object} config Configuration options
45964  */
45965 Roo.htmleditor.FilterStyleToTag = function(cfg)
45966 {
45967     
45968     this.tags = {
45969         B  : [ 'fontWeight' , 'bold'],
45970         I :  [ 'fontStyle' , 'italic'],
45971         //pre :  [ 'font-style' , 'italic'],
45972         // h1.. h6 ?? font-size?
45973         SUP : [ 'verticalAlign' , 'super' ],
45974         SUB : [ 'verticalAlign' , 'sub' ]
45975         
45976         
45977     };
45978     
45979     Roo.apply(this, cfg);
45980      
45981     
45982     this.walk(cfg.node);
45983     
45984     
45985     
45986 }
45987
45988
45989 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45990 {
45991     tag: true, // all tags
45992     
45993     tags : false,
45994     
45995     
45996     replaceTag : function(node)
45997     {
45998         
45999         
46000         if (node.getAttribute("style") === null) {
46001             return true;
46002         }
46003         var inject = [];
46004         for (var k in this.tags) {
46005             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46006                 inject.push(k);
46007                 node.style.removeProperty(this.tags[k][0]);
46008             }
46009         }
46010         if (!inject.length) {
46011             return true; 
46012         }
46013         var cn = Array.from(node.childNodes);
46014         var nn = node;
46015         Roo.each(inject, function(t) {
46016             var nc = node.ownerDocument.createelement(t);
46017             nn.appendChild(nc);
46018             nn = nc;
46019         });
46020         for(var i = 0;i < cn.length;cn++) {
46021             node.removeChild(cn[i]);
46022             nn.appendChild(cn[i]);
46023         }
46024         return true /// iterate thru
46025     }
46026     
46027 })/**
46028  * @class Roo.htmleditor.FilterLongBr
46029  * BR/BR/BR - keep a maximum of 2...
46030  * @constructor
46031  * Run a new Long BR Filter
46032  * @param {Object} config Configuration options
46033  */
46034
46035 Roo.htmleditor.FilterLongBr = function(cfg)
46036 {
46037     // no need to apply config.
46038     this.walk(cfg.node);
46039 }
46040
46041 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46042 {
46043     
46044      
46045     tag : 'BR',
46046     
46047      
46048     replaceTag : function(node)
46049     {
46050         
46051         var ps = node.nextSibling;
46052         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46053             ps = ps.nextSibling;
46054         }
46055         
46056         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46057             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46058             return false;
46059         }
46060         
46061         if (!ps || ps.nodeType != 1) {
46062             return false;
46063         }
46064         
46065         if (!ps || ps.tagName != 'BR') {
46066            
46067             return false;
46068         }
46069         
46070         
46071         
46072         
46073         
46074         if (!node.previousSibling) {
46075             return false;
46076         }
46077         var ps = node.previousSibling;
46078         
46079         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46080             ps = ps.previousSibling;
46081         }
46082         if (!ps || ps.nodeType != 1) {
46083             return false;
46084         }
46085         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46086         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46087             return false;
46088         }
46089         
46090         node.parentNode.removeChild(node); // remove me...
46091         
46092         return false; // no need to do children
46093
46094     }
46095     
46096 });
46097 /**
46098  * @class Roo.htmleditor.Tidy
46099  * Tidy HTML 
46100  * @cfg {Roo.HtmlEditorCore} core the editor.
46101  * @constructor
46102  * Create a new Filter.
46103  * @param {Object} config Configuration options
46104  */
46105
46106
46107 Roo.htmleditor.Tidy = function(cfg) {
46108     Roo.apply(this, cfg);
46109     
46110     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46111      
46112 }
46113
46114 Roo.htmleditor.Tidy.toString = function(node)
46115 {
46116     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46117 }
46118
46119 Roo.htmleditor.Tidy.prototype = {
46120     
46121     
46122     wrap : function(s) {
46123         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46124     },
46125
46126     
46127     tidy : function(node, indent) {
46128      
46129         if  (node.nodeType == 3) {
46130             // text.
46131             
46132             
46133             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46134                 
46135             
46136         }
46137         
46138         if  (node.nodeType != 1) {
46139             return '';
46140         }
46141         
46142         
46143         
46144         if (node.tagName == 'BODY') {
46145             
46146             return this.cn(node, '');
46147         }
46148              
46149              // Prints the node tagName, such as <A>, <IMG>, etc
46150         var ret = "<" + node.tagName +  this.attr(node) ;
46151         
46152         // elements with no children..
46153         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46154                 return ret + '/>';
46155         }
46156         ret += '>';
46157         
46158         
46159         var cindent = indent === false ? '' : (indent + '  ');
46160         // tags where we will not pad the children.. (inline text tags etc..)
46161         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46162             cindent = false;
46163             
46164             
46165         }
46166         
46167         var cn = this.cn(node, cindent );
46168         
46169         return ret + cn  + '</' + node.tagName + '>';
46170         
46171     },
46172     cn: function(node, indent)
46173     {
46174         var ret = [];
46175         
46176         var ar = Array.from(node.childNodes);
46177         for (var i = 0 ; i < ar.length ; i++) {
46178             
46179             
46180             
46181             if (indent !== false   // indent==false preservies everything
46182                 && i > 0
46183                 && ar[i].nodeType == 3 
46184                 && ar[i].nodeValue.length > 0
46185                 && ar[i].nodeValue.match(/^\s+/)
46186             ) {
46187                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46188                     ret.pop(); // remove line break from last?
46189                 }
46190                 
46191                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46192             }
46193             if (indent !== false
46194                 && ar[i].nodeType == 1 // element - and indent is not set... 
46195             ) {
46196                 ret.push("\n" + indent); 
46197             }
46198             
46199             ret.push(this.tidy(ar[i], indent));
46200             // text + trailing indent 
46201             if (indent !== false
46202                 && ar[i].nodeType == 3
46203                 && ar[i].nodeValue.length > 0
46204                 && ar[i].nodeValue.match(/\s+$/)
46205             ){
46206                 ret.push("\n" + indent); 
46207             }
46208             
46209             
46210             
46211             
46212         }
46213         // what if all text?
46214         
46215         
46216         return ret.join('');
46217     },
46218     
46219          
46220         
46221     attr : function(node)
46222     {
46223         var attr = [];
46224         for(i = 0; i < node.attributes.length;i++) {
46225             
46226             // skip empty values?
46227             if (!node.attributes.item(i).value.length) {
46228                 continue;
46229             }
46230             attr.push(  node.attributes.item(i).name + '="' +
46231                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46232             );
46233         }
46234         return attr.length ? (' ' + attr.join(' ') ) : '';
46235         
46236     }
46237     
46238     
46239     
46240 }
46241 /**
46242  * @class Roo.htmleditor.KeyEnter
46243  * Handle Enter press..
46244  * @cfg {Roo.HtmlEditorCore} core the editor.
46245  * @constructor
46246  * Create a new Filter.
46247  * @param {Object} config Configuration options
46248  */
46249
46250
46251
46252 Roo.htmleditor.KeyEnter = function(cfg) {
46253     Roo.apply(this, cfg);
46254     // this does not actually call walk as it's really just a abstract class
46255  
46256     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46257 }
46258
46259
46260 Roo.htmleditor.KeyEnter.prototype = {
46261     
46262     core : false,
46263     
46264     keypress : function(e)
46265     {
46266         if (e.charCode != 13) {
46267             return true;
46268         }
46269         e.preventDefault();
46270         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46271         var doc = this.core.doc;
46272         
46273         var docFragment = doc.createDocumentFragment();
46274     
46275         //add a new line
46276         var newEle = doc.createTextNode('\n');
46277         docFragment.appendChild(newEle);
46278     
46279     
46280         var range = this.core.win.getSelection().getRangeAt(0);
46281         var n = range.commonAncestorContainer ;
46282         while (n && n.nodeType != 1) {
46283             n  = n.parentNode;
46284         }
46285         var li = false;
46286         if (n && n.tagName == 'UL') {
46287             li = doc.createElement('LI');
46288             n.appendChild(li);
46289             
46290         }
46291         if (n && n.tagName == 'LI') {
46292             li = doc.createElement('LI');
46293             if (n.nextSibling) {
46294                 n.parentNode.insertBefore(li, n.firstSibling);
46295                 
46296             } else {
46297                 n.parentNode.appendChild(li);
46298             }
46299         }
46300         if (li) {   
46301             range = doc.createRange();
46302             range.setStartAfter(li);
46303             range.collapse(true);
46304         
46305             //make the cursor there
46306             var sel = this.core.win.getSelection();
46307             sel.removeAllRanges();
46308             sel.addRange(range);
46309             return false;
46310             
46311             
46312         }
46313         //add the br, or p, or something else
46314         newEle = doc.createElement('br');
46315         docFragment.appendChild(newEle);
46316     
46317         //make the br replace selection
46318         
46319         range.deleteContents();
46320         
46321         range.insertNode(docFragment);
46322         range = range.cloneRange();
46323         range.collapse(true);
46324         var sel = this.core.win.getSelection();
46325         sel.removeAllRanges();
46326         sel.addRange(range);
46327         sel.collapseToEnd();
46328     
46329         return false;
46330          
46331     }
46332 };
46333      
46334 /**
46335  * @class Roo.htmleditor.Block
46336  * Base class for html editor blocks - do not use it directly .. extend it..
46337  * @cfg {DomElement} node The node to apply stuff to.
46338  * @cfg {String} friendly_name the name that appears in the context bar about this block
46339  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46340  
46341  * @constructor
46342  * Create a new Filter.
46343  * @param {Object} config Configuration options
46344  */
46345
46346 Roo.htmleditor.Block  = function(cfg)
46347 {
46348     // do nothing .. should not be called really.
46349 }
46350
46351 Roo.htmleditor.Block.factory = function(node)
46352 {
46353     
46354     var id = Roo.get(node).id;
46355     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46356         Roo.htmleditor.Block.cache[id].readElement();
46357         return Roo.htmleditor.Block.cache[id];
46358     }
46359     
46360     var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46361     if (typeof(cls) == 'undefined') {
46362         Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46363         return false;
46364     }
46365     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46366     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46367 };
46368 // question goes here... do we need to clear out this cache sometimes?
46369 // or show we make it relivant to the htmleditor.
46370 Roo.htmleditor.Block.cache = {};
46371
46372 Roo.htmleditor.Block.prototype = {
46373     
46374     node : false,
46375     
46376      // used by context menu
46377     friendly_name : 'Image with caption',
46378     
46379     context : false,
46380     /**
46381      * Update a node with values from this object
46382      * @param {DomElement} node
46383      */
46384     updateElement : function(node)
46385     {
46386         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46387     },
46388      /**
46389      * convert to plain HTML for calling insertAtCursor..
46390      */
46391     toHTML : function()
46392     {
46393         return Roo.DomHelper.markup(this.toObject());
46394     },
46395     /**
46396      * used by readEleemnt to extract data from a node
46397      * may need improving as it's pretty basic
46398      
46399      * @param {DomElement} node
46400      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46401      * @param {String} attribute (use html - for contents, or style for using next param as style)
46402      * @param {String} style the style property - eg. text-align
46403      */
46404     getVal : function(node, tag, attr, style)
46405     {
46406         var n = node;
46407         if (tag !== true && n.tagName != tag.toUpperCase()) {
46408             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46409             // but kiss for now.
46410             n = node.getElementsByTagName(tag).item(0);
46411         }
46412         if (attr == 'html') {
46413             return n.innerHTML;
46414         }
46415         if (attr == 'style') {
46416             return n.style[style]
46417         }
46418         
46419         return Roo.get(n).attr(attr);
46420             
46421     },
46422     /**
46423      * create a DomHelper friendly object - for use with 
46424      * Roo.DomHelper.markup / overwrite / etc..
46425      * (override this)
46426      */
46427     toObject : function()
46428     {
46429         return {};
46430     },
46431       /**
46432      * Read a node that has a 'data-block' property - and extract the values from it.
46433      * @param {DomElement} node - the node
46434      */
46435     readElement : function(node)
46436     {
46437         
46438     } 
46439     
46440     
46441 };
46442
46443  
46444
46445 /**
46446  * @class Roo.htmleditor.BlockFigure
46447  * Block that has an image and a figcaption
46448  * @cfg {String} image_src the url for the image
46449  * @cfg {String} align (left|right) alignment for the block default left
46450  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46451  * @cfg {String} caption the text to appear below  (and in the alt tag)
46452  * @cfg {String|number} image_width the width of the image number or %?
46453  * @cfg {String|number} image_height the height of the image number or %?
46454  * 
46455  * @constructor
46456  * Create a new Filter.
46457  * @param {Object} config Configuration options
46458  */
46459
46460 Roo.htmleditor.BlockFigure = function(cfg)
46461 {
46462     if (cfg.node) {
46463         this.readElement(cfg.node);
46464         this.updateElement(cfg.node);
46465     }
46466     Roo.apply(this, cfg);
46467 }
46468 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46469  
46470     
46471     // setable values.
46472     image_src: '',
46473     
46474     align: 'left',
46475     caption : '',
46476     text_align: 'left',
46477     
46478     width : '46%',
46479     margin: '2%',
46480     
46481     // used by context menu
46482     friendly_name : 'Image with caption',
46483     
46484     context : { // ?? static really
46485         width : {
46486             title: "Width",
46487             width: 40
46488             // ?? number
46489         },
46490         margin : {
46491             title: "Margin",
46492             width: 40
46493             // ?? number
46494         },
46495         align: {
46496             title: "Align",
46497             opts : [[ "left"],[ "right"]],
46498             width : 80
46499             
46500         },
46501         text_align: {
46502             title: "Caption Align",
46503             opts : [ [ "left"],[ "right"],[ "center"]],
46504             width : 80
46505         },
46506         
46507        
46508         image_src : {
46509             title: "Src",
46510             width: 220
46511         }
46512     },
46513     /**
46514      * create a DomHelper friendly object - for use with
46515      * Roo.DomHelper.markup / overwrite / etc..
46516      */
46517     toObject : function()
46518     {
46519         var d = document.createElement('div');
46520         d.innerHTML = this.caption;
46521         
46522         return {
46523             tag: 'figure',
46524             'data-block' : 'Figure',
46525             contenteditable : 'false',
46526             style : {
46527                 display: 'table',
46528                 float :  this.align ,
46529                 width :  this.width,
46530                 margin:  this.margin
46531             },
46532             cn : [
46533                 {
46534                     tag : 'img',
46535                     src : this.image_src,
46536                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46537                     style: {
46538                         width: '100%'
46539                     }
46540                 },
46541                 {
46542                     tag: 'figcaption',
46543                     contenteditable : true,
46544                     style : {
46545                         'text-align': this.text_align
46546                     },
46547                     html : this.caption
46548                     
46549                 }
46550             ]
46551         };
46552     },
46553     
46554     readElement : function(node)
46555     {
46556         this.image_src = this.getVal(node, 'img', 'src');
46557         this.align = this.getVal(node, 'figure', 'style', 'float');
46558         this.caption = this.getVal(node, 'figcaption', 'html');
46559         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46560         this.width = this.getVal(node, 'figure', 'style', 'width');
46561         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46562         
46563     } 
46564     
46565   
46566    
46567      
46568     
46569     
46570     
46571     
46572 })
46573
46574 //<script type="text/javascript">
46575
46576 /*
46577  * Based  Ext JS Library 1.1.1
46578  * Copyright(c) 2006-2007, Ext JS, LLC.
46579  * LGPL
46580  *
46581  */
46582  
46583 /**
46584  * @class Roo.HtmlEditorCore
46585  * @extends Roo.Component
46586  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46587  *
46588  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46589  */
46590
46591 Roo.HtmlEditorCore = function(config){
46592     
46593     
46594     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46595     
46596     
46597     this.addEvents({
46598         /**
46599          * @event initialize
46600          * Fires when the editor is fully initialized (including the iframe)
46601          * @param {Roo.HtmlEditorCore} this
46602          */
46603         initialize: true,
46604         /**
46605          * @event activate
46606          * Fires when the editor is first receives the focus. Any insertion must wait
46607          * until after this event.
46608          * @param {Roo.HtmlEditorCore} this
46609          */
46610         activate: true,
46611          /**
46612          * @event beforesync
46613          * Fires before the textarea is updated with content from the editor iframe. Return false
46614          * to cancel the sync.
46615          * @param {Roo.HtmlEditorCore} this
46616          * @param {String} html
46617          */
46618         beforesync: true,
46619          /**
46620          * @event beforepush
46621          * Fires before the iframe editor is updated with content from the textarea. Return false
46622          * to cancel the push.
46623          * @param {Roo.HtmlEditorCore} this
46624          * @param {String} html
46625          */
46626         beforepush: true,
46627          /**
46628          * @event sync
46629          * Fires when the textarea is updated with content from the editor iframe.
46630          * @param {Roo.HtmlEditorCore} this
46631          * @param {String} html
46632          */
46633         sync: true,
46634          /**
46635          * @event push
46636          * Fires when the iframe editor is updated with content from the textarea.
46637          * @param {Roo.HtmlEditorCore} this
46638          * @param {String} html
46639          */
46640         push: true,
46641         
46642         /**
46643          * @event editorevent
46644          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46645          * @param {Roo.HtmlEditorCore} this
46646          */
46647         editorevent: true
46648         
46649     });
46650     
46651     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46652     
46653     // defaults : white / black...
46654     this.applyBlacklists();
46655     
46656     
46657     
46658 };
46659
46660
46661 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46662
46663
46664      /**
46665      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46666      */
46667     
46668     owner : false,
46669     
46670      /**
46671      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46672      *                        Roo.resizable.
46673      */
46674     resizable : false,
46675      /**
46676      * @cfg {Number} height (in pixels)
46677      */   
46678     height: 300,
46679    /**
46680      * @cfg {Number} width (in pixels)
46681      */   
46682     width: 500,
46683     
46684     /**
46685      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46686      * 
46687      */
46688     stylesheets: false,
46689     
46690     /**
46691      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46692      */
46693     allowComments: false,
46694     // id of frame..
46695     frameId: false,
46696     
46697     // private properties
46698     validationEvent : false,
46699     deferHeight: true,
46700     initialized : false,
46701     activated : false,
46702     sourceEditMode : false,
46703     onFocus : Roo.emptyFn,
46704     iframePad:3,
46705     hideMode:'offsets',
46706     
46707     clearUp: true,
46708     
46709     // blacklist + whitelisted elements..
46710     black: false,
46711     white: false,
46712      
46713     bodyCls : '',
46714
46715     
46716     undoManager : false,
46717     /**
46718      * Protected method that will not generally be called directly. It
46719      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46720      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46721      */
46722     getDocMarkup : function(){
46723         // body styles..
46724         var st = '';
46725         
46726         // inherit styels from page...?? 
46727         if (this.stylesheets === false) {
46728             
46729             Roo.get(document.head).select('style').each(function(node) {
46730                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46731             });
46732             
46733             Roo.get(document.head).select('link').each(function(node) { 
46734                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46735             });
46736             
46737         } else if (!this.stylesheets.length) {
46738                 // simple..
46739                 st = '<style type="text/css">' +
46740                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46741                    '</style>';
46742         } else {
46743             for (var i in this.stylesheets) {
46744                 if (typeof(this.stylesheets[i]) != 'string') {
46745                     continue;
46746                 }
46747                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46748             }
46749             
46750         }
46751         
46752         st +=  '<style type="text/css">' +
46753             'IMG { cursor: pointer } ' +
46754         '</style>';
46755
46756         var cls = 'roo-htmleditor-body';
46757         
46758         if(this.bodyCls.length){
46759             cls += ' ' + this.bodyCls;
46760         }
46761         
46762         return '<html><head>' + st  +
46763             //<style type="text/css">' +
46764             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46765             //'</style>' +
46766             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46767     },
46768
46769     // private
46770     onRender : function(ct, position)
46771     {
46772         var _t = this;
46773         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46774         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46775         
46776         
46777         this.el.dom.style.border = '0 none';
46778         this.el.dom.setAttribute('tabIndex', -1);
46779         this.el.addClass('x-hidden hide');
46780         
46781         
46782         
46783         if(Roo.isIE){ // fix IE 1px bogus margin
46784             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46785         }
46786        
46787         
46788         this.frameId = Roo.id();
46789         
46790          
46791         
46792         var iframe = this.owner.wrap.createChild({
46793             tag: 'iframe',
46794             cls: 'form-control', // bootstrap..
46795             id: this.frameId,
46796             name: this.frameId,
46797             frameBorder : 'no',
46798             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46799         }, this.el
46800         );
46801         
46802         
46803         this.iframe = iframe.dom;
46804
46805         this.assignDocWin();
46806         
46807         this.doc.designMode = 'on';
46808        
46809         this.doc.open();
46810         this.doc.write(this.getDocMarkup());
46811         this.doc.close();
46812
46813         
46814         var task = { // must defer to wait for browser to be ready
46815             run : function(){
46816                 //console.log("run task?" + this.doc.readyState);
46817                 this.assignDocWin();
46818                 if(this.doc.body || this.doc.readyState == 'complete'){
46819                     try {
46820                         this.doc.designMode="on";
46821                         
46822                     } catch (e) {
46823                         return;
46824                     }
46825                     Roo.TaskMgr.stop(task);
46826                     this.initEditor.defer(10, this);
46827                 }
46828             },
46829             interval : 10,
46830             duration: 10000,
46831             scope: this
46832         };
46833         Roo.TaskMgr.start(task);
46834
46835     },
46836
46837     // private
46838     onResize : function(w, h)
46839     {
46840          Roo.log('resize: ' +w + ',' + h );
46841         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46842         if(!this.iframe){
46843             return;
46844         }
46845         if(typeof w == 'number'){
46846             
46847             this.iframe.style.width = w + 'px';
46848         }
46849         if(typeof h == 'number'){
46850             
46851             this.iframe.style.height = h + 'px';
46852             if(this.doc){
46853                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46854             }
46855         }
46856         
46857     },
46858
46859     /**
46860      * Toggles the editor between standard and source edit mode.
46861      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46862      */
46863     toggleSourceEdit : function(sourceEditMode){
46864         
46865         this.sourceEditMode = sourceEditMode === true;
46866         
46867         if(this.sourceEditMode){
46868  
46869             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46870             
46871         }else{
46872             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46873             //this.iframe.className = '';
46874             this.deferFocus();
46875         }
46876         //this.setSize(this.owner.wrap.getSize());
46877         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46878     },
46879
46880     
46881   
46882
46883     /**
46884      * Protected method that will not generally be called directly. If you need/want
46885      * custom HTML cleanup, this is the method you should override.
46886      * @param {String} html The HTML to be cleaned
46887      * return {String} The cleaned HTML
46888      */
46889     cleanHtml : function(html){
46890         html = String(html);
46891         if(html.length > 5){
46892             if(Roo.isSafari){ // strip safari nonsense
46893                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46894             }
46895         }
46896         if(html == '&nbsp;'){
46897             html = '';
46898         }
46899         return html;
46900     },
46901
46902     /**
46903      * HTML Editor -> Textarea
46904      * Protected method that will not generally be called directly. Syncs the contents
46905      * of the editor iframe with the textarea.
46906      */
46907     syncValue : function()
46908     {
46909         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46910         if(this.initialized){
46911             
46912             this.undoManager.addEvent();
46913
46914             
46915             var bd = (this.doc.body || this.doc.documentElement);
46916             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46917             
46918             // not sure if this is really the place for this
46919             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46920             // this has to update attributes that get duped.. like alt and caption..
46921             
46922             
46923             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46924             //     Roo.htmleditor.Block.factory(e);
46925             //},this);
46926             
46927             
46928             var div = document.createElement('div');
46929             div.innerHTML = bd.innerHTML;
46930             // remove content editable. (blocks)
46931             
46932            
46933             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46934             //?? tidy?
46935             var html = div.innerHTML;
46936             if(Roo.isSafari){
46937                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46938                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46939                 if(m && m[1]){
46940                     html = '<div style="'+m[0]+'">' + html + '</div>';
46941                 }
46942             }
46943             html = this.cleanHtml(html);
46944             // fix up the special chars.. normaly like back quotes in word...
46945             // however we do not want to do this with chinese..
46946             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46947                 
46948                 var cc = match.charCodeAt();
46949
46950                 // Get the character value, handling surrogate pairs
46951                 if (match.length == 2) {
46952                     // It's a surrogate pair, calculate the Unicode code point
46953                     var high = match.charCodeAt(0) - 0xD800;
46954                     var low  = match.charCodeAt(1) - 0xDC00;
46955                     cc = (high * 0x400) + low + 0x10000;
46956                 }  else if (
46957                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46958                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46959                     (cc >= 0xf900 && cc < 0xfb00 )
46960                 ) {
46961                         return match;
46962                 }  
46963          
46964                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46965                 return "&#" + cc + ";";
46966                 
46967                 
46968             });
46969             
46970             
46971              
46972             if(this.owner.fireEvent('beforesync', this, html) !== false){
46973                 this.el.dom.value = html;
46974                 this.owner.fireEvent('sync', this, html);
46975             }
46976         }
46977     },
46978
46979     /**
46980      * TEXTAREA -> EDITABLE
46981      * Protected method that will not generally be called directly. Pushes the value of the textarea
46982      * into the iframe editor.
46983      */
46984     pushValue : function()
46985     {
46986         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46987         if(this.initialized){
46988             var v = this.el.dom.value.trim();
46989             
46990             
46991             if(this.owner.fireEvent('beforepush', this, v) !== false){
46992                 var d = (this.doc.body || this.doc.documentElement);
46993                 d.innerHTML = v;
46994                  
46995                 this.el.dom.value = d.innerHTML;
46996                 this.owner.fireEvent('push', this, v);
46997             }
46998             
46999             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
47000                 
47001                 Roo.htmleditor.Block.factory(e);
47002                 
47003             },this);
47004             var lc = this.doc.body.lastChild;
47005             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47006                 // add an extra line at the end.
47007                 this.doc.body.appendChild(this.doc.createElement('br'));
47008             }
47009             
47010             
47011         }
47012     },
47013
47014     // private
47015     deferFocus : function(){
47016         this.focus.defer(10, this);
47017     },
47018
47019     // doc'ed in Field
47020     focus : function(){
47021         if(this.win && !this.sourceEditMode){
47022             this.win.focus();
47023         }else{
47024             this.el.focus();
47025         }
47026     },
47027     
47028     assignDocWin: function()
47029     {
47030         var iframe = this.iframe;
47031         
47032          if(Roo.isIE){
47033             this.doc = iframe.contentWindow.document;
47034             this.win = iframe.contentWindow;
47035         } else {
47036 //            if (!Roo.get(this.frameId)) {
47037 //                return;
47038 //            }
47039 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47040 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47041             
47042             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47043                 return;
47044             }
47045             
47046             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47047             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47048         }
47049     },
47050     
47051     // private
47052     initEditor : function(){
47053         //console.log("INIT EDITOR");
47054         this.assignDocWin();
47055         
47056         
47057         
47058         this.doc.designMode="on";
47059         this.doc.open();
47060         this.doc.write(this.getDocMarkup());
47061         this.doc.close();
47062         
47063         var dbody = (this.doc.body || this.doc.documentElement);
47064         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47065         // this copies styles from the containing element into thsi one..
47066         // not sure why we need all of this..
47067         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47068         
47069         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47070         //ss['background-attachment'] = 'fixed'; // w3c
47071         dbody.bgProperties = 'fixed'; // ie
47072         //Roo.DomHelper.applyStyles(dbody, ss);
47073         Roo.EventManager.on(this.doc, {
47074             //'mousedown': this.onEditorEvent,
47075             'mouseup': this.onEditorEvent,
47076             'dblclick': this.onEditorEvent,
47077             'click': this.onEditorEvent,
47078             'keyup': this.onEditorEvent,
47079             
47080             buffer:100,
47081             scope: this
47082         });
47083         Roo.EventManager.on(this.doc, {
47084             'paste': this.onPasteEvent,
47085             scope : this
47086         });
47087         if(Roo.isGecko){
47088             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47089         }
47090         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47091             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47092         }
47093         this.initialized = true;
47094
47095         
47096         // initialize special key events - enter
47097         new Roo.htmleditor.KeyEnter({core : this});
47098         
47099          
47100         
47101         this.owner.fireEvent('initialize', this);
47102         this.pushValue();
47103     },
47104     
47105     onPasteEvent : function(e,v)
47106     {
47107         // I think we better assume paste is going to be a dirty load of rubish from word..
47108         
47109         // even pasting into a 'email version' of this widget will have to clean up that mess.
47110         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47111         
47112         // check what type of paste - if it's an image, then handle it differently.
47113         if (cd.files.length > 0) {
47114             // pasting images?
47115             var urlAPI = (window.createObjectURL && window) || 
47116                 (window.URL && URL.revokeObjectURL && URL) || 
47117                 (window.webkitURL && webkitURL);
47118     
47119             var url = urlAPI.createObjectURL( cd.files[0]);
47120             this.insertAtCursor('<img src=" + url + ">');
47121             return false;
47122         }
47123         
47124         var html = cd.getData('text/html'); // clipboard event
47125         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47126         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47127         Roo.log(images);
47128         //Roo.log(imgs);
47129         // fixme..
47130         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47131                        .map(function(g) { return g.toDataURL(); });
47132         
47133         
47134         html = this.cleanWordChars(html);
47135         
47136         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47137         
47138         if (images.length > 0) {
47139             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47140                 img.setAttribute('src', images[i]);
47141             });
47142         }
47143         
47144       
47145         new Roo.htmleditor.FilterStyleToTag({ node : d });
47146         new Roo.htmleditor.FilterAttributes({
47147             node : d,
47148             attrib_white : ['href', 'src', 'name', 'align'],
47149             attrib_clean : ['href', 'src' ] 
47150         });
47151         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47152         // should be fonts..
47153         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47154         new Roo.htmleditor.FilterParagraph({ node : d });
47155         new Roo.htmleditor.FilterSpan({ node : d });
47156         new Roo.htmleditor.FilterLongBr({ node : d });
47157         
47158         
47159         
47160         this.insertAtCursor(d.innerHTML);
47161         
47162         e.preventDefault();
47163         return false;
47164         // default behaveiour should be our local cleanup paste? (optional?)
47165         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47166         //this.owner.fireEvent('paste', e, v);
47167     },
47168     // private
47169     onDestroy : function(){
47170         
47171         
47172         
47173         if(this.rendered){
47174             
47175             //for (var i =0; i < this.toolbars.length;i++) {
47176             //    // fixme - ask toolbars for heights?
47177             //    this.toolbars[i].onDestroy();
47178            // }
47179             
47180             //this.wrap.dom.innerHTML = '';
47181             //this.wrap.remove();
47182         }
47183     },
47184
47185     // private
47186     onFirstFocus : function(){
47187         
47188         this.assignDocWin();
47189         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47190         
47191         this.activated = true;
47192          
47193     
47194         if(Roo.isGecko){ // prevent silly gecko errors
47195             this.win.focus();
47196             var s = this.win.getSelection();
47197             if(!s.focusNode || s.focusNode.nodeType != 3){
47198                 var r = s.getRangeAt(0);
47199                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47200                 r.collapse(true);
47201                 this.deferFocus();
47202             }
47203             try{
47204                 this.execCmd('useCSS', true);
47205                 this.execCmd('styleWithCSS', false);
47206             }catch(e){}
47207         }
47208         this.owner.fireEvent('activate', this);
47209     },
47210
47211     // private
47212     adjustFont: function(btn){
47213         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47214         //if(Roo.isSafari){ // safari
47215         //    adjust *= 2;
47216        // }
47217         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47218         if(Roo.isSafari){ // safari
47219             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47220             v =  (v < 10) ? 10 : v;
47221             v =  (v > 48) ? 48 : v;
47222             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47223             
47224         }
47225         
47226         
47227         v = Math.max(1, v+adjust);
47228         
47229         this.execCmd('FontSize', v  );
47230     },
47231
47232     onEditorEvent : function(e)
47233     {
47234         this.owner.fireEvent('editorevent', this, e);
47235       //  this.updateToolbar();
47236         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47237     },
47238
47239     insertTag : function(tg)
47240     {
47241         // could be a bit smarter... -> wrap the current selected tRoo..
47242         if (tg.toLowerCase() == 'span' ||
47243             tg.toLowerCase() == 'code' ||
47244             tg.toLowerCase() == 'sup' ||
47245             tg.toLowerCase() == 'sub' 
47246             ) {
47247             
47248             range = this.createRange(this.getSelection());
47249             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47250             wrappingNode.appendChild(range.extractContents());
47251             range.insertNode(wrappingNode);
47252
47253             return;
47254             
47255             
47256             
47257         }
47258         this.execCmd("formatblock",   tg);
47259         this.undoManager.addEvent(); 
47260     },
47261     
47262     insertText : function(txt)
47263     {
47264         
47265         
47266         var range = this.createRange();
47267         range.deleteContents();
47268                //alert(Sender.getAttribute('label'));
47269                
47270         range.insertNode(this.doc.createTextNode(txt));
47271         this.undoManager.addEvent();
47272     } ,
47273     
47274      
47275
47276     /**
47277      * Executes a Midas editor command on the editor document and performs necessary focus and
47278      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47279      * @param {String} cmd The Midas command
47280      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47281      */
47282     relayCmd : function(cmd, value){
47283         this.win.focus();
47284         this.execCmd(cmd, value);
47285         this.owner.fireEvent('editorevent', this);
47286         //this.updateToolbar();
47287         this.owner.deferFocus();
47288     },
47289
47290     /**
47291      * Executes a Midas editor command directly on the editor document.
47292      * For visual commands, you should use {@link #relayCmd} instead.
47293      * <b>This should only be called after the editor is initialized.</b>
47294      * @param {String} cmd The Midas command
47295      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47296      */
47297     execCmd : function(cmd, value){
47298         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47299         this.syncValue();
47300     },
47301  
47302  
47303    
47304     /**
47305      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47306      * to insert tRoo.
47307      * @param {String} text | dom node.. 
47308      */
47309     insertAtCursor : function(text)
47310     {
47311         
47312         if(!this.activated){
47313             return;
47314         }
47315          
47316         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47317             this.win.focus();
47318             
47319             
47320             // from jquery ui (MIT licenced)
47321             var range, node;
47322             var win = this.win;
47323             
47324             if (win.getSelection && win.getSelection().getRangeAt) {
47325                 
47326                 // delete the existing?
47327                 
47328                 this.createRange(this.getSelection()).deleteContents();
47329                 range = win.getSelection().getRangeAt(0);
47330                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47331                 range.insertNode(node);
47332                 range = range.cloneRange();
47333                 range.collapse(false);
47334                  
47335                 win.getSelection().removeAllRanges();
47336                 win.getSelection().addRange(range);
47337                 
47338                 
47339                 
47340             } else if (win.document.selection && win.document.selection.createRange) {
47341                 // no firefox support
47342                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47343                 win.document.selection.createRange().pasteHTML(txt);
47344             
47345             } else {
47346                 // no firefox support
47347                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47348                 this.execCmd('InsertHTML', txt);
47349             } 
47350             this.syncValue();
47351             
47352             this.deferFocus();
47353         }
47354     },
47355  // private
47356     mozKeyPress : function(e){
47357         if(e.ctrlKey){
47358             var c = e.getCharCode(), cmd;
47359           
47360             if(c > 0){
47361                 c = String.fromCharCode(c).toLowerCase();
47362                 switch(c){
47363                     case 'b':
47364                         cmd = 'bold';
47365                         break;
47366                     case 'i':
47367                         cmd = 'italic';
47368                         break;
47369                     
47370                     case 'u':
47371                         cmd = 'underline';
47372                         break;
47373                     
47374                     //case 'v':
47375                       //  this.cleanUpPaste.defer(100, this);
47376                       //  return;
47377                         
47378                 }
47379                 if(cmd){
47380                     this.win.focus();
47381                     this.execCmd(cmd);
47382                     this.deferFocus();
47383                     e.preventDefault();
47384                 }
47385                 
47386             }
47387         }
47388     },
47389
47390     // private
47391     fixKeys : function(){ // load time branching for fastest keydown performance
47392         if(Roo.isIE){
47393             return function(e){
47394                 var k = e.getKey(), r;
47395                 if(k == e.TAB){
47396                     e.stopEvent();
47397                     r = this.doc.selection.createRange();
47398                     if(r){
47399                         r.collapse(true);
47400                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47401                         this.deferFocus();
47402                     }
47403                     return;
47404                 }
47405                 
47406                 if(k == e.ENTER){
47407                     r = this.doc.selection.createRange();
47408                     if(r){
47409                         var target = r.parentElement();
47410                         if(!target || target.tagName.toLowerCase() != 'li'){
47411                             e.stopEvent();
47412                             r.pasteHTML('<br/>');
47413                             r.collapse(false);
47414                             r.select();
47415                         }
47416                     }
47417                 }
47418                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47419                 //    this.cleanUpPaste.defer(100, this);
47420                 //    return;
47421                 //}
47422                 
47423                 
47424             };
47425         }else if(Roo.isOpera){
47426             return function(e){
47427                 var k = e.getKey();
47428                 if(k == e.TAB){
47429                     e.stopEvent();
47430                     this.win.focus();
47431                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47432                     this.deferFocus();
47433                 }
47434                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47435                 //    this.cleanUpPaste.defer(100, this);
47436                  //   return;
47437                 //}
47438                 
47439             };
47440         }else if(Roo.isSafari){
47441             return function(e){
47442                 var k = e.getKey();
47443                 
47444                 if(k == e.TAB){
47445                     e.stopEvent();
47446                     this.execCmd('InsertText','\t');
47447                     this.deferFocus();
47448                     return;
47449                 }
47450                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47451                  //   this.cleanUpPaste.defer(100, this);
47452                  //   return;
47453                // }
47454                 
47455              };
47456         }
47457     }(),
47458     
47459     getAllAncestors: function()
47460     {
47461         var p = this.getSelectedNode();
47462         var a = [];
47463         if (!p) {
47464             a.push(p); // push blank onto stack..
47465             p = this.getParentElement();
47466         }
47467         
47468         
47469         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47470             a.push(p);
47471             p = p.parentNode;
47472         }
47473         a.push(this.doc.body);
47474         return a;
47475     },
47476     lastSel : false,
47477     lastSelNode : false,
47478     
47479     
47480     getSelection : function() 
47481     {
47482         this.assignDocWin();
47483         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47484     },
47485     /**
47486      * Select a dom node
47487      * @param {DomElement} node the node to select
47488      */
47489     selectNode : function(node)
47490     {
47491         var nodeRange = node.ownerDocument.createRange();
47492         try {
47493             nodeRange.selectNode(node);
47494         } catch (e) {
47495             nodeRange.selectNodeContents(node);
47496         }
47497         //nodeRange.collapse(true);
47498         var s = this.win.getSelection();
47499         s.removeAllRanges();
47500         s.addRange(nodeRange);
47501     },
47502     
47503     getSelectedNode: function() 
47504     {
47505         // this may only work on Gecko!!!
47506         
47507         // should we cache this!!!!
47508         
47509         
47510         
47511          
47512         var range = this.createRange(this.getSelection()).cloneRange();
47513         
47514         if (Roo.isIE) {
47515             var parent = range.parentElement();
47516             while (true) {
47517                 var testRange = range.duplicate();
47518                 testRange.moveToElementText(parent);
47519                 if (testRange.inRange(range)) {
47520                     break;
47521                 }
47522                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47523                     break;
47524                 }
47525                 parent = parent.parentElement;
47526             }
47527             return parent;
47528         }
47529         
47530         // is ancestor a text element.
47531         var ac =  range.commonAncestorContainer;
47532         if (ac.nodeType == 3) {
47533             ac = ac.parentNode;
47534         }
47535         
47536         var ar = ac.childNodes;
47537          
47538         var nodes = [];
47539         var other_nodes = [];
47540         var has_other_nodes = false;
47541         for (var i=0;i<ar.length;i++) {
47542             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47543                 continue;
47544             }
47545             // fullly contained node.
47546             
47547             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47548                 nodes.push(ar[i]);
47549                 continue;
47550             }
47551             
47552             // probably selected..
47553             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47554                 other_nodes.push(ar[i]);
47555                 continue;
47556             }
47557             // outer..
47558             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47559                 continue;
47560             }
47561             
47562             
47563             has_other_nodes = true;
47564         }
47565         if (!nodes.length && other_nodes.length) {
47566             nodes= other_nodes;
47567         }
47568         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47569             return false;
47570         }
47571         
47572         return nodes[0];
47573     },
47574     createRange: function(sel)
47575     {
47576         // this has strange effects when using with 
47577         // top toolbar - not sure if it's a great idea.
47578         //this.editor.contentWindow.focus();
47579         if (typeof sel != "undefined") {
47580             try {
47581                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47582             } catch(e) {
47583                 return this.doc.createRange();
47584             }
47585         } else {
47586             return this.doc.createRange();
47587         }
47588     },
47589     getParentElement: function()
47590     {
47591         
47592         this.assignDocWin();
47593         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47594         
47595         var range = this.createRange(sel);
47596          
47597         try {
47598             var p = range.commonAncestorContainer;
47599             while (p.nodeType == 3) { // text node
47600                 p = p.parentNode;
47601             }
47602             return p;
47603         } catch (e) {
47604             return null;
47605         }
47606     
47607     },
47608     /***
47609      *
47610      * Range intersection.. the hard stuff...
47611      *  '-1' = before
47612      *  '0' = hits..
47613      *  '1' = after.
47614      *         [ -- selected range --- ]
47615      *   [fail]                        [fail]
47616      *
47617      *    basically..
47618      *      if end is before start or  hits it. fail.
47619      *      if start is after end or hits it fail.
47620      *
47621      *   if either hits (but other is outside. - then it's not 
47622      *   
47623      *    
47624      **/
47625     
47626     
47627     // @see http://www.thismuchiknow.co.uk/?p=64.
47628     rangeIntersectsNode : function(range, node)
47629     {
47630         var nodeRange = node.ownerDocument.createRange();
47631         try {
47632             nodeRange.selectNode(node);
47633         } catch (e) {
47634             nodeRange.selectNodeContents(node);
47635         }
47636     
47637         var rangeStartRange = range.cloneRange();
47638         rangeStartRange.collapse(true);
47639     
47640         var rangeEndRange = range.cloneRange();
47641         rangeEndRange.collapse(false);
47642     
47643         var nodeStartRange = nodeRange.cloneRange();
47644         nodeStartRange.collapse(true);
47645     
47646         var nodeEndRange = nodeRange.cloneRange();
47647         nodeEndRange.collapse(false);
47648     
47649         return rangeStartRange.compareBoundaryPoints(
47650                  Range.START_TO_START, nodeEndRange) == -1 &&
47651                rangeEndRange.compareBoundaryPoints(
47652                  Range.START_TO_START, nodeStartRange) == 1;
47653         
47654          
47655     },
47656     rangeCompareNode : function(range, node)
47657     {
47658         var nodeRange = node.ownerDocument.createRange();
47659         try {
47660             nodeRange.selectNode(node);
47661         } catch (e) {
47662             nodeRange.selectNodeContents(node);
47663         }
47664         
47665         
47666         range.collapse(true);
47667     
47668         nodeRange.collapse(true);
47669      
47670         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47671         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47672          
47673         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47674         
47675         var nodeIsBefore   =  ss == 1;
47676         var nodeIsAfter    = ee == -1;
47677         
47678         if (nodeIsBefore && nodeIsAfter) {
47679             return 0; // outer
47680         }
47681         if (!nodeIsBefore && nodeIsAfter) {
47682             return 1; //right trailed.
47683         }
47684         
47685         if (nodeIsBefore && !nodeIsAfter) {
47686             return 2;  // left trailed.
47687         }
47688         // fully contined.
47689         return 3;
47690     },
47691  
47692     cleanWordChars : function(input) {// change the chars to hex code
47693         
47694        var swapCodes  = [ 
47695             [    8211, "&#8211;" ], 
47696             [    8212, "&#8212;" ], 
47697             [    8216,  "'" ],  
47698             [    8217, "'" ],  
47699             [    8220, '"' ],  
47700             [    8221, '"' ],  
47701             [    8226, "*" ],  
47702             [    8230, "..." ]
47703         ]; 
47704         var output = input;
47705         Roo.each(swapCodes, function(sw) { 
47706             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47707             
47708             output = output.replace(swapper, sw[1]);
47709         });
47710         
47711         return output;
47712     },
47713     
47714      
47715     
47716         
47717     
47718     cleanUpChild : function (node)
47719     {
47720         
47721         new Roo.htmleditor.FilterComment({node : node});
47722         new Roo.htmleditor.FilterAttributes({
47723                 node : node,
47724                 attrib_black : this.ablack,
47725                 attrib_clean : this.aclean,
47726                 style_white : this.cwhite,
47727                 style_black : this.cblack
47728         });
47729         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47730         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47731          
47732         
47733     },
47734     
47735     /**
47736      * Clean up MS wordisms...
47737      * @deprecated - use filter directly
47738      */
47739     cleanWord : function(node)
47740     {
47741         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47742         
47743     },
47744    
47745     
47746     /**
47747
47748      * @deprecated - use filters
47749      */
47750     cleanTableWidths : function(node)
47751     {
47752         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47753         
47754  
47755     },
47756     
47757      
47758         
47759     applyBlacklists : function()
47760     {
47761         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47762         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47763         
47764         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47765         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47766         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47767         
47768         this.white = [];
47769         this.black = [];
47770         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47771             if (b.indexOf(tag) > -1) {
47772                 return;
47773             }
47774             this.white.push(tag);
47775             
47776         }, this);
47777         
47778         Roo.each(w, function(tag) {
47779             if (b.indexOf(tag) > -1) {
47780                 return;
47781             }
47782             if (this.white.indexOf(tag) > -1) {
47783                 return;
47784             }
47785             this.white.push(tag);
47786             
47787         }, this);
47788         
47789         
47790         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47791             if (w.indexOf(tag) > -1) {
47792                 return;
47793             }
47794             this.black.push(tag);
47795             
47796         }, this);
47797         
47798         Roo.each(b, function(tag) {
47799             if (w.indexOf(tag) > -1) {
47800                 return;
47801             }
47802             if (this.black.indexOf(tag) > -1) {
47803                 return;
47804             }
47805             this.black.push(tag);
47806             
47807         }, this);
47808         
47809         
47810         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47811         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47812         
47813         this.cwhite = [];
47814         this.cblack = [];
47815         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47816             if (b.indexOf(tag) > -1) {
47817                 return;
47818             }
47819             this.cwhite.push(tag);
47820             
47821         }, this);
47822         
47823         Roo.each(w, function(tag) {
47824             if (b.indexOf(tag) > -1) {
47825                 return;
47826             }
47827             if (this.cwhite.indexOf(tag) > -1) {
47828                 return;
47829             }
47830             this.cwhite.push(tag);
47831             
47832         }, this);
47833         
47834         
47835         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47836             if (w.indexOf(tag) > -1) {
47837                 return;
47838             }
47839             this.cblack.push(tag);
47840             
47841         }, this);
47842         
47843         Roo.each(b, function(tag) {
47844             if (w.indexOf(tag) > -1) {
47845                 return;
47846             }
47847             if (this.cblack.indexOf(tag) > -1) {
47848                 return;
47849             }
47850             this.cblack.push(tag);
47851             
47852         }, this);
47853     },
47854     
47855     setStylesheets : function(stylesheets)
47856     {
47857         if(typeof(stylesheets) == 'string'){
47858             Roo.get(this.iframe.contentDocument.head).createChild({
47859                 tag : 'link',
47860                 rel : 'stylesheet',
47861                 type : 'text/css',
47862                 href : stylesheets
47863             });
47864             
47865             return;
47866         }
47867         var _this = this;
47868      
47869         Roo.each(stylesheets, function(s) {
47870             if(!s.length){
47871                 return;
47872             }
47873             
47874             Roo.get(_this.iframe.contentDocument.head).createChild({
47875                 tag : 'link',
47876                 rel : 'stylesheet',
47877                 type : 'text/css',
47878                 href : s
47879             });
47880         });
47881
47882         
47883     },
47884     
47885     removeStylesheets : function()
47886     {
47887         var _this = this;
47888         
47889         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47890             s.remove();
47891         });
47892     },
47893     
47894     setStyle : function(style)
47895     {
47896         Roo.get(this.iframe.contentDocument.head).createChild({
47897             tag : 'style',
47898             type : 'text/css',
47899             html : style
47900         });
47901
47902         return;
47903     }
47904     
47905     // hide stuff that is not compatible
47906     /**
47907      * @event blur
47908      * @hide
47909      */
47910     /**
47911      * @event change
47912      * @hide
47913      */
47914     /**
47915      * @event focus
47916      * @hide
47917      */
47918     /**
47919      * @event specialkey
47920      * @hide
47921      */
47922     /**
47923      * @cfg {String} fieldClass @hide
47924      */
47925     /**
47926      * @cfg {String} focusClass @hide
47927      */
47928     /**
47929      * @cfg {String} autoCreate @hide
47930      */
47931     /**
47932      * @cfg {String} inputType @hide
47933      */
47934     /**
47935      * @cfg {String} invalidClass @hide
47936      */
47937     /**
47938      * @cfg {String} invalidText @hide
47939      */
47940     /**
47941      * @cfg {String} msgFx @hide
47942      */
47943     /**
47944      * @cfg {String} validateOnBlur @hide
47945      */
47946 });
47947
47948 Roo.HtmlEditorCore.white = [
47949         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47950         
47951        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47952        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47953        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47954        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47955        'TABLE',   'UL',         'XMP', 
47956        
47957        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47958       'THEAD',   'TR', 
47959      
47960       'DIR', 'MENU', 'OL', 'UL', 'DL',
47961        
47962       'EMBED',  'OBJECT'
47963 ];
47964
47965
47966 Roo.HtmlEditorCore.black = [
47967     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47968         'APPLET', // 
47969         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47970         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47971         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47972         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47973         //'FONT' // CLEAN LATER..
47974         'COLGROUP', 'COL'  // messy tables.
47975         
47976 ];
47977 Roo.HtmlEditorCore.clean = [ // ?? needed???
47978      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47979 ];
47980 Roo.HtmlEditorCore.tag_remove = [
47981     'FONT', 'TBODY'  
47982 ];
47983 // attributes..
47984
47985 Roo.HtmlEditorCore.ablack = [
47986     'on'
47987 ];
47988     
47989 Roo.HtmlEditorCore.aclean = [ 
47990     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47991 ];
47992
47993 // protocols..
47994 Roo.HtmlEditorCore.pwhite= [
47995         'http',  'https',  'mailto'
47996 ];
47997
47998 // white listed style attributes.
47999 Roo.HtmlEditorCore.cwhite= [
48000       //  'text-align', /// default is to allow most things..
48001       
48002          
48003 //        'font-size'//??
48004 ];
48005
48006 // black listed style attributes.
48007 Roo.HtmlEditorCore.cblack= [
48008       //  'font-size' -- this can be set by the project 
48009 ];
48010
48011
48012
48013
48014     //<script type="text/javascript">
48015
48016 /*
48017  * Ext JS Library 1.1.1
48018  * Copyright(c) 2006-2007, Ext JS, LLC.
48019  * Licence LGPL
48020  * 
48021  */
48022  
48023  
48024 Roo.form.HtmlEditor = function(config){
48025     
48026     
48027     
48028     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48029     
48030     if (!this.toolbars) {
48031         this.toolbars = [];
48032     }
48033     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48034     
48035     
48036 };
48037
48038 /**
48039  * @class Roo.form.HtmlEditor
48040  * @extends Roo.form.Field
48041  * Provides a lightweight HTML Editor component.
48042  *
48043  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48044  * 
48045  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48046  * supported by this editor.</b><br/><br/>
48047  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48048  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48049  */
48050 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48051     /**
48052      * @cfg {Boolean} clearUp
48053      */
48054     clearUp : true,
48055       /**
48056      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48057      */
48058     toolbars : false,
48059    
48060      /**
48061      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48062      *                        Roo.resizable.
48063      */
48064     resizable : false,
48065      /**
48066      * @cfg {Number} height (in pixels)
48067      */   
48068     height: 300,
48069    /**
48070      * @cfg {Number} width (in pixels)
48071      */   
48072     width: 500,
48073     
48074     /**
48075      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48076      * 
48077      */
48078     stylesheets: false,
48079     
48080     
48081      /**
48082      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48083      * 
48084      */
48085     cblack: false,
48086     /**
48087      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48088      * 
48089      */
48090     cwhite: false,
48091     
48092      /**
48093      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48094      * 
48095      */
48096     black: false,
48097     /**
48098      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48099      * 
48100      */
48101     white: false,
48102     /**
48103      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48104      */
48105     allowComments: false,
48106     /**
48107      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48108      */
48109     
48110     
48111      bodyCls : '',
48112     
48113     // id of frame..
48114     frameId: false,
48115     
48116     // private properties
48117     validationEvent : false,
48118     deferHeight: true,
48119     initialized : false,
48120     activated : false,
48121     
48122     onFocus : Roo.emptyFn,
48123     iframePad:3,
48124     hideMode:'offsets',
48125     
48126     actionMode : 'container', // defaults to hiding it...
48127     
48128     defaultAutoCreate : { // modified by initCompnoent..
48129         tag: "textarea",
48130         style:"width:500px;height:300px;",
48131         autocomplete: "new-password"
48132     },
48133
48134     // private
48135     initComponent : function(){
48136         this.addEvents({
48137             /**
48138              * @event initialize
48139              * Fires when the editor is fully initialized (including the iframe)
48140              * @param {HtmlEditor} this
48141              */
48142             initialize: true,
48143             /**
48144              * @event activate
48145              * Fires when the editor is first receives the focus. Any insertion must wait
48146              * until after this event.
48147              * @param {HtmlEditor} this
48148              */
48149             activate: true,
48150              /**
48151              * @event beforesync
48152              * Fires before the textarea is updated with content from the editor iframe. Return false
48153              * to cancel the sync.
48154              * @param {HtmlEditor} this
48155              * @param {String} html
48156              */
48157             beforesync: true,
48158              /**
48159              * @event beforepush
48160              * Fires before the iframe editor is updated with content from the textarea. Return false
48161              * to cancel the push.
48162              * @param {HtmlEditor} this
48163              * @param {String} html
48164              */
48165             beforepush: true,
48166              /**
48167              * @event sync
48168              * Fires when the textarea is updated with content from the editor iframe.
48169              * @param {HtmlEditor} this
48170              * @param {String} html
48171              */
48172             sync: true,
48173              /**
48174              * @event push
48175              * Fires when the iframe editor is updated with content from the textarea.
48176              * @param {HtmlEditor} this
48177              * @param {String} html
48178              */
48179             push: true,
48180              /**
48181              * @event editmodechange
48182              * Fires when the editor switches edit modes
48183              * @param {HtmlEditor} this
48184              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48185              */
48186             editmodechange: true,
48187             /**
48188              * @event editorevent
48189              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48190              * @param {HtmlEditor} this
48191              */
48192             editorevent: true,
48193             /**
48194              * @event firstfocus
48195              * Fires when on first focus - needed by toolbars..
48196              * @param {HtmlEditor} this
48197              */
48198             firstfocus: true,
48199             /**
48200              * @event autosave
48201              * Auto save the htmlEditor value as a file into Events
48202              * @param {HtmlEditor} this
48203              */
48204             autosave: true,
48205             /**
48206              * @event savedpreview
48207              * preview the saved version of htmlEditor
48208              * @param {HtmlEditor} this
48209              */
48210             savedpreview: true,
48211             
48212             /**
48213             * @event stylesheetsclick
48214             * Fires when press the Sytlesheets button
48215             * @param {Roo.HtmlEditorCore} this
48216             */
48217             stylesheetsclick: true,
48218             /**
48219             * @event paste
48220             * Fires when press user pastes into the editor
48221             * @param {Roo.HtmlEditorCore} this
48222             */
48223             paste: true 
48224         });
48225         this.defaultAutoCreate =  {
48226             tag: "textarea",
48227             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48228             autocomplete: "new-password"
48229         };
48230     },
48231
48232     /**
48233      * Protected method that will not generally be called directly. It
48234      * is called when the editor creates its toolbar. Override this method if you need to
48235      * add custom toolbar buttons.
48236      * @param {HtmlEditor} editor
48237      */
48238     createToolbar : function(editor){
48239         Roo.log("create toolbars");
48240         if (!editor.toolbars || !editor.toolbars.length) {
48241             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48242         }
48243         
48244         for (var i =0 ; i < editor.toolbars.length;i++) {
48245             editor.toolbars[i] = Roo.factory(
48246                     typeof(editor.toolbars[i]) == 'string' ?
48247                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48248                 Roo.form.HtmlEditor);
48249             editor.toolbars[i].init(editor);
48250         }
48251          
48252         
48253     },
48254
48255      
48256     // private
48257     onRender : function(ct, position)
48258     {
48259         var _t = this;
48260         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48261         
48262         this.wrap = this.el.wrap({
48263             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48264         });
48265         
48266         this.editorcore.onRender(ct, position);
48267          
48268         if (this.resizable) {
48269             this.resizeEl = new Roo.Resizable(this.wrap, {
48270                 pinned : true,
48271                 wrap: true,
48272                 dynamic : true,
48273                 minHeight : this.height,
48274                 height: this.height,
48275                 handles : this.resizable,
48276                 width: this.width,
48277                 listeners : {
48278                     resize : function(r, w, h) {
48279                         _t.onResize(w,h); // -something
48280                     }
48281                 }
48282             });
48283             
48284         }
48285         this.createToolbar(this);
48286        
48287         
48288         if(!this.width){
48289             this.setSize(this.wrap.getSize());
48290         }
48291         if (this.resizeEl) {
48292             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48293             // should trigger onReize..
48294         }
48295         
48296         this.keyNav = new Roo.KeyNav(this.el, {
48297             
48298             "tab" : function(e){
48299                 e.preventDefault();
48300                 
48301                 var value = this.getValue();
48302                 
48303                 var start = this.el.dom.selectionStart;
48304                 var end = this.el.dom.selectionEnd;
48305                 
48306                 if(!e.shiftKey){
48307                     
48308                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48309                     this.el.dom.setSelectionRange(end + 1, end + 1);
48310                     return;
48311                 }
48312                 
48313                 var f = value.substring(0, start).split("\t");
48314                 
48315                 if(f.pop().length != 0){
48316                     return;
48317                 }
48318                 
48319                 this.setValue(f.join("\t") + value.substring(end));
48320                 this.el.dom.setSelectionRange(start - 1, start - 1);
48321                 
48322             },
48323             
48324             "home" : function(e){
48325                 e.preventDefault();
48326                 
48327                 var curr = this.el.dom.selectionStart;
48328                 var lines = this.getValue().split("\n");
48329                 
48330                 if(!lines.length){
48331                     return;
48332                 }
48333                 
48334                 if(e.ctrlKey){
48335                     this.el.dom.setSelectionRange(0, 0);
48336                     return;
48337                 }
48338                 
48339                 var pos = 0;
48340                 
48341                 for (var i = 0; i < lines.length;i++) {
48342                     pos += lines[i].length;
48343                     
48344                     if(i != 0){
48345                         pos += 1;
48346                     }
48347                     
48348                     if(pos < curr){
48349                         continue;
48350                     }
48351                     
48352                     pos -= lines[i].length;
48353                     
48354                     break;
48355                 }
48356                 
48357                 if(!e.shiftKey){
48358                     this.el.dom.setSelectionRange(pos, pos);
48359                     return;
48360                 }
48361                 
48362                 this.el.dom.selectionStart = pos;
48363                 this.el.dom.selectionEnd = curr;
48364             },
48365             
48366             "end" : function(e){
48367                 e.preventDefault();
48368                 
48369                 var curr = this.el.dom.selectionStart;
48370                 var lines = this.getValue().split("\n");
48371                 
48372                 if(!lines.length){
48373                     return;
48374                 }
48375                 
48376                 if(e.ctrlKey){
48377                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48378                     return;
48379                 }
48380                 
48381                 var pos = 0;
48382                 
48383                 for (var i = 0; i < lines.length;i++) {
48384                     
48385                     pos += lines[i].length;
48386                     
48387                     if(i != 0){
48388                         pos += 1;
48389                     }
48390                     
48391                     if(pos < curr){
48392                         continue;
48393                     }
48394                     
48395                     break;
48396                 }
48397                 
48398                 if(!e.shiftKey){
48399                     this.el.dom.setSelectionRange(pos, pos);
48400                     return;
48401                 }
48402                 
48403                 this.el.dom.selectionStart = curr;
48404                 this.el.dom.selectionEnd = pos;
48405             },
48406
48407             scope : this,
48408
48409             doRelay : function(foo, bar, hname){
48410                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48411             },
48412
48413             forceKeyDown: true
48414         });
48415         
48416 //        if(this.autosave && this.w){
48417 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48418 //        }
48419     },
48420
48421     // private
48422     onResize : function(w, h)
48423     {
48424         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48425         var ew = false;
48426         var eh = false;
48427         
48428         if(this.el ){
48429             if(typeof w == 'number'){
48430                 var aw = w - this.wrap.getFrameWidth('lr');
48431                 this.el.setWidth(this.adjustWidth('textarea', aw));
48432                 ew = aw;
48433             }
48434             if(typeof h == 'number'){
48435                 var tbh = 0;
48436                 for (var i =0; i < this.toolbars.length;i++) {
48437                     // fixme - ask toolbars for heights?
48438                     tbh += this.toolbars[i].tb.el.getHeight();
48439                     if (this.toolbars[i].footer) {
48440                         tbh += this.toolbars[i].footer.el.getHeight();
48441                     }
48442                 }
48443                 
48444                 
48445                 
48446                 
48447                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48448                 ah -= 5; // knock a few pixes off for look..
48449 //                Roo.log(ah);
48450                 this.el.setHeight(this.adjustWidth('textarea', ah));
48451                 var eh = ah;
48452             }
48453         }
48454         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48455         this.editorcore.onResize(ew,eh);
48456         
48457     },
48458
48459     /**
48460      * Toggles the editor between standard and source edit mode.
48461      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48462      */
48463     toggleSourceEdit : function(sourceEditMode)
48464     {
48465         this.editorcore.toggleSourceEdit(sourceEditMode);
48466         
48467         if(this.editorcore.sourceEditMode){
48468             Roo.log('editor - showing textarea');
48469             
48470 //            Roo.log('in');
48471 //            Roo.log(this.syncValue());
48472             this.editorcore.syncValue();
48473             this.el.removeClass('x-hidden');
48474             this.el.dom.removeAttribute('tabIndex');
48475             this.el.focus();
48476             this.el.dom.scrollTop = 0;
48477             
48478             
48479             for (var i = 0; i < this.toolbars.length; i++) {
48480                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48481                     this.toolbars[i].tb.hide();
48482                     this.toolbars[i].footer.hide();
48483                 }
48484             }
48485             
48486         }else{
48487             Roo.log('editor - hiding textarea');
48488 //            Roo.log('out')
48489 //            Roo.log(this.pushValue()); 
48490             this.editorcore.pushValue();
48491             
48492             this.el.addClass('x-hidden');
48493             this.el.dom.setAttribute('tabIndex', -1);
48494             
48495             for (var i = 0; i < this.toolbars.length; i++) {
48496                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48497                     this.toolbars[i].tb.show();
48498                     this.toolbars[i].footer.show();
48499                 }
48500             }
48501             
48502             //this.deferFocus();
48503         }
48504         
48505         this.setSize(this.wrap.getSize());
48506         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48507         
48508         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48509     },
48510  
48511     // private (for BoxComponent)
48512     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48513
48514     // private (for BoxComponent)
48515     getResizeEl : function(){
48516         return this.wrap;
48517     },
48518
48519     // private (for BoxComponent)
48520     getPositionEl : function(){
48521         return this.wrap;
48522     },
48523
48524     // private
48525     initEvents : function(){
48526         this.originalValue = this.getValue();
48527     },
48528
48529     /**
48530      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48531      * @method
48532      */
48533     markInvalid : Roo.emptyFn,
48534     /**
48535      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48536      * @method
48537      */
48538     clearInvalid : Roo.emptyFn,
48539
48540     setValue : function(v){
48541         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48542         this.editorcore.pushValue();
48543     },
48544
48545      
48546     // private
48547     deferFocus : function(){
48548         this.focus.defer(10, this);
48549     },
48550
48551     // doc'ed in Field
48552     focus : function(){
48553         this.editorcore.focus();
48554         
48555     },
48556       
48557
48558     // private
48559     onDestroy : function(){
48560         
48561         
48562         
48563         if(this.rendered){
48564             
48565             for (var i =0; i < this.toolbars.length;i++) {
48566                 // fixme - ask toolbars for heights?
48567                 this.toolbars[i].onDestroy();
48568             }
48569             
48570             this.wrap.dom.innerHTML = '';
48571             this.wrap.remove();
48572         }
48573     },
48574
48575     // private
48576     onFirstFocus : function(){
48577         //Roo.log("onFirstFocus");
48578         this.editorcore.onFirstFocus();
48579          for (var i =0; i < this.toolbars.length;i++) {
48580             this.toolbars[i].onFirstFocus();
48581         }
48582         
48583     },
48584     
48585     // private
48586     syncValue : function()
48587     {
48588         this.editorcore.syncValue();
48589     },
48590     
48591     pushValue : function()
48592     {
48593         this.editorcore.pushValue();
48594     },
48595     
48596     setStylesheets : function(stylesheets)
48597     {
48598         this.editorcore.setStylesheets(stylesheets);
48599     },
48600     
48601     removeStylesheets : function()
48602     {
48603         this.editorcore.removeStylesheets();
48604     }
48605      
48606     
48607     // hide stuff that is not compatible
48608     /**
48609      * @event blur
48610      * @hide
48611      */
48612     /**
48613      * @event change
48614      * @hide
48615      */
48616     /**
48617      * @event focus
48618      * @hide
48619      */
48620     /**
48621      * @event specialkey
48622      * @hide
48623      */
48624     /**
48625      * @cfg {String} fieldClass @hide
48626      */
48627     /**
48628      * @cfg {String} focusClass @hide
48629      */
48630     /**
48631      * @cfg {String} autoCreate @hide
48632      */
48633     /**
48634      * @cfg {String} inputType @hide
48635      */
48636     /**
48637      * @cfg {String} invalidClass @hide
48638      */
48639     /**
48640      * @cfg {String} invalidText @hide
48641      */
48642     /**
48643      * @cfg {String} msgFx @hide
48644      */
48645     /**
48646      * @cfg {String} validateOnBlur @hide
48647      */
48648 });
48649  
48650     // <script type="text/javascript">
48651 /*
48652  * Based on
48653  * Ext JS Library 1.1.1
48654  * Copyright(c) 2006-2007, Ext JS, LLC.
48655  *  
48656  
48657  */
48658
48659 /**
48660  * @class Roo.form.HtmlEditorToolbar1
48661  * Basic Toolbar
48662  * 
48663  * Usage:
48664  *
48665  new Roo.form.HtmlEditor({
48666     ....
48667     toolbars : [
48668         new Roo.form.HtmlEditorToolbar1({
48669             disable : { fonts: 1 , format: 1, ..., ... , ...],
48670             btns : [ .... ]
48671         })
48672     }
48673      
48674  * 
48675  * @cfg {Object} disable List of elements to disable..
48676  * @cfg {Array} btns List of additional buttons.
48677  * 
48678  * 
48679  * NEEDS Extra CSS? 
48680  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48681  */
48682  
48683 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48684 {
48685     
48686     Roo.apply(this, config);
48687     
48688     // default disabled, based on 'good practice'..
48689     this.disable = this.disable || {};
48690     Roo.applyIf(this.disable, {
48691         fontSize : true,
48692         colors : true,
48693         specialElements : true
48694     });
48695     
48696     
48697     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48698     // dont call parent... till later.
48699 }
48700
48701 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48702     
48703     tb: false,
48704     
48705     rendered: false,
48706     
48707     editor : false,
48708     editorcore : false,
48709     /**
48710      * @cfg {Object} disable  List of toolbar elements to disable
48711          
48712      */
48713     disable : false,
48714     
48715     
48716      /**
48717      * @cfg {String} createLinkText The default text for the create link prompt
48718      */
48719     createLinkText : 'Please enter the URL for the link:',
48720     /**
48721      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48722      */
48723     defaultLinkValue : 'http:/'+'/',
48724    
48725     
48726       /**
48727      * @cfg {Array} fontFamilies An array of available font families
48728      */
48729     fontFamilies : [
48730         'Arial',
48731         'Courier New',
48732         'Tahoma',
48733         'Times New Roman',
48734         'Verdana'
48735     ],
48736     
48737     specialChars : [
48738            "&#169;",
48739           "&#174;",     
48740           "&#8482;",    
48741           "&#163;" ,    
48742          // "&#8212;",    
48743           "&#8230;",    
48744           "&#247;" ,    
48745         //  "&#225;" ,     ?? a acute?
48746            "&#8364;"    , //Euro
48747        //   "&#8220;"    ,
48748         //  "&#8221;"    ,
48749         //  "&#8226;"    ,
48750           "&#176;"  //   , // degrees
48751
48752          // "&#233;"     , // e ecute
48753          // "&#250;"     , // u ecute?
48754     ],
48755     
48756     specialElements : [
48757         {
48758             text: "Insert Table",
48759             xtype: 'MenuItem',
48760             xns : Roo.Menu,
48761             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48762                 
48763         },
48764         {    
48765             text: "Insert Image",
48766             xtype: 'MenuItem',
48767             xns : Roo.Menu,
48768             ihtml : '<img src="about:blank"/>'
48769             
48770         }
48771         
48772          
48773     ],
48774     
48775     
48776     inputElements : [ 
48777             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48778             "input:submit", "input:button", "select", "textarea", "label" ],
48779     formats : [
48780         ["p"] ,  
48781         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48782         ["pre"],[ "code"], 
48783         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48784         ['div'],['span'],
48785         ['sup'],['sub']
48786     ],
48787     
48788     cleanStyles : [
48789         "font-size"
48790     ],
48791      /**
48792      * @cfg {String} defaultFont default font to use.
48793      */
48794     defaultFont: 'tahoma',
48795    
48796     fontSelect : false,
48797     
48798     
48799     formatCombo : false,
48800     
48801     init : function(editor)
48802     {
48803         this.editor = editor;
48804         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48805         var editorcore = this.editorcore;
48806         
48807         var _t = this;
48808         
48809         var fid = editorcore.frameId;
48810         var etb = this;
48811         function btn(id, toggle, handler){
48812             var xid = fid + '-'+ id ;
48813             return {
48814                 id : xid,
48815                 cmd : id,
48816                 cls : 'x-btn-icon x-edit-'+id,
48817                 enableToggle:toggle !== false,
48818                 scope: _t, // was editor...
48819                 handler:handler||_t.relayBtnCmd,
48820                 clickEvent:'mousedown',
48821                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48822                 tabIndex:-1
48823             };
48824         }
48825         
48826         
48827         
48828         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48829         this.tb = tb;
48830          // stop form submits
48831         tb.el.on('click', function(e){
48832             e.preventDefault(); // what does this do?
48833         });
48834
48835         if(!this.disable.font) { // && !Roo.isSafari){
48836             /* why no safari for fonts 
48837             editor.fontSelect = tb.el.createChild({
48838                 tag:'select',
48839                 tabIndex: -1,
48840                 cls:'x-font-select',
48841                 html: this.createFontOptions()
48842             });
48843             
48844             editor.fontSelect.on('change', function(){
48845                 var font = editor.fontSelect.dom.value;
48846                 editor.relayCmd('fontname', font);
48847                 editor.deferFocus();
48848             }, editor);
48849             
48850             tb.add(
48851                 editor.fontSelect.dom,
48852                 '-'
48853             );
48854             */
48855             
48856         };
48857         if(!this.disable.formats){
48858             this.formatCombo = new Roo.form.ComboBox({
48859                 store: new Roo.data.SimpleStore({
48860                     id : 'tag',
48861                     fields: ['tag'],
48862                     data : this.formats // from states.js
48863                 }),
48864                 blockFocus : true,
48865                 name : '',
48866                 //autoCreate : {tag: "div",  size: "20"},
48867                 displayField:'tag',
48868                 typeAhead: false,
48869                 mode: 'local',
48870                 editable : false,
48871                 triggerAction: 'all',
48872                 emptyText:'Add tag',
48873                 selectOnFocus:true,
48874                 width:135,
48875                 listeners : {
48876                     'select': function(c, r, i) {
48877                         editorcore.insertTag(r.get('tag'));
48878                         editor.focus();
48879                     }
48880                 }
48881
48882             });
48883             tb.addField(this.formatCombo);
48884             
48885         }
48886         
48887         if(!this.disable.format){
48888             tb.add(
48889                 btn('bold'),
48890                 btn('italic'),
48891                 btn('underline'),
48892                 btn('strikethrough')
48893             );
48894         };
48895         if(!this.disable.fontSize){
48896             tb.add(
48897                 '-',
48898                 
48899                 
48900                 btn('increasefontsize', false, editorcore.adjustFont),
48901                 btn('decreasefontsize', false, editorcore.adjustFont)
48902             );
48903         };
48904         
48905         
48906         if(!this.disable.colors){
48907             tb.add(
48908                 '-', {
48909                     id:editorcore.frameId +'-forecolor',
48910                     cls:'x-btn-icon x-edit-forecolor',
48911                     clickEvent:'mousedown',
48912                     tooltip: this.buttonTips['forecolor'] || undefined,
48913                     tabIndex:-1,
48914                     menu : new Roo.menu.ColorMenu({
48915                         allowReselect: true,
48916                         focus: Roo.emptyFn,
48917                         value:'000000',
48918                         plain:true,
48919                         selectHandler: function(cp, color){
48920                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48921                             editor.deferFocus();
48922                         },
48923                         scope: editorcore,
48924                         clickEvent:'mousedown'
48925                     })
48926                 }, {
48927                     id:editorcore.frameId +'backcolor',
48928                     cls:'x-btn-icon x-edit-backcolor',
48929                     clickEvent:'mousedown',
48930                     tooltip: this.buttonTips['backcolor'] || undefined,
48931                     tabIndex:-1,
48932                     menu : new Roo.menu.ColorMenu({
48933                         focus: Roo.emptyFn,
48934                         value:'FFFFFF',
48935                         plain:true,
48936                         allowReselect: true,
48937                         selectHandler: function(cp, color){
48938                             if(Roo.isGecko){
48939                                 editorcore.execCmd('useCSS', false);
48940                                 editorcore.execCmd('hilitecolor', color);
48941                                 editorcore.execCmd('useCSS', true);
48942                                 editor.deferFocus();
48943                             }else{
48944                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48945                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48946                                 editor.deferFocus();
48947                             }
48948                         },
48949                         scope:editorcore,
48950                         clickEvent:'mousedown'
48951                     })
48952                 }
48953             );
48954         };
48955         // now add all the items...
48956         
48957
48958         if(!this.disable.alignments){
48959             tb.add(
48960                 '-',
48961                 btn('justifyleft'),
48962                 btn('justifycenter'),
48963                 btn('justifyright')
48964             );
48965         };
48966
48967         //if(!Roo.isSafari){
48968             if(!this.disable.links){
48969                 tb.add(
48970                     '-',
48971                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48972                 );
48973             };
48974
48975             if(!this.disable.lists){
48976                 tb.add(
48977                     '-',
48978                     btn('insertorderedlist'),
48979                     btn('insertunorderedlist')
48980                 );
48981             }
48982             if(!this.disable.sourceEdit){
48983                 tb.add(
48984                     '-',
48985                     btn('sourceedit', true, function(btn){
48986                         this.toggleSourceEdit(btn.pressed);
48987                     })
48988                 );
48989             }
48990         //}
48991         
48992         var smenu = { };
48993         // special menu.. - needs to be tidied up..
48994         if (!this.disable.special) {
48995             smenu = {
48996                 text: "&#169;",
48997                 cls: 'x-edit-none',
48998                 
48999                 menu : {
49000                     items : []
49001                 }
49002             };
49003             for (var i =0; i < this.specialChars.length; i++) {
49004                 smenu.menu.items.push({
49005                     
49006                     html: this.specialChars[i],
49007                     handler: function(a,b) {
49008                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49009                         //editor.insertAtCursor(a.html);
49010                         
49011                     },
49012                     tabIndex:-1
49013                 });
49014             }
49015             
49016             
49017             tb.add(smenu);
49018             
49019             
49020         }
49021         
49022         var cmenu = { };
49023         if (!this.disable.cleanStyles) {
49024             cmenu = {
49025                 cls: 'x-btn-icon x-btn-clear',
49026                 
49027                 menu : {
49028                     items : []
49029                 }
49030             };
49031             for (var i =0; i < this.cleanStyles.length; i++) {
49032                 cmenu.menu.items.push({
49033                     actiontype : this.cleanStyles[i],
49034                     html: 'Remove ' + this.cleanStyles[i],
49035                     handler: function(a,b) {
49036 //                        Roo.log(a);
49037 //                        Roo.log(b);
49038                         var c = Roo.get(editorcore.doc.body);
49039                         c.select('[style]').each(function(s) {
49040                             s.dom.style.removeProperty(a.actiontype);
49041                         });
49042                         editorcore.syncValue();
49043                     },
49044                     tabIndex:-1
49045                 });
49046             }
49047             cmenu.menu.items.push({
49048                 actiontype : 'tablewidths',
49049                 html: 'Remove Table Widths',
49050                 handler: function(a,b) {
49051                     editorcore.cleanTableWidths();
49052                     editorcore.syncValue();
49053                 },
49054                 tabIndex:-1
49055             });
49056             cmenu.menu.items.push({
49057                 actiontype : 'word',
49058                 html: 'Remove MS Word Formating',
49059                 handler: function(a,b) {
49060                     editorcore.cleanWord();
49061                     editorcore.syncValue();
49062                 },
49063                 tabIndex:-1
49064             });
49065             
49066             cmenu.menu.items.push({
49067                 actiontype : 'all',
49068                 html: 'Remove All Styles',
49069                 handler: function(a,b) {
49070                     
49071                     var c = Roo.get(editorcore.doc.body);
49072                     c.select('[style]').each(function(s) {
49073                         s.dom.removeAttribute('style');
49074                     });
49075                     editorcore.syncValue();
49076                 },
49077                 tabIndex:-1
49078             });
49079             
49080             cmenu.menu.items.push({
49081                 actiontype : 'all',
49082                 html: 'Remove All CSS Classes',
49083                 handler: function(a,b) {
49084                     
49085                     var c = Roo.get(editorcore.doc.body);
49086                     c.select('[class]').each(function(s) {
49087                         s.dom.removeAttribute('class');
49088                     });
49089                     editorcore.cleanWord();
49090                     editorcore.syncValue();
49091                 },
49092                 tabIndex:-1
49093             });
49094             
49095              cmenu.menu.items.push({
49096                 actiontype : 'tidy',
49097                 html: 'Tidy HTML Source',
49098                 handler: function(a,b) {
49099                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49100                     editorcore.syncValue();
49101                 },
49102                 tabIndex:-1
49103             });
49104             
49105             
49106             tb.add(cmenu);
49107         }
49108          
49109         if (!this.disable.specialElements) {
49110             var semenu = {
49111                 text: "Other;",
49112                 cls: 'x-edit-none',
49113                 menu : {
49114                     items : []
49115                 }
49116             };
49117             for (var i =0; i < this.specialElements.length; i++) {
49118                 semenu.menu.items.push(
49119                     Roo.apply({ 
49120                         handler: function(a,b) {
49121                             editor.insertAtCursor(this.ihtml);
49122                         }
49123                     }, this.specialElements[i])
49124                 );
49125                     
49126             }
49127             
49128             tb.add(semenu);
49129             
49130             
49131         }
49132          
49133         
49134         if (this.btns) {
49135             for(var i =0; i< this.btns.length;i++) {
49136                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49137                 b.cls =  'x-edit-none';
49138                 
49139                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49140                     b.cls += ' x-init-enable';
49141                 }
49142                 
49143                 b.scope = editorcore;
49144                 tb.add(b);
49145             }
49146         
49147         }
49148         
49149         
49150         
49151         // disable everything...
49152         
49153         this.tb.items.each(function(item){
49154             
49155            if(
49156                 item.id != editorcore.frameId+ '-sourceedit' && 
49157                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49158             ){
49159                 
49160                 item.disable();
49161             }
49162         });
49163         this.rendered = true;
49164         
49165         // the all the btns;
49166         editor.on('editorevent', this.updateToolbar, this);
49167         // other toolbars need to implement this..
49168         //editor.on('editmodechange', this.updateToolbar, this);
49169     },
49170     
49171     
49172     relayBtnCmd : function(btn) {
49173         this.editorcore.relayCmd(btn.cmd);
49174     },
49175     // private used internally
49176     createLink : function(){
49177         Roo.log("create link?");
49178         var url = prompt(this.createLinkText, this.defaultLinkValue);
49179         if(url && url != 'http:/'+'/'){
49180             this.editorcore.relayCmd('createlink', url);
49181         }
49182     },
49183
49184     
49185     /**
49186      * Protected method that will not generally be called directly. It triggers
49187      * a toolbar update by reading the markup state of the current selection in the editor.
49188      */
49189     updateToolbar: function(){
49190
49191         if(!this.editorcore.activated){
49192             this.editor.onFirstFocus();
49193             return;
49194         }
49195
49196         var btns = this.tb.items.map, 
49197             doc = this.editorcore.doc,
49198             frameId = this.editorcore.frameId;
49199
49200         if(!this.disable.font && !Roo.isSafari){
49201             /*
49202             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49203             if(name != this.fontSelect.dom.value){
49204                 this.fontSelect.dom.value = name;
49205             }
49206             */
49207         }
49208         if(!this.disable.format){
49209             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49210             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49211             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49212             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49213         }
49214         if(!this.disable.alignments){
49215             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49216             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49217             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49218         }
49219         if(!Roo.isSafari && !this.disable.lists){
49220             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49221             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49222         }
49223         
49224         var ans = this.editorcore.getAllAncestors();
49225         if (this.formatCombo) {
49226             
49227             
49228             var store = this.formatCombo.store;
49229             this.formatCombo.setValue("");
49230             for (var i =0; i < ans.length;i++) {
49231                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49232                     // select it..
49233                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49234                     break;
49235                 }
49236             }
49237         }
49238         
49239         
49240         
49241         // hides menus... - so this cant be on a menu...
49242         Roo.menu.MenuMgr.hideAll();
49243
49244         //this.editorsyncValue();
49245     },
49246    
49247     
49248     createFontOptions : function(){
49249         var buf = [], fs = this.fontFamilies, ff, lc;
49250         
49251         
49252         
49253         for(var i = 0, len = fs.length; i< len; i++){
49254             ff = fs[i];
49255             lc = ff.toLowerCase();
49256             buf.push(
49257                 '<option value="',lc,'" style="font-family:',ff,';"',
49258                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49259                     ff,
49260                 '</option>'
49261             );
49262         }
49263         return buf.join('');
49264     },
49265     
49266     toggleSourceEdit : function(sourceEditMode){
49267         
49268         Roo.log("toolbar toogle");
49269         if(sourceEditMode === undefined){
49270             sourceEditMode = !this.sourceEditMode;
49271         }
49272         this.sourceEditMode = sourceEditMode === true;
49273         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49274         // just toggle the button?
49275         if(btn.pressed !== this.sourceEditMode){
49276             btn.toggle(this.sourceEditMode);
49277             return;
49278         }
49279         
49280         if(sourceEditMode){
49281             Roo.log("disabling buttons");
49282             this.tb.items.each(function(item){
49283                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49284                     item.disable();
49285                 }
49286             });
49287           
49288         }else{
49289             Roo.log("enabling buttons");
49290             if(this.editorcore.initialized){
49291                 this.tb.items.each(function(item){
49292                     item.enable();
49293                 });
49294             }
49295             
49296         }
49297         Roo.log("calling toggole on editor");
49298         // tell the editor that it's been pressed..
49299         this.editor.toggleSourceEdit(sourceEditMode);
49300        
49301     },
49302      /**
49303      * Object collection of toolbar tooltips for the buttons in the editor. The key
49304      * is the command id associated with that button and the value is a valid QuickTips object.
49305      * For example:
49306 <pre><code>
49307 {
49308     bold : {
49309         title: 'Bold (Ctrl+B)',
49310         text: 'Make the selected text bold.',
49311         cls: 'x-html-editor-tip'
49312     },
49313     italic : {
49314         title: 'Italic (Ctrl+I)',
49315         text: 'Make the selected text italic.',
49316         cls: 'x-html-editor-tip'
49317     },
49318     ...
49319 </code></pre>
49320     * @type Object
49321      */
49322     buttonTips : {
49323         bold : {
49324             title: 'Bold (Ctrl+B)',
49325             text: 'Make the selected text bold.',
49326             cls: 'x-html-editor-tip'
49327         },
49328         italic : {
49329             title: 'Italic (Ctrl+I)',
49330             text: 'Make the selected text italic.',
49331             cls: 'x-html-editor-tip'
49332         },
49333         underline : {
49334             title: 'Underline (Ctrl+U)',
49335             text: 'Underline the selected text.',
49336             cls: 'x-html-editor-tip'
49337         },
49338         strikethrough : {
49339             title: 'Strikethrough',
49340             text: 'Strikethrough the selected text.',
49341             cls: 'x-html-editor-tip'
49342         },
49343         increasefontsize : {
49344             title: 'Grow Text',
49345             text: 'Increase the font size.',
49346             cls: 'x-html-editor-tip'
49347         },
49348         decreasefontsize : {
49349             title: 'Shrink Text',
49350             text: 'Decrease the font size.',
49351             cls: 'x-html-editor-tip'
49352         },
49353         backcolor : {
49354             title: 'Text Highlight Color',
49355             text: 'Change the background color of the selected text.',
49356             cls: 'x-html-editor-tip'
49357         },
49358         forecolor : {
49359             title: 'Font Color',
49360             text: 'Change the color of the selected text.',
49361             cls: 'x-html-editor-tip'
49362         },
49363         justifyleft : {
49364             title: 'Align Text Left',
49365             text: 'Align text to the left.',
49366             cls: 'x-html-editor-tip'
49367         },
49368         justifycenter : {
49369             title: 'Center Text',
49370             text: 'Center text in the editor.',
49371             cls: 'x-html-editor-tip'
49372         },
49373         justifyright : {
49374             title: 'Align Text Right',
49375             text: 'Align text to the right.',
49376             cls: 'x-html-editor-tip'
49377         },
49378         insertunorderedlist : {
49379             title: 'Bullet List',
49380             text: 'Start a bulleted list.',
49381             cls: 'x-html-editor-tip'
49382         },
49383         insertorderedlist : {
49384             title: 'Numbered List',
49385             text: 'Start a numbered list.',
49386             cls: 'x-html-editor-tip'
49387         },
49388         createlink : {
49389             title: 'Hyperlink',
49390             text: 'Make the selected text a hyperlink.',
49391             cls: 'x-html-editor-tip'
49392         },
49393         sourceedit : {
49394             title: 'Source Edit',
49395             text: 'Switch to source editing mode.',
49396             cls: 'x-html-editor-tip'
49397         }
49398     },
49399     // private
49400     onDestroy : function(){
49401         if(this.rendered){
49402             
49403             this.tb.items.each(function(item){
49404                 if(item.menu){
49405                     item.menu.removeAll();
49406                     if(item.menu.el){
49407                         item.menu.el.destroy();
49408                     }
49409                 }
49410                 item.destroy();
49411             });
49412              
49413         }
49414     },
49415     onFirstFocus: function() {
49416         this.tb.items.each(function(item){
49417            item.enable();
49418         });
49419     }
49420 });
49421
49422
49423
49424
49425 // <script type="text/javascript">
49426 /*
49427  * Based on
49428  * Ext JS Library 1.1.1
49429  * Copyright(c) 2006-2007, Ext JS, LLC.
49430  *  
49431  
49432  */
49433
49434  
49435 /**
49436  * @class Roo.form.HtmlEditor.ToolbarContext
49437  * Context Toolbar
49438  * 
49439  * Usage:
49440  *
49441  new Roo.form.HtmlEditor({
49442     ....
49443     toolbars : [
49444         { xtype: 'ToolbarStandard', styles : {} }
49445         { xtype: 'ToolbarContext', disable : {} }
49446     ]
49447 })
49448
49449      
49450  * 
49451  * @config : {Object} disable List of elements to disable.. (not done yet.)
49452  * @config : {Object} styles  Map of styles available.
49453  * 
49454  */
49455
49456 Roo.form.HtmlEditor.ToolbarContext = function(config)
49457 {
49458     
49459     Roo.apply(this, config);
49460     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49461     // dont call parent... till later.
49462     this.styles = this.styles || {};
49463 }
49464
49465  
49466
49467 Roo.form.HtmlEditor.ToolbarContext.types = {
49468     'IMG' : [
49469         {
49470             name : 'width',
49471             title: "Width",
49472             width: 40
49473         },
49474         {
49475             name : 'height',
49476             title: "Height",
49477             width: 40
49478         },
49479         {
49480             name : 'align',
49481             title: "Align",
49482             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49483             width : 80
49484             
49485         },
49486         {
49487             name : 'border',
49488             title: "Border",
49489             width: 40
49490         },
49491         {
49492             name : 'alt',
49493             title: "Alt",
49494             width: 120
49495         },
49496         {
49497             name : 'src',
49498             title: "Src",
49499             width: 220
49500         }
49501         
49502     ],
49503     
49504     'FIGURE' : [
49505         {
49506             name : 'align',
49507             title: "Align",
49508             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49509             width : 80  
49510         }
49511     ],
49512     'A' : [
49513         {
49514             name : 'name',
49515             title: "Name",
49516             width: 50
49517         },
49518         {
49519             name : 'target',
49520             title: "Target",
49521             width: 120
49522         },
49523         {
49524             name : 'href',
49525             title: "Href",
49526             width: 220
49527         } // border?
49528         
49529     ],
49530     
49531     'INPUT' : [
49532         {
49533             name : 'name',
49534             title: "name",
49535             width: 120
49536         },
49537         {
49538             name : 'value',
49539             title: "Value",
49540             width: 120
49541         },
49542         {
49543             name : 'width',
49544             title: "Width",
49545             width: 40
49546         }
49547     ],
49548     'LABEL' : [
49549          {
49550             name : 'for',
49551             title: "For",
49552             width: 120
49553         }
49554     ],
49555     'TEXTAREA' : [
49556         {
49557             name : 'name',
49558             title: "name",
49559             width: 120
49560         },
49561         {
49562             name : 'rows',
49563             title: "Rows",
49564             width: 20
49565         },
49566         {
49567             name : 'cols',
49568             title: "Cols",
49569             width: 20
49570         }
49571     ],
49572     'SELECT' : [
49573         {
49574             name : 'name',
49575             title: "name",
49576             width: 120
49577         },
49578         {
49579             name : 'selectoptions',
49580             title: "Options",
49581             width: 200
49582         }
49583     ],
49584     
49585     // should we really allow this??
49586     // should this just be 
49587     'BODY' : [
49588         
49589         {
49590             name : 'title',
49591             title: "Title",
49592             width: 200,
49593             disabled : true
49594         }
49595     ],
49596  
49597     '*' : [
49598         // empty.
49599     ]
49600
49601 };
49602
49603 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49604 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49605
49606 Roo.form.HtmlEditor.ToolbarContext.options = {
49607         'font-family'  : [ 
49608                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49609                 [ 'Courier New', 'Courier New'],
49610                 [ 'Tahoma', 'Tahoma'],
49611                 [ 'Times New Roman,serif', 'Times'],
49612                 [ 'Verdana','Verdana' ]
49613         ]
49614 };
49615
49616 // fixme - these need to be configurable..
49617  
49618
49619 //Roo.form.HtmlEditor.ToolbarContext.types
49620
49621
49622 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49623     
49624     tb: false,
49625     
49626     rendered: false,
49627     
49628     editor : false,
49629     editorcore : false,
49630     /**
49631      * @cfg {Object} disable  List of toolbar elements to disable
49632          
49633      */
49634     disable : false,
49635     /**
49636      * @cfg {Object} styles List of styles 
49637      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49638      *
49639      * These must be defined in the page, so they get rendered correctly..
49640      * .headline { }
49641      * TD.underline { }
49642      * 
49643      */
49644     styles : false,
49645     
49646     options: false,
49647     
49648     toolbars : false,
49649     
49650     init : function(editor)
49651     {
49652         this.editor = editor;
49653         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49654         var editorcore = this.editorcore;
49655         
49656         var fid = editorcore.frameId;
49657         var etb = this;
49658         function btn(id, toggle, handler){
49659             var xid = fid + '-'+ id ;
49660             return {
49661                 id : xid,
49662                 cmd : id,
49663                 cls : 'x-btn-icon x-edit-'+id,
49664                 enableToggle:toggle !== false,
49665                 scope: editorcore, // was editor...
49666                 handler:handler||editorcore.relayBtnCmd,
49667                 clickEvent:'mousedown',
49668                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49669                 tabIndex:-1
49670             };
49671         }
49672         // create a new element.
49673         var wdiv = editor.wrap.createChild({
49674                 tag: 'div'
49675             }, editor.wrap.dom.firstChild.nextSibling, true);
49676         
49677         // can we do this more than once??
49678         
49679          // stop form submits
49680       
49681  
49682         // disable everything...
49683         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49684         this.toolbars = {};
49685            
49686         for (var i in  ty) {
49687           
49688             this.toolbars[i] = this.buildToolbar(ty[i],i);
49689         }
49690         this.tb = this.toolbars.BODY;
49691         this.tb.el.show();
49692         this.buildFooter();
49693         this.footer.show();
49694         editor.on('hide', function( ) { this.footer.hide() }, this);
49695         editor.on('show', function( ) { this.footer.show() }, this);
49696         
49697          
49698         this.rendered = true;
49699         
49700         // the all the btns;
49701         editor.on('editorevent', this.updateToolbar, this);
49702         // other toolbars need to implement this..
49703         //editor.on('editmodechange', this.updateToolbar, this);
49704     },
49705     
49706     
49707     
49708     /**
49709      * Protected method that will not generally be called directly. It triggers
49710      * a toolbar update by reading the markup state of the current selection in the editor.
49711      *
49712      * Note you can force an update by calling on('editorevent', scope, false)
49713      */
49714     updateToolbar: function(editor ,ev, sel)
49715     {
49716         
49717         if (ev) {
49718             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49719         }
49720         
49721         //Roo.log(ev);
49722         // capture mouse up - this is handy for selecting images..
49723         // perhaps should go somewhere else...
49724         if(!this.editorcore.activated){
49725              this.editor.onFirstFocus();
49726             return;
49727         }
49728         //Roo.log(ev ? ev.target : 'NOTARGET');
49729         
49730         
49731         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49732         // selectNode - might want to handle IE?
49733         
49734         
49735         
49736         if (ev &&
49737             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49738             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49739             // they have click on an image...
49740             // let's see if we can change the selection...
49741             sel = ev.target;
49742             
49743             // this triggers looping?
49744             //this.editorcore.selectNode(sel);
49745              
49746         }  
49747         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49748         //Roo.get(node).addClass('roo-ed-selection');
49749       
49750         //var updateFooter = sel ? false : true; 
49751         
49752         
49753         var ans = this.editorcore.getAllAncestors();
49754         
49755         // pick
49756         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49757         
49758         if (!sel) { 
49759             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49760             sel = sel ? sel : this.editorcore.doc.body;
49761             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49762             
49763         }
49764         
49765         var tn = sel.tagName.toUpperCase();
49766         var lastSel = this.tb.selectedNode;
49767         this.tb.selectedNode = sel;
49768         var left_label = tn;
49769         
49770         // ok see if we are editing a block?
49771         
49772         var db = false;
49773         // you are not actually selecting the block.
49774         if (sel && sel.hasAttribute('data-block')) {
49775             db = sel;
49776         } else if (sel && !sel.hasAttribute('contenteditable')) {
49777             var sel_el = Roo.get(sel);
49778             db = sel_el.findParent('[data-block]');
49779             var cepar = sel_el.findParent('[contenteditable=true]');
49780             if (db && cepar && cepar.tagName != 'BODY') {
49781                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49782             }   
49783         }
49784         
49785         
49786         var block = false;
49787         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49788         if (db) {
49789             block = Roo.htmleditor.Block.factory(db);
49790             
49791             
49792             if (block) {
49793                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49794                 tn = 'BLOCK.' + db.getAttribute('data-block');
49795                 
49796                 //this.editorcore.selectNode(db);
49797                 if (typeof(this.toolbars[tn]) == 'undefined') {
49798                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49799                 }
49800                 this.toolbars[tn].selectedNode = db;
49801                 left_label = block.friendly_name;
49802                 ans = this.editorcore.getAllAncestors();
49803             }
49804             
49805                 
49806             
49807         }
49808         
49809         
49810         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49811             return; // no change?
49812         }
49813         
49814         
49815           
49816         this.tb.el.hide();
49817         ///console.log("show: " + tn);
49818         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49819         
49820         this.tb.el.show();
49821         // update name
49822         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49823         
49824         
49825         // update attributes
49826         if (block && this.tb.fields) {
49827              
49828             this.tb.fields.each(function(e) {
49829                 e.setValue(block[e.name]);
49830             });
49831             
49832             
49833         } else  if (this.tb.fields && this.tb.selectedNode) {
49834             this.tb.fields.each( function(e) {
49835                 if (e.stylename) {
49836                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49837                     return;
49838                 } 
49839                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49840             }, this);
49841             this.updateToolbarStyles(this.tb.selectedNode);  
49842         }
49843         
49844         
49845        
49846         Roo.menu.MenuMgr.hideAll();
49847
49848         
49849         
49850     
49851         // update the footer
49852         //
49853         this.updateFooter(ans);
49854              
49855     },
49856     
49857     updateToolbarStyles : function(sel)
49858     {
49859         var hasStyles = false;
49860         for(var i in this.styles) {
49861             hasStyles = true;
49862             break;
49863         }
49864         
49865         // update styles
49866         if (hasStyles && this.tb.hasStyles) { 
49867             var st = this.tb.fields.item(0);
49868             
49869             st.store.removeAll();
49870             var cn = sel.className.split(/\s+/);
49871             
49872             var avs = [];
49873             if (this.styles['*']) {
49874                 
49875                 Roo.each(this.styles['*'], function(v) {
49876                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49877                 });
49878             }
49879             if (this.styles[tn]) { 
49880                 Roo.each(this.styles[tn], function(v) {
49881                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49882                 });
49883             }
49884             
49885             st.store.loadData(avs);
49886             st.collapse();
49887             st.setValue(cn);
49888         }
49889     },
49890     
49891      
49892     updateFooter : function(ans)
49893     {
49894         var html = '';
49895         if (ans === false) {
49896             this.footDisp.dom.innerHTML = '';
49897             return;
49898         }
49899         
49900         this.footerEls = ans.reverse();
49901         Roo.each(this.footerEls, function(a,i) {
49902             if (!a) { return; }
49903             html += html.length ? ' &gt; '  :  '';
49904             
49905             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49906             
49907         });
49908        
49909         // 
49910         var sz = this.footDisp.up('td').getSize();
49911         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49912         this.footDisp.dom.style.marginLeft = '5px';
49913         
49914         this.footDisp.dom.style.overflow = 'hidden';
49915         
49916         this.footDisp.dom.innerHTML = html;
49917             
49918         
49919     },
49920    
49921        
49922     // private
49923     onDestroy : function(){
49924         if(this.rendered){
49925             
49926             this.tb.items.each(function(item){
49927                 if(item.menu){
49928                     item.menu.removeAll();
49929                     if(item.menu.el){
49930                         item.menu.el.destroy();
49931                     }
49932                 }
49933                 item.destroy();
49934             });
49935              
49936         }
49937     },
49938     onFirstFocus: function() {
49939         // need to do this for all the toolbars..
49940         this.tb.items.each(function(item){
49941            item.enable();
49942         });
49943     },
49944     buildToolbar: function(tlist, nm, friendly_name, block)
49945     {
49946         var editor = this.editor;
49947         var editorcore = this.editorcore;
49948          // create a new element.
49949         var wdiv = editor.wrap.createChild({
49950                 tag: 'div'
49951             }, editor.wrap.dom.firstChild.nextSibling, true);
49952         
49953        
49954         var tb = new Roo.Toolbar(wdiv);
49955         ///this.tb = tb; // << this sets the active toolbar..
49956         if (tlist === false && block) {
49957             tlist = block.contextMenu(this);
49958         }
49959         
49960         tb.hasStyles = false;
49961         tb.name = nm;
49962         
49963         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49964         
49965         var styles = Array.from(this.styles);
49966         
49967         
49968         // styles...
49969         if (styles && styles.length) {
49970             tb.hasStyles = true;
49971             // this needs a multi-select checkbox...
49972             tb.addField( new Roo.form.ComboBox({
49973                 store: new Roo.data.SimpleStore({
49974                     id : 'val',
49975                     fields: ['val', 'selected'],
49976                     data : [] 
49977                 }),
49978                 name : '-roo-edit-className',
49979                 attrname : 'className',
49980                 displayField: 'val',
49981                 typeAhead: false,
49982                 mode: 'local',
49983                 editable : false,
49984                 triggerAction: 'all',
49985                 emptyText:'Select Style',
49986                 selectOnFocus:true,
49987                 width: 130,
49988                 listeners : {
49989                     'select': function(c, r, i) {
49990                         // initial support only for on class per el..
49991                         tb.selectedNode.className =  r ? r.get('val') : '';
49992                         editorcore.syncValue();
49993                     }
49994                 }
49995     
49996             }));
49997         }
49998         
49999         var tbc = Roo.form.HtmlEditor.ToolbarContext;
50000         
50001         
50002         for (var i = 0; i < tlist.length; i++) {
50003             
50004             // newer versions will use xtype cfg to create menus.
50005             if (typeof(tlist[i].xtype) != 'undefined') {
50006                 
50007                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50008                 
50009                 
50010                 continue;
50011             }
50012             
50013             var item = tlist[i];
50014             tb.add(item.title + ":&nbsp;");
50015             
50016             
50017             //optname == used so you can configure the options available..
50018             var opts = item.opts ? item.opts : false;
50019             if (item.optname) { // use the b
50020                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50021            
50022             }
50023             
50024             if (opts) {
50025                 // opts == pulldown..
50026                 tb.addField( new Roo.form.ComboBox({
50027                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50028                         id : 'val',
50029                         fields: ['val', 'display'],
50030                         data : opts  
50031                     }),
50032                     name : '-roo-edit-' + tlist[i].name,
50033                     
50034                     attrname : tlist[i].name,
50035                     stylename : item.style ? item.style : false,
50036                     
50037                     displayField: item.displayField ? item.displayField : 'val',
50038                     valueField :  'val',
50039                     typeAhead: false,
50040                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50041                     editable : false,
50042                     triggerAction: 'all',
50043                     emptyText:'Select',
50044                     selectOnFocus:true,
50045                     width: item.width ? item.width  : 130,
50046                     listeners : {
50047                         'select': function(c, r, i) {
50048                             if (tb.selectedNode.hasAttribute('data-block')) {
50049                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50050                                 b[c.attrname] = r.get('val');
50051                                 b.updateElement(tb.selectedNode);
50052                                 editorcore.syncValue();
50053                                 return;
50054                             }
50055                             
50056                             if (c.stylename) {
50057                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50058                                 editorcore.syncValue();
50059                                 return;
50060                             }
50061                             if (r === false) {
50062                                 tb.selectedNode.removeAttribute(c.attrname);
50063                                 editorcore.syncValue();
50064                                 return;
50065                             }
50066                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50067                             editorcore.syncValue();
50068                         }
50069                     }
50070
50071                 }));
50072                 continue;
50073                     
50074                  
50075                 /*
50076                 tb.addField( new Roo.form.TextField({
50077                     name: i,
50078                     width: 100,
50079                     //allowBlank:false,
50080                     value: ''
50081                 }));
50082                 continue;
50083                 */
50084             }
50085             tb.addField( new Roo.form.TextField({
50086                 name: '-roo-edit-' + tlist[i].name,
50087                 attrname : tlist[i].name,
50088                 
50089                 width: item.width,
50090                 //allowBlank:true,
50091                 value: '',
50092                 listeners: {
50093                     'change' : function(f, nv, ov) {
50094                         
50095                         if (tb.selectedNode.hasAttribute('data-block')) {
50096                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50097                             b[f.attrname] = nv;
50098                             b.updateElement(tb.selectedNode);
50099                             editorcore.syncValue();
50100                             return;
50101                         }
50102                         
50103                         tb.selectedNode.setAttribute(f.attrname, nv);
50104                         editorcore.syncValue();
50105                     }
50106                 }
50107             }));
50108              
50109         }
50110         
50111         var _this = this;
50112         
50113         if(nm == 'BODY'){
50114             tb.addSeparator();
50115         
50116             tb.addButton( {
50117                 text: 'Stylesheets',
50118
50119                 listeners : {
50120                     click : function ()
50121                     {
50122                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50123                     }
50124                 }
50125             });
50126         }
50127         
50128         tb.addFill();
50129         tb.addButton({
50130             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50131     
50132             listeners : {
50133                 click : function ()
50134                 {
50135                     var sn = tb.selectedNode;
50136                     if (block) {
50137                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50138                         
50139                     }
50140                     if (!sn) {
50141                         return;
50142                     }
50143                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50144                     if (sn.hasAttribute('data-block')) {
50145                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50146                         sn.parentNode.removeChild(sn);
50147                         
50148                     } else if (sn && sn.tagName != 'BODY') {
50149                         // remove and keep parents.
50150                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50151                         a.removeTag(sn);
50152                     }
50153                     
50154                     
50155                     var range = editorcore.createRange();
50156         
50157                     range.setStart(stn,0);
50158                     range.setEnd(stn,0); 
50159                     var selection = editorcore.getSelection();
50160                     selection.removeAllRanges();
50161                     selection.addRange(range);
50162                     
50163                     
50164                     //_this.updateToolbar(null, null, pn);
50165                     _this.updateToolbar(null, null, null);
50166                     _this.updateFooter(false);
50167                     
50168                 }
50169             }
50170             
50171                     
50172                 
50173             
50174         });
50175         
50176         
50177         tb.el.on('click', function(e){
50178             e.preventDefault(); // what does this do?
50179         });
50180         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50181         tb.el.hide();
50182         
50183         // dont need to disable them... as they will get hidden
50184         return tb;
50185          
50186         
50187     },
50188     buildFooter : function()
50189     {
50190         
50191         var fel = this.editor.wrap.createChild();
50192         this.footer = new Roo.Toolbar(fel);
50193         // toolbar has scrolly on left / right?
50194         var footDisp= new Roo.Toolbar.Fill();
50195         var _t = this;
50196         this.footer.add(
50197             {
50198                 text : '&lt;',
50199                 xtype: 'Button',
50200                 handler : function() {
50201                     _t.footDisp.scrollTo('left',0,true)
50202                 }
50203             }
50204         );
50205         this.footer.add( footDisp );
50206         this.footer.add( 
50207             {
50208                 text : '&gt;',
50209                 xtype: 'Button',
50210                 handler : function() {
50211                     // no animation..
50212                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50213                 }
50214             }
50215         );
50216         var fel = Roo.get(footDisp.el);
50217         fel.addClass('x-editor-context');
50218         this.footDispWrap = fel; 
50219         this.footDispWrap.overflow  = 'hidden';
50220         
50221         this.footDisp = fel.createChild();
50222         this.footDispWrap.on('click', this.onContextClick, this)
50223         
50224         
50225     },
50226     // when the footer contect changes
50227     onContextClick : function (ev,dom)
50228     {
50229         ev.preventDefault();
50230         var  cn = dom.className;
50231         //Roo.log(cn);
50232         if (!cn.match(/x-ed-loc-/)) {
50233             return;
50234         }
50235         var n = cn.split('-').pop();
50236         var ans = this.footerEls;
50237         var sel = ans[n];
50238         
50239         this.editorcore.selectNode(sel);
50240         
50241         
50242         this.updateToolbar(null, null, sel);
50243         
50244         
50245     }
50246     
50247     
50248     
50249     
50250     
50251 });
50252
50253
50254
50255
50256
50257 /*
50258  * Based on:
50259  * Ext JS Library 1.1.1
50260  * Copyright(c) 2006-2007, Ext JS, LLC.
50261  *
50262  * Originally Released Under LGPL - original licence link has changed is not relivant.
50263  *
50264  * Fork - LGPL
50265  * <script type="text/javascript">
50266  */
50267  
50268 /**
50269  * @class Roo.form.BasicForm
50270  * @extends Roo.util.Observable
50271  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50272  * @constructor
50273  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50274  * @param {Object} config Configuration options
50275  */
50276 Roo.form.BasicForm = function(el, config){
50277     this.allItems = [];
50278     this.childForms = [];
50279     Roo.apply(this, config);
50280     /*
50281      * The Roo.form.Field items in this form.
50282      * @type MixedCollection
50283      */
50284      
50285      
50286     this.items = new Roo.util.MixedCollection(false, function(o){
50287         return o.id || (o.id = Roo.id());
50288     });
50289     this.addEvents({
50290         /**
50291          * @event beforeaction
50292          * Fires before any action is performed. Return false to cancel the action.
50293          * @param {Form} this
50294          * @param {Action} action The action to be performed
50295          */
50296         beforeaction: true,
50297         /**
50298          * @event actionfailed
50299          * Fires when an action fails.
50300          * @param {Form} this
50301          * @param {Action} action The action that failed
50302          */
50303         actionfailed : true,
50304         /**
50305          * @event actioncomplete
50306          * Fires when an action is completed.
50307          * @param {Form} this
50308          * @param {Action} action The action that completed
50309          */
50310         actioncomplete : true
50311     });
50312     if(el){
50313         this.initEl(el);
50314     }
50315     Roo.form.BasicForm.superclass.constructor.call(this);
50316     
50317     Roo.form.BasicForm.popover.apply();
50318 };
50319
50320 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50321     /**
50322      * @cfg {String} method
50323      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50324      */
50325     /**
50326      * @cfg {DataReader} reader
50327      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50328      * This is optional as there is built-in support for processing JSON.
50329      */
50330     /**
50331      * @cfg {DataReader} errorReader
50332      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50333      * This is completely optional as there is built-in support for processing JSON.
50334      */
50335     /**
50336      * @cfg {String} url
50337      * The URL to use for form actions if one isn't supplied in the action options.
50338      */
50339     /**
50340      * @cfg {Boolean} fileUpload
50341      * Set to true if this form is a file upload.
50342      */
50343      
50344     /**
50345      * @cfg {Object} baseParams
50346      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50347      */
50348      /**
50349      
50350     /**
50351      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50352      */
50353     timeout: 30,
50354
50355     // private
50356     activeAction : null,
50357
50358     /**
50359      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50360      * or setValues() data instead of when the form was first created.
50361      */
50362     trackResetOnLoad : false,
50363     
50364     
50365     /**
50366      * childForms - used for multi-tab forms
50367      * @type {Array}
50368      */
50369     childForms : false,
50370     
50371     /**
50372      * allItems - full list of fields.
50373      * @type {Array}
50374      */
50375     allItems : false,
50376     
50377     /**
50378      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50379      * element by passing it or its id or mask the form itself by passing in true.
50380      * @type Mixed
50381      */
50382     waitMsgTarget : false,
50383     
50384     /**
50385      * @type Boolean
50386      */
50387     disableMask : false,
50388     
50389     /**
50390      * @cfg {Boolean} errorMask (true|false) default false
50391      */
50392     errorMask : false,
50393     
50394     /**
50395      * @cfg {Number} maskOffset Default 100
50396      */
50397     maskOffset : 100,
50398
50399     // private
50400     initEl : function(el){
50401         this.el = Roo.get(el);
50402         this.id = this.el.id || Roo.id();
50403         this.el.on('submit', this.onSubmit, this);
50404         this.el.addClass('x-form');
50405     },
50406
50407     // private
50408     onSubmit : function(e){
50409         e.stopEvent();
50410     },
50411
50412     /**
50413      * Returns true if client-side validation on the form is successful.
50414      * @return Boolean
50415      */
50416     isValid : function(){
50417         var valid = true;
50418         var target = false;
50419         this.items.each(function(f){
50420             if(f.validate()){
50421                 return;
50422             }
50423             
50424             valid = false;
50425                 
50426             if(!target && f.el.isVisible(true)){
50427                 target = f;
50428             }
50429         });
50430         
50431         if(this.errorMask && !valid){
50432             Roo.form.BasicForm.popover.mask(this, target);
50433         }
50434         
50435         return valid;
50436     },
50437     /**
50438      * Returns array of invalid form fields.
50439      * @return Array
50440      */
50441     
50442     invalidFields : function()
50443     {
50444         var ret = [];
50445         this.items.each(function(f){
50446             if(f.validate()){
50447                 return;
50448             }
50449             ret.push(f);
50450             
50451         });
50452         
50453         return ret;
50454     },
50455     
50456     
50457     /**
50458      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50459      * @return Boolean
50460      */
50461     isDirty : function(){
50462         var dirty = false;
50463         this.items.each(function(f){
50464            if(f.isDirty()){
50465                dirty = true;
50466                return false;
50467            }
50468         });
50469         return dirty;
50470     },
50471     
50472     /**
50473      * Returns true if any fields in this form have changed since their original load. (New version)
50474      * @return Boolean
50475      */
50476     
50477     hasChanged : function()
50478     {
50479         var dirty = false;
50480         this.items.each(function(f){
50481            if(f.hasChanged()){
50482                dirty = true;
50483                return false;
50484            }
50485         });
50486         return dirty;
50487         
50488     },
50489     /**
50490      * Resets all hasChanged to 'false' -
50491      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50492      * So hasChanged storage is only to be used for this purpose
50493      * @return Boolean
50494      */
50495     resetHasChanged : function()
50496     {
50497         this.items.each(function(f){
50498            f.resetHasChanged();
50499         });
50500         
50501     },
50502     
50503     
50504     /**
50505      * Performs a predefined action (submit or load) or custom actions you define on this form.
50506      * @param {String} actionName The name of the action type
50507      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50508      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50509      * accept other config options):
50510      * <pre>
50511 Property          Type             Description
50512 ----------------  ---------------  ----------------------------------------------------------------------------------
50513 url               String           The url for the action (defaults to the form's url)
50514 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50515 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50516 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50517                                    validate the form on the client (defaults to false)
50518      * </pre>
50519      * @return {BasicForm} this
50520      */
50521     doAction : function(action, options){
50522         if(typeof action == 'string'){
50523             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50524         }
50525         if(this.fireEvent('beforeaction', this, action) !== false){
50526             this.beforeAction(action);
50527             action.run.defer(100, action);
50528         }
50529         return this;
50530     },
50531
50532     /**
50533      * Shortcut to do a submit action.
50534      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50535      * @return {BasicForm} this
50536      */
50537     submit : function(options){
50538         this.doAction('submit', options);
50539         return this;
50540     },
50541
50542     /**
50543      * Shortcut to do a load action.
50544      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50545      * @return {BasicForm} this
50546      */
50547     load : function(options){
50548         this.doAction('load', options);
50549         return this;
50550     },
50551
50552     /**
50553      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50554      * @param {Record} record The record to edit
50555      * @return {BasicForm} this
50556      */
50557     updateRecord : function(record){
50558         record.beginEdit();
50559         var fs = record.fields;
50560         fs.each(function(f){
50561             var field = this.findField(f.name);
50562             if(field){
50563                 record.set(f.name, field.getValue());
50564             }
50565         }, this);
50566         record.endEdit();
50567         return this;
50568     },
50569
50570     /**
50571      * Loads an Roo.data.Record into this form.
50572      * @param {Record} record The record to load
50573      * @return {BasicForm} this
50574      */
50575     loadRecord : function(record){
50576         this.setValues(record.data);
50577         return this;
50578     },
50579
50580     // private
50581     beforeAction : function(action){
50582         var o = action.options;
50583         
50584         if(!this.disableMask) {
50585             if(this.waitMsgTarget === true){
50586                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50587             }else if(this.waitMsgTarget){
50588                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50589                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50590             }else {
50591                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50592             }
50593         }
50594         
50595          
50596     },
50597
50598     // private
50599     afterAction : function(action, success){
50600         this.activeAction = null;
50601         var o = action.options;
50602         
50603         if(!this.disableMask) {
50604             if(this.waitMsgTarget === true){
50605                 this.el.unmask();
50606             }else if(this.waitMsgTarget){
50607                 this.waitMsgTarget.unmask();
50608             }else{
50609                 Roo.MessageBox.updateProgress(1);
50610                 Roo.MessageBox.hide();
50611             }
50612         }
50613         
50614         if(success){
50615             if(o.reset){
50616                 this.reset();
50617             }
50618             Roo.callback(o.success, o.scope, [this, action]);
50619             this.fireEvent('actioncomplete', this, action);
50620             
50621         }else{
50622             
50623             // failure condition..
50624             // we have a scenario where updates need confirming.
50625             // eg. if a locking scenario exists..
50626             // we look for { errors : { needs_confirm : true }} in the response.
50627             if (
50628                 (typeof(action.result) != 'undefined')  &&
50629                 (typeof(action.result.errors) != 'undefined')  &&
50630                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50631            ){
50632                 var _t = this;
50633                 Roo.MessageBox.confirm(
50634                     "Change requires confirmation",
50635                     action.result.errorMsg,
50636                     function(r) {
50637                         if (r != 'yes') {
50638                             return;
50639                         }
50640                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50641                     }
50642                     
50643                 );
50644                 
50645                 
50646                 
50647                 return;
50648             }
50649             
50650             Roo.callback(o.failure, o.scope, [this, action]);
50651             // show an error message if no failed handler is set..
50652             if (!this.hasListener('actionfailed')) {
50653                 Roo.MessageBox.alert("Error",
50654                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50655                         action.result.errorMsg :
50656                         "Saving Failed, please check your entries or try again"
50657                 );
50658             }
50659             
50660             this.fireEvent('actionfailed', this, action);
50661         }
50662         
50663     },
50664
50665     /**
50666      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50667      * @param {String} id The value to search for
50668      * @return Field
50669      */
50670     findField : function(id){
50671         var field = this.items.get(id);
50672         if(!field){
50673             this.items.each(function(f){
50674                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50675                     field = f;
50676                     return false;
50677                 }
50678             });
50679         }
50680         return field || null;
50681     },
50682
50683     /**
50684      * Add a secondary form to this one, 
50685      * Used to provide tabbed forms. One form is primary, with hidden values 
50686      * which mirror the elements from the other forms.
50687      * 
50688      * @param {Roo.form.Form} form to add.
50689      * 
50690      */
50691     addForm : function(form)
50692     {
50693        
50694         if (this.childForms.indexOf(form) > -1) {
50695             // already added..
50696             return;
50697         }
50698         this.childForms.push(form);
50699         var n = '';
50700         Roo.each(form.allItems, function (fe) {
50701             
50702             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50703             if (this.findField(n)) { // already added..
50704                 return;
50705             }
50706             var add = new Roo.form.Hidden({
50707                 name : n
50708             });
50709             add.render(this.el);
50710             
50711             this.add( add );
50712         }, this);
50713         
50714     },
50715     /**
50716      * Mark fields in this form invalid in bulk.
50717      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50718      * @return {BasicForm} this
50719      */
50720     markInvalid : function(errors){
50721         if(errors instanceof Array){
50722             for(var i = 0, len = errors.length; i < len; i++){
50723                 var fieldError = errors[i];
50724                 var f = this.findField(fieldError.id);
50725                 if(f){
50726                     f.markInvalid(fieldError.msg);
50727                 }
50728             }
50729         }else{
50730             var field, id;
50731             for(id in errors){
50732                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50733                     field.markInvalid(errors[id]);
50734                 }
50735             }
50736         }
50737         Roo.each(this.childForms || [], function (f) {
50738             f.markInvalid(errors);
50739         });
50740         
50741         return this;
50742     },
50743
50744     /**
50745      * Set values for fields in this form in bulk.
50746      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50747      * @return {BasicForm} this
50748      */
50749     setValues : function(values){
50750         if(values instanceof Array){ // array of objects
50751             for(var i = 0, len = values.length; i < len; i++){
50752                 var v = values[i];
50753                 var f = this.findField(v.id);
50754                 if(f){
50755                     f.setValue(v.value);
50756                     if(this.trackResetOnLoad){
50757                         f.originalValue = f.getValue();
50758                     }
50759                 }
50760             }
50761         }else{ // object hash
50762             var field, id;
50763             for(id in values){
50764                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50765                     
50766                     if (field.setFromData && 
50767                         field.valueField && 
50768                         field.displayField &&
50769                         // combos' with local stores can 
50770                         // be queried via setValue()
50771                         // to set their value..
50772                         (field.store && !field.store.isLocal)
50773                         ) {
50774                         // it's a combo
50775                         var sd = { };
50776                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50777                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50778                         field.setFromData(sd);
50779                         
50780                     } else {
50781                         field.setValue(values[id]);
50782                     }
50783                     
50784                     
50785                     if(this.trackResetOnLoad){
50786                         field.originalValue = field.getValue();
50787                     }
50788                 }
50789             }
50790         }
50791         this.resetHasChanged();
50792         
50793         
50794         Roo.each(this.childForms || [], function (f) {
50795             f.setValues(values);
50796             f.resetHasChanged();
50797         });
50798                 
50799         return this;
50800     },
50801  
50802     /**
50803      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50804      * they are returned as an array.
50805      * @param {Boolean} asString
50806      * @return {Object}
50807      */
50808     getValues : function(asString)
50809     {
50810         if (this.childForms) {
50811             // copy values from the child forms
50812             Roo.each(this.childForms, function (f) {
50813                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50814             }, this);
50815         }
50816         
50817         // use formdata
50818         if (typeof(FormData) != 'undefined' && asString !== true) {
50819             // this relies on a 'recent' version of chrome apparently...
50820             try {
50821                 var fd = (new FormData(this.el.dom)).entries();
50822                 var ret = {};
50823                 var ent = fd.next();
50824                 while (!ent.done) {
50825                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50826                     ent = fd.next();
50827                 };
50828                 return ret;
50829             } catch(e) {
50830                 
50831             }
50832             
50833         }
50834         
50835         
50836         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50837         if(asString === true){
50838             return fs;
50839         }
50840         return Roo.urlDecode(fs);
50841     },
50842     
50843     /**
50844      * Returns the fields in this form as an object with key/value pairs. 
50845      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50846      * Normally this will not return readOnly data 
50847      * @param {Boolean} with_readonly return readonly field data.
50848      * @return {Object}
50849      */
50850     getFieldValues : function(with_readonly)
50851     {
50852         if (this.childForms) {
50853             // copy values from the child forms
50854             // should this call getFieldValues - probably not as we do not currently copy
50855             // hidden fields when we generate..
50856             Roo.each(this.childForms, function (f) {
50857                 this.setValues(f.getFieldValues());
50858             }, this);
50859         }
50860         
50861         var ret = {};
50862         this.items.each(function(f){
50863             
50864             if (f.readOnly && with_readonly !== true) {
50865                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50866                         // if a subform contains a copy of them.
50867                         // if you have subforms with the same editable data, you will need to copy the data back
50868                         // and forth.
50869             }
50870             
50871             if (!f.getName()) {
50872                 return;
50873             }
50874             var v = f.getValue();
50875             if (f.inputType =='radio') {
50876                 if (typeof(ret[f.getName()]) == 'undefined') {
50877                     ret[f.getName()] = ''; // empty..
50878                 }
50879                 
50880                 if (!f.el.dom.checked) {
50881                     return;
50882                     
50883                 }
50884                 v = f.el.dom.value;
50885                 
50886             }
50887             
50888             // not sure if this supported any more..
50889             if ((typeof(v) == 'object') && f.getRawValue) {
50890                 v = f.getRawValue() ; // dates..
50891             }
50892             // combo boxes where name != hiddenName...
50893             if (f.name != f.getName()) {
50894                 ret[f.name] = f.getRawValue();
50895             }
50896             ret[f.getName()] = v;
50897         });
50898         
50899         return ret;
50900     },
50901
50902     /**
50903      * Clears all invalid messages in this form.
50904      * @return {BasicForm} this
50905      */
50906     clearInvalid : function(){
50907         this.items.each(function(f){
50908            f.clearInvalid();
50909         });
50910         
50911         Roo.each(this.childForms || [], function (f) {
50912             f.clearInvalid();
50913         });
50914         
50915         
50916         return this;
50917     },
50918
50919     /**
50920      * Resets this form.
50921      * @return {BasicForm} this
50922      */
50923     reset : function(){
50924         this.items.each(function(f){
50925             f.reset();
50926         });
50927         
50928         Roo.each(this.childForms || [], function (f) {
50929             f.reset();
50930         });
50931         this.resetHasChanged();
50932         
50933         return this;
50934     },
50935
50936     /**
50937      * Add Roo.form components to this form.
50938      * @param {Field} field1
50939      * @param {Field} field2 (optional)
50940      * @param {Field} etc (optional)
50941      * @return {BasicForm} this
50942      */
50943     add : function(){
50944         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50945         return this;
50946     },
50947
50948
50949     /**
50950      * Removes a field from the items collection (does NOT remove its markup).
50951      * @param {Field} field
50952      * @return {BasicForm} this
50953      */
50954     remove : function(field){
50955         this.items.remove(field);
50956         return this;
50957     },
50958
50959     /**
50960      * Looks at the fields in this form, checks them for an id attribute,
50961      * and calls applyTo on the existing dom element with that id.
50962      * @return {BasicForm} this
50963      */
50964     render : function(){
50965         this.items.each(function(f){
50966             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50967                 f.applyTo(f.id);
50968             }
50969         });
50970         return this;
50971     },
50972
50973     /**
50974      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50975      * @param {Object} values
50976      * @return {BasicForm} this
50977      */
50978     applyToFields : function(o){
50979         this.items.each(function(f){
50980            Roo.apply(f, o);
50981         });
50982         return this;
50983     },
50984
50985     /**
50986      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50987      * @param {Object} values
50988      * @return {BasicForm} this
50989      */
50990     applyIfToFields : function(o){
50991         this.items.each(function(f){
50992            Roo.applyIf(f, o);
50993         });
50994         return this;
50995     }
50996 });
50997
50998 // back compat
50999 Roo.BasicForm = Roo.form.BasicForm;
51000
51001 Roo.apply(Roo.form.BasicForm, {
51002     
51003     popover : {
51004         
51005         padding : 5,
51006         
51007         isApplied : false,
51008         
51009         isMasked : false,
51010         
51011         form : false,
51012         
51013         target : false,
51014         
51015         intervalID : false,
51016         
51017         maskEl : false,
51018         
51019         apply : function()
51020         {
51021             if(this.isApplied){
51022                 return;
51023             }
51024             
51025             this.maskEl = {
51026                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51027                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51028                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51029                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51030             };
51031             
51032             this.maskEl.top.enableDisplayMode("block");
51033             this.maskEl.left.enableDisplayMode("block");
51034             this.maskEl.bottom.enableDisplayMode("block");
51035             this.maskEl.right.enableDisplayMode("block");
51036             
51037             Roo.get(document.body).on('click', function(){
51038                 this.unmask();
51039             }, this);
51040             
51041             Roo.get(document.body).on('touchstart', function(){
51042                 this.unmask();
51043             }, this);
51044             
51045             this.isApplied = true
51046         },
51047         
51048         mask : function(form, target)
51049         {
51050             this.form = form;
51051             
51052             this.target = target;
51053             
51054             if(!this.form.errorMask || !target.el){
51055                 return;
51056             }
51057             
51058             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51059             
51060             var ot = this.target.el.calcOffsetsTo(scrollable);
51061             
51062             var scrollTo = ot[1] - this.form.maskOffset;
51063             
51064             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51065             
51066             scrollable.scrollTo('top', scrollTo);
51067             
51068             var el = this.target.wrap || this.target.el;
51069             
51070             var box = el.getBox();
51071             
51072             this.maskEl.top.setStyle('position', 'absolute');
51073             this.maskEl.top.setStyle('z-index', 10000);
51074             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51075             this.maskEl.top.setLeft(0);
51076             this.maskEl.top.setTop(0);
51077             this.maskEl.top.show();
51078             
51079             this.maskEl.left.setStyle('position', 'absolute');
51080             this.maskEl.left.setStyle('z-index', 10000);
51081             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51082             this.maskEl.left.setLeft(0);
51083             this.maskEl.left.setTop(box.y - this.padding);
51084             this.maskEl.left.show();
51085
51086             this.maskEl.bottom.setStyle('position', 'absolute');
51087             this.maskEl.bottom.setStyle('z-index', 10000);
51088             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51089             this.maskEl.bottom.setLeft(0);
51090             this.maskEl.bottom.setTop(box.bottom + this.padding);
51091             this.maskEl.bottom.show();
51092
51093             this.maskEl.right.setStyle('position', 'absolute');
51094             this.maskEl.right.setStyle('z-index', 10000);
51095             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51096             this.maskEl.right.setLeft(box.right + this.padding);
51097             this.maskEl.right.setTop(box.y - this.padding);
51098             this.maskEl.right.show();
51099
51100             this.intervalID = window.setInterval(function() {
51101                 Roo.form.BasicForm.popover.unmask();
51102             }, 10000);
51103
51104             window.onwheel = function(){ return false;};
51105             
51106             (function(){ this.isMasked = true; }).defer(500, this);
51107             
51108         },
51109         
51110         unmask : function()
51111         {
51112             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51113                 return;
51114             }
51115             
51116             this.maskEl.top.setStyle('position', 'absolute');
51117             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51118             this.maskEl.top.hide();
51119
51120             this.maskEl.left.setStyle('position', 'absolute');
51121             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51122             this.maskEl.left.hide();
51123
51124             this.maskEl.bottom.setStyle('position', 'absolute');
51125             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51126             this.maskEl.bottom.hide();
51127
51128             this.maskEl.right.setStyle('position', 'absolute');
51129             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51130             this.maskEl.right.hide();
51131             
51132             window.onwheel = function(){ return true;};
51133             
51134             if(this.intervalID){
51135                 window.clearInterval(this.intervalID);
51136                 this.intervalID = false;
51137             }
51138             
51139             this.isMasked = false;
51140             
51141         }
51142         
51143     }
51144     
51145 });/*
51146  * Based on:
51147  * Ext JS Library 1.1.1
51148  * Copyright(c) 2006-2007, Ext JS, LLC.
51149  *
51150  * Originally Released Under LGPL - original licence link has changed is not relivant.
51151  *
51152  * Fork - LGPL
51153  * <script type="text/javascript">
51154  */
51155
51156 /**
51157  * @class Roo.form.Form
51158  * @extends Roo.form.BasicForm
51159  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51160  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51161  * @constructor
51162  * @param {Object} config Configuration options
51163  */
51164 Roo.form.Form = function(config){
51165     var xitems =  [];
51166     if (config.items) {
51167         xitems = config.items;
51168         delete config.items;
51169     }
51170    
51171     
51172     Roo.form.Form.superclass.constructor.call(this, null, config);
51173     this.url = this.url || this.action;
51174     if(!this.root){
51175         this.root = new Roo.form.Layout(Roo.applyIf({
51176             id: Roo.id()
51177         }, config));
51178     }
51179     this.active = this.root;
51180     /**
51181      * Array of all the buttons that have been added to this form via {@link addButton}
51182      * @type Array
51183      */
51184     this.buttons = [];
51185     this.allItems = [];
51186     this.addEvents({
51187         /**
51188          * @event clientvalidation
51189          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51190          * @param {Form} this
51191          * @param {Boolean} valid true if the form has passed client-side validation
51192          */
51193         clientvalidation: true,
51194         /**
51195          * @event rendered
51196          * Fires when the form is rendered
51197          * @param {Roo.form.Form} form
51198          */
51199         rendered : true
51200     });
51201     
51202     if (this.progressUrl) {
51203             // push a hidden field onto the list of fields..
51204             this.addxtype( {
51205                     xns: Roo.form, 
51206                     xtype : 'Hidden', 
51207                     name : 'UPLOAD_IDENTIFIER' 
51208             });
51209         }
51210         
51211     
51212     Roo.each(xitems, this.addxtype, this);
51213     
51214 };
51215
51216 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51217      /**
51218      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51219      */
51220     
51221     /**
51222      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51223      */
51224     /**
51225      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51226      */
51227     /**
51228      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51229      */
51230     buttonAlign:'center',
51231
51232     /**
51233      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51234      */
51235     minButtonWidth:75,
51236
51237     /**
51238      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51239      * This property cascades to child containers if not set.
51240      */
51241     labelAlign:'left',
51242
51243     /**
51244      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51245      * fires a looping event with that state. This is required to bind buttons to the valid
51246      * state using the config value formBind:true on the button.
51247      */
51248     monitorValid : false,
51249
51250     /**
51251      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51252      */
51253     monitorPoll : 200,
51254     
51255     /**
51256      * @cfg {String} progressUrl - Url to return progress data 
51257      */
51258     
51259     progressUrl : false,
51260     /**
51261      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51262      * sending a formdata with extra parameters - eg uploaded elements.
51263      */
51264     
51265     formData : false,
51266     
51267     /**
51268      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51269      * fields are added and the column is closed. If no fields are passed the column remains open
51270      * until end() is called.
51271      * @param {Object} config The config to pass to the column
51272      * @param {Field} field1 (optional)
51273      * @param {Field} field2 (optional)
51274      * @param {Field} etc (optional)
51275      * @return Column The column container object
51276      */
51277     column : function(c){
51278         var col = new Roo.form.Column(c);
51279         this.start(col);
51280         if(arguments.length > 1){ // duplicate code required because of Opera
51281             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51282             this.end();
51283         }
51284         return col;
51285     },
51286
51287     /**
51288      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51289      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51290      * until end() is called.
51291      * @param {Object} config The config to pass to the fieldset
51292      * @param {Field} field1 (optional)
51293      * @param {Field} field2 (optional)
51294      * @param {Field} etc (optional)
51295      * @return FieldSet The fieldset container object
51296      */
51297     fieldset : function(c){
51298         var fs = new Roo.form.FieldSet(c);
51299         this.start(fs);
51300         if(arguments.length > 1){ // duplicate code required because of Opera
51301             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51302             this.end();
51303         }
51304         return fs;
51305     },
51306
51307     /**
51308      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51309      * fields are added and the container is closed. If no fields are passed the container remains open
51310      * until end() is called.
51311      * @param {Object} config The config to pass to the Layout
51312      * @param {Field} field1 (optional)
51313      * @param {Field} field2 (optional)
51314      * @param {Field} etc (optional)
51315      * @return Layout The container object
51316      */
51317     container : function(c){
51318         var l = new Roo.form.Layout(c);
51319         this.start(l);
51320         if(arguments.length > 1){ // duplicate code required because of Opera
51321             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51322             this.end();
51323         }
51324         return l;
51325     },
51326
51327     /**
51328      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51329      * @param {Object} container A Roo.form.Layout or subclass of Layout
51330      * @return {Form} this
51331      */
51332     start : function(c){
51333         // cascade label info
51334         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51335         this.active.stack.push(c);
51336         c.ownerCt = this.active;
51337         this.active = c;
51338         return this;
51339     },
51340
51341     /**
51342      * Closes the current open container
51343      * @return {Form} this
51344      */
51345     end : function(){
51346         if(this.active == this.root){
51347             return this;
51348         }
51349         this.active = this.active.ownerCt;
51350         return this;
51351     },
51352
51353     /**
51354      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51355      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51356      * as the label of the field.
51357      * @param {Field} field1
51358      * @param {Field} field2 (optional)
51359      * @param {Field} etc. (optional)
51360      * @return {Form} this
51361      */
51362     add : function(){
51363         this.active.stack.push.apply(this.active.stack, arguments);
51364         this.allItems.push.apply(this.allItems,arguments);
51365         var r = [];
51366         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51367             if(a[i].isFormField){
51368                 r.push(a[i]);
51369             }
51370         }
51371         if(r.length > 0){
51372             Roo.form.Form.superclass.add.apply(this, r);
51373         }
51374         return this;
51375     },
51376     
51377
51378     
51379     
51380     
51381      /**
51382      * Find any element that has been added to a form, using it's ID or name
51383      * This can include framesets, columns etc. along with regular fields..
51384      * @param {String} id - id or name to find.
51385      
51386      * @return {Element} e - or false if nothing found.
51387      */
51388     findbyId : function(id)
51389     {
51390         var ret = false;
51391         if (!id) {
51392             return ret;
51393         }
51394         Roo.each(this.allItems, function(f){
51395             if (f.id == id || f.name == id ){
51396                 ret = f;
51397                 return false;
51398             }
51399         });
51400         return ret;
51401     },
51402
51403     
51404     
51405     /**
51406      * Render this form into the passed container. This should only be called once!
51407      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51408      * @return {Form} this
51409      */
51410     render : function(ct)
51411     {
51412         
51413         
51414         
51415         ct = Roo.get(ct);
51416         var o = this.autoCreate || {
51417             tag: 'form',
51418             method : this.method || 'POST',
51419             id : this.id || Roo.id()
51420         };
51421         this.initEl(ct.createChild(o));
51422
51423         this.root.render(this.el);
51424         
51425        
51426              
51427         this.items.each(function(f){
51428             f.render('x-form-el-'+f.id);
51429         });
51430
51431         if(this.buttons.length > 0){
51432             // tables are required to maintain order and for correct IE layout
51433             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51434                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51435                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51436             }}, null, true);
51437             var tr = tb.getElementsByTagName('tr')[0];
51438             for(var i = 0, len = this.buttons.length; i < len; i++) {
51439                 var b = this.buttons[i];
51440                 var td = document.createElement('td');
51441                 td.className = 'x-form-btn-td';
51442                 b.render(tr.appendChild(td));
51443             }
51444         }
51445         if(this.monitorValid){ // initialize after render
51446             this.startMonitoring();
51447         }
51448         this.fireEvent('rendered', this);
51449         return this;
51450     },
51451
51452     /**
51453      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51454      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51455      * object or a valid Roo.DomHelper element config
51456      * @param {Function} handler The function called when the button is clicked
51457      * @param {Object} scope (optional) The scope of the handler function
51458      * @return {Roo.Button}
51459      */
51460     addButton : function(config, handler, scope){
51461         var bc = {
51462             handler: handler,
51463             scope: scope,
51464             minWidth: this.minButtonWidth,
51465             hideParent:true
51466         };
51467         if(typeof config == "string"){
51468             bc.text = config;
51469         }else{
51470             Roo.apply(bc, config);
51471         }
51472         var btn = new Roo.Button(null, bc);
51473         this.buttons.push(btn);
51474         return btn;
51475     },
51476
51477      /**
51478      * Adds a series of form elements (using the xtype property as the factory method.
51479      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51480      * @param {Object} config 
51481      */
51482     
51483     addxtype : function()
51484     {
51485         var ar = Array.prototype.slice.call(arguments, 0);
51486         var ret = false;
51487         for(var i = 0; i < ar.length; i++) {
51488             if (!ar[i]) {
51489                 continue; // skip -- if this happends something invalid got sent, we 
51490                 // should ignore it, as basically that interface element will not show up
51491                 // and that should be pretty obvious!!
51492             }
51493             
51494             if (Roo.form[ar[i].xtype]) {
51495                 ar[i].form = this;
51496                 var fe = Roo.factory(ar[i], Roo.form);
51497                 if (!ret) {
51498                     ret = fe;
51499                 }
51500                 fe.form = this;
51501                 if (fe.store) {
51502                     fe.store.form = this;
51503                 }
51504                 if (fe.isLayout) {  
51505                          
51506                     this.start(fe);
51507                     this.allItems.push(fe);
51508                     if (fe.items && fe.addxtype) {
51509                         fe.addxtype.apply(fe, fe.items);
51510                         delete fe.items;
51511                     }
51512                      this.end();
51513                     continue;
51514                 }
51515                 
51516                 
51517                  
51518                 this.add(fe);
51519               //  console.log('adding ' + ar[i].xtype);
51520             }
51521             if (ar[i].xtype == 'Button') {  
51522                 //console.log('adding button');
51523                 //console.log(ar[i]);
51524                 this.addButton(ar[i]);
51525                 this.allItems.push(fe);
51526                 continue;
51527             }
51528             
51529             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51530                 alert('end is not supported on xtype any more, use items');
51531             //    this.end();
51532             //    //console.log('adding end');
51533             }
51534             
51535         }
51536         return ret;
51537     },
51538     
51539     /**
51540      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51541      * option "monitorValid"
51542      */
51543     startMonitoring : function(){
51544         if(!this.bound){
51545             this.bound = true;
51546             Roo.TaskMgr.start({
51547                 run : this.bindHandler,
51548                 interval : this.monitorPoll || 200,
51549                 scope: this
51550             });
51551         }
51552     },
51553
51554     /**
51555      * Stops monitoring of the valid state of this form
51556      */
51557     stopMonitoring : function(){
51558         this.bound = false;
51559     },
51560
51561     // private
51562     bindHandler : function(){
51563         if(!this.bound){
51564             return false; // stops binding
51565         }
51566         var valid = true;
51567         this.items.each(function(f){
51568             if(!f.isValid(true)){
51569                 valid = false;
51570                 return false;
51571             }
51572         });
51573         for(var i = 0, len = this.buttons.length; i < len; i++){
51574             var btn = this.buttons[i];
51575             if(btn.formBind === true && btn.disabled === valid){
51576                 btn.setDisabled(!valid);
51577             }
51578         }
51579         this.fireEvent('clientvalidation', this, valid);
51580     }
51581     
51582     
51583     
51584     
51585     
51586     
51587     
51588     
51589 });
51590
51591
51592 // back compat
51593 Roo.Form = Roo.form.Form;
51594 /*
51595  * Based on:
51596  * Ext JS Library 1.1.1
51597  * Copyright(c) 2006-2007, Ext JS, LLC.
51598  *
51599  * Originally Released Under LGPL - original licence link has changed is not relivant.
51600  *
51601  * Fork - LGPL
51602  * <script type="text/javascript">
51603  */
51604
51605 // as we use this in bootstrap.
51606 Roo.namespace('Roo.form');
51607  /**
51608  * @class Roo.form.Action
51609  * Internal Class used to handle form actions
51610  * @constructor
51611  * @param {Roo.form.BasicForm} el The form element or its id
51612  * @param {Object} config Configuration options
51613  */
51614
51615  
51616  
51617 // define the action interface
51618 Roo.form.Action = function(form, options){
51619     this.form = form;
51620     this.options = options || {};
51621 };
51622 /**
51623  * Client Validation Failed
51624  * @const 
51625  */
51626 Roo.form.Action.CLIENT_INVALID = 'client';
51627 /**
51628  * Server Validation Failed
51629  * @const 
51630  */
51631 Roo.form.Action.SERVER_INVALID = 'server';
51632  /**
51633  * Connect to Server Failed
51634  * @const 
51635  */
51636 Roo.form.Action.CONNECT_FAILURE = 'connect';
51637 /**
51638  * Reading Data from Server Failed
51639  * @const 
51640  */
51641 Roo.form.Action.LOAD_FAILURE = 'load';
51642
51643 Roo.form.Action.prototype = {
51644     type : 'default',
51645     failureType : undefined,
51646     response : undefined,
51647     result : undefined,
51648
51649     // interface method
51650     run : function(options){
51651
51652     },
51653
51654     // interface method
51655     success : function(response){
51656
51657     },
51658
51659     // interface method
51660     handleResponse : function(response){
51661
51662     },
51663
51664     // default connection failure
51665     failure : function(response){
51666         
51667         this.response = response;
51668         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51669         this.form.afterAction(this, false);
51670     },
51671
51672     processResponse : function(response){
51673         this.response = response;
51674         if(!response.responseText){
51675             return true;
51676         }
51677         this.result = this.handleResponse(response);
51678         return this.result;
51679     },
51680
51681     // utility functions used internally
51682     getUrl : function(appendParams){
51683         var url = this.options.url || this.form.url || this.form.el.dom.action;
51684         if(appendParams){
51685             var p = this.getParams();
51686             if(p){
51687                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51688             }
51689         }
51690         return url;
51691     },
51692
51693     getMethod : function(){
51694         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51695     },
51696
51697     getParams : function(){
51698         var bp = this.form.baseParams;
51699         var p = this.options.params;
51700         if(p){
51701             if(typeof p == "object"){
51702                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51703             }else if(typeof p == 'string' && bp){
51704                 p += '&' + Roo.urlEncode(bp);
51705             }
51706         }else if(bp){
51707             p = Roo.urlEncode(bp);
51708         }
51709         return p;
51710     },
51711
51712     createCallback : function(){
51713         return {
51714             success: this.success,
51715             failure: this.failure,
51716             scope: this,
51717             timeout: (this.form.timeout*1000),
51718             upload: this.form.fileUpload ? this.success : undefined
51719         };
51720     }
51721 };
51722
51723 Roo.form.Action.Submit = function(form, options){
51724     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51725 };
51726
51727 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51728     type : 'submit',
51729
51730     haveProgress : false,
51731     uploadComplete : false,
51732     
51733     // uploadProgress indicator.
51734     uploadProgress : function()
51735     {
51736         if (!this.form.progressUrl) {
51737             return;
51738         }
51739         
51740         if (!this.haveProgress) {
51741             Roo.MessageBox.progress("Uploading", "Uploading");
51742         }
51743         if (this.uploadComplete) {
51744            Roo.MessageBox.hide();
51745            return;
51746         }
51747         
51748         this.haveProgress = true;
51749    
51750         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51751         
51752         var c = new Roo.data.Connection();
51753         c.request({
51754             url : this.form.progressUrl,
51755             params: {
51756                 id : uid
51757             },
51758             method: 'GET',
51759             success : function(req){
51760                //console.log(data);
51761                 var rdata = false;
51762                 var edata;
51763                 try  {
51764                    rdata = Roo.decode(req.responseText)
51765                 } catch (e) {
51766                     Roo.log("Invalid data from server..");
51767                     Roo.log(edata);
51768                     return;
51769                 }
51770                 if (!rdata || !rdata.success) {
51771                     Roo.log(rdata);
51772                     Roo.MessageBox.alert(Roo.encode(rdata));
51773                     return;
51774                 }
51775                 var data = rdata.data;
51776                 
51777                 if (this.uploadComplete) {
51778                    Roo.MessageBox.hide();
51779                    return;
51780                 }
51781                    
51782                 if (data){
51783                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51784                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51785                     );
51786                 }
51787                 this.uploadProgress.defer(2000,this);
51788             },
51789        
51790             failure: function(data) {
51791                 Roo.log('progress url failed ');
51792                 Roo.log(data);
51793             },
51794             scope : this
51795         });
51796            
51797     },
51798     
51799     
51800     run : function()
51801     {
51802         // run get Values on the form, so it syncs any secondary forms.
51803         this.form.getValues();
51804         
51805         var o = this.options;
51806         var method = this.getMethod();
51807         var isPost = method == 'POST';
51808         if(o.clientValidation === false || this.form.isValid()){
51809             
51810             if (this.form.progressUrl) {
51811                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51812                     (new Date() * 1) + '' + Math.random());
51813                     
51814             } 
51815             
51816             
51817             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51818                 form:this.form.el.dom,
51819                 url:this.getUrl(!isPost),
51820                 method: method,
51821                 params:isPost ? this.getParams() : null,
51822                 isUpload: this.form.fileUpload,
51823                 formData : this.form.formData
51824             }));
51825             
51826             this.uploadProgress();
51827
51828         }else if (o.clientValidation !== false){ // client validation failed
51829             this.failureType = Roo.form.Action.CLIENT_INVALID;
51830             this.form.afterAction(this, false);
51831         }
51832     },
51833
51834     success : function(response)
51835     {
51836         this.uploadComplete= true;
51837         if (this.haveProgress) {
51838             Roo.MessageBox.hide();
51839         }
51840         
51841         
51842         var result = this.processResponse(response);
51843         if(result === true || result.success){
51844             this.form.afterAction(this, true);
51845             return;
51846         }
51847         if(result.errors){
51848             this.form.markInvalid(result.errors);
51849             this.failureType = Roo.form.Action.SERVER_INVALID;
51850         }
51851         this.form.afterAction(this, false);
51852     },
51853     failure : function(response)
51854     {
51855         this.uploadComplete= true;
51856         if (this.haveProgress) {
51857             Roo.MessageBox.hide();
51858         }
51859         
51860         this.response = response;
51861         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51862         this.form.afterAction(this, false);
51863     },
51864     
51865     handleResponse : function(response){
51866         if(this.form.errorReader){
51867             var rs = this.form.errorReader.read(response);
51868             var errors = [];
51869             if(rs.records){
51870                 for(var i = 0, len = rs.records.length; i < len; i++) {
51871                     var r = rs.records[i];
51872                     errors[i] = r.data;
51873                 }
51874             }
51875             if(errors.length < 1){
51876                 errors = null;
51877             }
51878             return {
51879                 success : rs.success,
51880                 errors : errors
51881             };
51882         }
51883         var ret = false;
51884         try {
51885             ret = Roo.decode(response.responseText);
51886         } catch (e) {
51887             ret = {
51888                 success: false,
51889                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51890                 errors : []
51891             };
51892         }
51893         return ret;
51894         
51895     }
51896 });
51897
51898
51899 Roo.form.Action.Load = function(form, options){
51900     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51901     this.reader = this.form.reader;
51902 };
51903
51904 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51905     type : 'load',
51906
51907     run : function(){
51908         
51909         Roo.Ajax.request(Roo.apply(
51910                 this.createCallback(), {
51911                     method:this.getMethod(),
51912                     url:this.getUrl(false),
51913                     params:this.getParams()
51914         }));
51915     },
51916
51917     success : function(response){
51918         
51919         var result = this.processResponse(response);
51920         if(result === true || !result.success || !result.data){
51921             this.failureType = Roo.form.Action.LOAD_FAILURE;
51922             this.form.afterAction(this, false);
51923             return;
51924         }
51925         this.form.clearInvalid();
51926         this.form.setValues(result.data);
51927         this.form.afterAction(this, true);
51928     },
51929
51930     handleResponse : function(response){
51931         if(this.form.reader){
51932             var rs = this.form.reader.read(response);
51933             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51934             return {
51935                 success : rs.success,
51936                 data : data
51937             };
51938         }
51939         return Roo.decode(response.responseText);
51940     }
51941 });
51942
51943 Roo.form.Action.ACTION_TYPES = {
51944     'load' : Roo.form.Action.Load,
51945     'submit' : Roo.form.Action.Submit
51946 };/*
51947  * Based on:
51948  * Ext JS Library 1.1.1
51949  * Copyright(c) 2006-2007, Ext JS, LLC.
51950  *
51951  * Originally Released Under LGPL - original licence link has changed is not relivant.
51952  *
51953  * Fork - LGPL
51954  * <script type="text/javascript">
51955  */
51956  
51957 /**
51958  * @class Roo.form.Layout
51959  * @extends Roo.Component
51960  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51961  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51962  * @constructor
51963  * @param {Object} config Configuration options
51964  */
51965 Roo.form.Layout = function(config){
51966     var xitems = [];
51967     if (config.items) {
51968         xitems = config.items;
51969         delete config.items;
51970     }
51971     Roo.form.Layout.superclass.constructor.call(this, config);
51972     this.stack = [];
51973     Roo.each(xitems, this.addxtype, this);
51974      
51975 };
51976
51977 Roo.extend(Roo.form.Layout, Roo.Component, {
51978     /**
51979      * @cfg {String/Object} autoCreate
51980      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51981      */
51982     /**
51983      * @cfg {String/Object/Function} style
51984      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51985      * a function which returns such a specification.
51986      */
51987     /**
51988      * @cfg {String} labelAlign
51989      * Valid values are "left," "top" and "right" (defaults to "left")
51990      */
51991     /**
51992      * @cfg {Number} labelWidth
51993      * Fixed width in pixels of all field labels (defaults to undefined)
51994      */
51995     /**
51996      * @cfg {Boolean} clear
51997      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51998      */
51999     clear : true,
52000     /**
52001      * @cfg {String} labelSeparator
52002      * The separator to use after field labels (defaults to ':')
52003      */
52004     labelSeparator : ':',
52005     /**
52006      * @cfg {Boolean} hideLabels
52007      * True to suppress the display of field labels in this layout (defaults to false)
52008      */
52009     hideLabels : false,
52010
52011     // private
52012     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52013     
52014     isLayout : true,
52015     
52016     // private
52017     onRender : function(ct, position){
52018         if(this.el){ // from markup
52019             this.el = Roo.get(this.el);
52020         }else {  // generate
52021             var cfg = this.getAutoCreate();
52022             this.el = ct.createChild(cfg, position);
52023         }
52024         if(this.style){
52025             this.el.applyStyles(this.style);
52026         }
52027         if(this.labelAlign){
52028             this.el.addClass('x-form-label-'+this.labelAlign);
52029         }
52030         if(this.hideLabels){
52031             this.labelStyle = "display:none";
52032             this.elementStyle = "padding-left:0;";
52033         }else{
52034             if(typeof this.labelWidth == 'number'){
52035                 this.labelStyle = "width:"+this.labelWidth+"px;";
52036                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52037             }
52038             if(this.labelAlign == 'top'){
52039                 this.labelStyle = "width:auto;";
52040                 this.elementStyle = "padding-left:0;";
52041             }
52042         }
52043         var stack = this.stack;
52044         var slen = stack.length;
52045         if(slen > 0){
52046             if(!this.fieldTpl){
52047                 var t = new Roo.Template(
52048                     '<div class="x-form-item {5}">',
52049                         '<label for="{0}" style="{2}">{1}{4}</label>',
52050                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52051                         '</div>',
52052                     '</div><div class="x-form-clear-left"></div>'
52053                 );
52054                 t.disableFormats = true;
52055                 t.compile();
52056                 Roo.form.Layout.prototype.fieldTpl = t;
52057             }
52058             for(var i = 0; i < slen; i++) {
52059                 if(stack[i].isFormField){
52060                     this.renderField(stack[i]);
52061                 }else{
52062                     this.renderComponent(stack[i]);
52063                 }
52064             }
52065         }
52066         if(this.clear){
52067             this.el.createChild({cls:'x-form-clear'});
52068         }
52069     },
52070
52071     // private
52072     renderField : function(f){
52073         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52074                f.id, //0
52075                f.fieldLabel, //1
52076                f.labelStyle||this.labelStyle||'', //2
52077                this.elementStyle||'', //3
52078                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52079                f.itemCls||this.itemCls||''  //5
52080        ], true).getPrevSibling());
52081     },
52082
52083     // private
52084     renderComponent : function(c){
52085         c.render(c.isLayout ? this.el : this.el.createChild());    
52086     },
52087     /**
52088      * Adds a object form elements (using the xtype property as the factory method.)
52089      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52090      * @param {Object} config 
52091      */
52092     addxtype : function(o)
52093     {
52094         // create the lement.
52095         o.form = this.form;
52096         var fe = Roo.factory(o, Roo.form);
52097         this.form.allItems.push(fe);
52098         this.stack.push(fe);
52099         
52100         if (fe.isFormField) {
52101             this.form.items.add(fe);
52102         }
52103          
52104         return fe;
52105     }
52106 });
52107
52108 /**
52109  * @class Roo.form.Column
52110  * @extends Roo.form.Layout
52111  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52112  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52113  * @constructor
52114  * @param {Object} config Configuration options
52115  */
52116 Roo.form.Column = function(config){
52117     Roo.form.Column.superclass.constructor.call(this, config);
52118 };
52119
52120 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52121     /**
52122      * @cfg {Number/String} width
52123      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52124      */
52125     /**
52126      * @cfg {String/Object} autoCreate
52127      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52128      */
52129
52130     // private
52131     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52132
52133     // private
52134     onRender : function(ct, position){
52135         Roo.form.Column.superclass.onRender.call(this, ct, position);
52136         if(this.width){
52137             this.el.setWidth(this.width);
52138         }
52139     }
52140 });
52141
52142
52143 /**
52144  * @class Roo.form.Row
52145  * @extends Roo.form.Layout
52146  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52147  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52148  * @constructor
52149  * @param {Object} config Configuration options
52150  */
52151
52152  
52153 Roo.form.Row = function(config){
52154     Roo.form.Row.superclass.constructor.call(this, config);
52155 };
52156  
52157 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52158       /**
52159      * @cfg {Number/String} width
52160      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52161      */
52162     /**
52163      * @cfg {Number/String} height
52164      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52165      */
52166     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52167     
52168     padWidth : 20,
52169     // private
52170     onRender : function(ct, position){
52171         //console.log('row render');
52172         if(!this.rowTpl){
52173             var t = new Roo.Template(
52174                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52175                     '<label for="{0}" style="{2}">{1}{4}</label>',
52176                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52177                     '</div>',
52178                 '</div>'
52179             );
52180             t.disableFormats = true;
52181             t.compile();
52182             Roo.form.Layout.prototype.rowTpl = t;
52183         }
52184         this.fieldTpl = this.rowTpl;
52185         
52186         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52187         var labelWidth = 100;
52188         
52189         if ((this.labelAlign != 'top')) {
52190             if (typeof this.labelWidth == 'number') {
52191                 labelWidth = this.labelWidth
52192             }
52193             this.padWidth =  20 + labelWidth;
52194             
52195         }
52196         
52197         Roo.form.Column.superclass.onRender.call(this, ct, position);
52198         if(this.width){
52199             this.el.setWidth(this.width);
52200         }
52201         if(this.height){
52202             this.el.setHeight(this.height);
52203         }
52204     },
52205     
52206     // private
52207     renderField : function(f){
52208         f.fieldEl = this.fieldTpl.append(this.el, [
52209                f.id, f.fieldLabel,
52210                f.labelStyle||this.labelStyle||'',
52211                this.elementStyle||'',
52212                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52213                f.itemCls||this.itemCls||'',
52214                f.width ? f.width + this.padWidth : 160 + this.padWidth
52215        ],true);
52216     }
52217 });
52218  
52219
52220 /**
52221  * @class Roo.form.FieldSet
52222  * @extends Roo.form.Layout
52223  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52224  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52225  * @constructor
52226  * @param {Object} config Configuration options
52227  */
52228 Roo.form.FieldSet = function(config){
52229     Roo.form.FieldSet.superclass.constructor.call(this, config);
52230 };
52231
52232 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52233     /**
52234      * @cfg {String} legend
52235      * The text to display as the legend for the FieldSet (defaults to '')
52236      */
52237     /**
52238      * @cfg {String/Object} autoCreate
52239      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52240      */
52241
52242     // private
52243     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52244
52245     // private
52246     onRender : function(ct, position){
52247         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52248         if(this.legend){
52249             this.setLegend(this.legend);
52250         }
52251     },
52252
52253     // private
52254     setLegend : function(text){
52255         if(this.rendered){
52256             this.el.child('legend').update(text);
52257         }
52258     }
52259 });/*
52260  * Based on:
52261  * Ext JS Library 1.1.1
52262  * Copyright(c) 2006-2007, Ext JS, LLC.
52263  *
52264  * Originally Released Under LGPL - original licence link has changed is not relivant.
52265  *
52266  * Fork - LGPL
52267  * <script type="text/javascript">
52268  */
52269 /**
52270  * @class Roo.form.VTypes
52271  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52272  * @static
52273  */
52274 Roo.form.VTypes = function(){
52275     // closure these in so they are only created once.
52276     var alpha = /^[a-zA-Z_]+$/;
52277     var alphanum = /^[a-zA-Z0-9_]+$/;
52278     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52279     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52280
52281     // All these messages and functions are configurable
52282     return {
52283         /**
52284          * The function used to validate email addresses
52285          * @param {String} value The email address
52286          */
52287         'email' : function(v){
52288             return email.test(v);
52289         },
52290         /**
52291          * The error text to display when the email validation function returns false
52292          * @type String
52293          */
52294         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52295         /**
52296          * The keystroke filter mask to be applied on email input
52297          * @type RegExp
52298          */
52299         'emailMask' : /[a-z0-9_\.\-@]/i,
52300
52301         /**
52302          * The function used to validate URLs
52303          * @param {String} value The URL
52304          */
52305         'url' : function(v){
52306             return url.test(v);
52307         },
52308         /**
52309          * The error text to display when the url validation function returns false
52310          * @type String
52311          */
52312         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52313         
52314         /**
52315          * The function used to validate alpha values
52316          * @param {String} value The value
52317          */
52318         'alpha' : function(v){
52319             return alpha.test(v);
52320         },
52321         /**
52322          * The error text to display when the alpha validation function returns false
52323          * @type String
52324          */
52325         'alphaText' : 'This field should only contain letters and _',
52326         /**
52327          * The keystroke filter mask to be applied on alpha input
52328          * @type RegExp
52329          */
52330         'alphaMask' : /[a-z_]/i,
52331
52332         /**
52333          * The function used to validate alphanumeric values
52334          * @param {String} value The value
52335          */
52336         'alphanum' : function(v){
52337             return alphanum.test(v);
52338         },
52339         /**
52340          * The error text to display when the alphanumeric validation function returns false
52341          * @type String
52342          */
52343         'alphanumText' : 'This field should only contain letters, numbers and _',
52344         /**
52345          * The keystroke filter mask to be applied on alphanumeric input
52346          * @type RegExp
52347          */
52348         'alphanumMask' : /[a-z0-9_]/i
52349     };
52350 }();//<script type="text/javascript">
52351
52352 /**
52353  * @class Roo.form.FCKeditor
52354  * @extends Roo.form.TextArea
52355  * Wrapper around the FCKEditor http://www.fckeditor.net
52356  * @constructor
52357  * Creates a new FCKeditor
52358  * @param {Object} config Configuration options
52359  */
52360 Roo.form.FCKeditor = function(config){
52361     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52362     this.addEvents({
52363          /**
52364          * @event editorinit
52365          * Fired when the editor is initialized - you can add extra handlers here..
52366          * @param {FCKeditor} this
52367          * @param {Object} the FCK object.
52368          */
52369         editorinit : true
52370     });
52371     
52372     
52373 };
52374 Roo.form.FCKeditor.editors = { };
52375 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52376 {
52377     //defaultAutoCreate : {
52378     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52379     //},
52380     // private
52381     /**
52382      * @cfg {Object} fck options - see fck manual for details.
52383      */
52384     fckconfig : false,
52385     
52386     /**
52387      * @cfg {Object} fck toolbar set (Basic or Default)
52388      */
52389     toolbarSet : 'Basic',
52390     /**
52391      * @cfg {Object} fck BasePath
52392      */ 
52393     basePath : '/fckeditor/',
52394     
52395     
52396     frame : false,
52397     
52398     value : '',
52399     
52400    
52401     onRender : function(ct, position)
52402     {
52403         if(!this.el){
52404             this.defaultAutoCreate = {
52405                 tag: "textarea",
52406                 style:"width:300px;height:60px;",
52407                 autocomplete: "new-password"
52408             };
52409         }
52410         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52411         /*
52412         if(this.grow){
52413             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52414             if(this.preventScrollbars){
52415                 this.el.setStyle("overflow", "hidden");
52416             }
52417             this.el.setHeight(this.growMin);
52418         }
52419         */
52420         //console.log('onrender' + this.getId() );
52421         Roo.form.FCKeditor.editors[this.getId()] = this;
52422          
52423
52424         this.replaceTextarea() ;
52425         
52426     },
52427     
52428     getEditor : function() {
52429         return this.fckEditor;
52430     },
52431     /**
52432      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52433      * @param {Mixed} value The value to set
52434      */
52435     
52436     
52437     setValue : function(value)
52438     {
52439         //console.log('setValue: ' + value);
52440         
52441         if(typeof(value) == 'undefined') { // not sure why this is happending...
52442             return;
52443         }
52444         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52445         
52446         //if(!this.el || !this.getEditor()) {
52447         //    this.value = value;
52448             //this.setValue.defer(100,this,[value]);    
52449         //    return;
52450         //} 
52451         
52452         if(!this.getEditor()) {
52453             return;
52454         }
52455         
52456         this.getEditor().SetData(value);
52457         
52458         //
52459
52460     },
52461
52462     /**
52463      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52464      * @return {Mixed} value The field value
52465      */
52466     getValue : function()
52467     {
52468         
52469         if (this.frame && this.frame.dom.style.display == 'none') {
52470             return Roo.form.FCKeditor.superclass.getValue.call(this);
52471         }
52472         
52473         if(!this.el || !this.getEditor()) {
52474            
52475            // this.getValue.defer(100,this); 
52476             return this.value;
52477         }
52478        
52479         
52480         var value=this.getEditor().GetData();
52481         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52482         return Roo.form.FCKeditor.superclass.getValue.call(this);
52483         
52484
52485     },
52486
52487     /**
52488      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52489      * @return {Mixed} value The field value
52490      */
52491     getRawValue : function()
52492     {
52493         if (this.frame && this.frame.dom.style.display == 'none') {
52494             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52495         }
52496         
52497         if(!this.el || !this.getEditor()) {
52498             //this.getRawValue.defer(100,this); 
52499             return this.value;
52500             return;
52501         }
52502         
52503         
52504         
52505         var value=this.getEditor().GetData();
52506         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52507         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52508          
52509     },
52510     
52511     setSize : function(w,h) {
52512         
52513         
52514         
52515         //if (this.frame && this.frame.dom.style.display == 'none') {
52516         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52517         //    return;
52518         //}
52519         //if(!this.el || !this.getEditor()) {
52520         //    this.setSize.defer(100,this, [w,h]); 
52521         //    return;
52522         //}
52523         
52524         
52525         
52526         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52527         
52528         this.frame.dom.setAttribute('width', w);
52529         this.frame.dom.setAttribute('height', h);
52530         this.frame.setSize(w,h);
52531         
52532     },
52533     
52534     toggleSourceEdit : function(value) {
52535         
52536       
52537          
52538         this.el.dom.style.display = value ? '' : 'none';
52539         this.frame.dom.style.display = value ?  'none' : '';
52540         
52541     },
52542     
52543     
52544     focus: function(tag)
52545     {
52546         if (this.frame.dom.style.display == 'none') {
52547             return Roo.form.FCKeditor.superclass.focus.call(this);
52548         }
52549         if(!this.el || !this.getEditor()) {
52550             this.focus.defer(100,this, [tag]); 
52551             return;
52552         }
52553         
52554         
52555         
52556         
52557         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52558         this.getEditor().Focus();
52559         if (tgs.length) {
52560             if (!this.getEditor().Selection.GetSelection()) {
52561                 this.focus.defer(100,this, [tag]); 
52562                 return;
52563             }
52564             
52565             
52566             var r = this.getEditor().EditorDocument.createRange();
52567             r.setStart(tgs[0],0);
52568             r.setEnd(tgs[0],0);
52569             this.getEditor().Selection.GetSelection().removeAllRanges();
52570             this.getEditor().Selection.GetSelection().addRange(r);
52571             this.getEditor().Focus();
52572         }
52573         
52574     },
52575     
52576     
52577     
52578     replaceTextarea : function()
52579     {
52580         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52581             return ;
52582         }
52583         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52584         //{
52585             // We must check the elements firstly using the Id and then the name.
52586         var oTextarea = document.getElementById( this.getId() );
52587         
52588         var colElementsByName = document.getElementsByName( this.getId() ) ;
52589          
52590         oTextarea.style.display = 'none' ;
52591
52592         if ( oTextarea.tabIndex ) {            
52593             this.TabIndex = oTextarea.tabIndex ;
52594         }
52595         
52596         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52597         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52598         this.frame = Roo.get(this.getId() + '___Frame')
52599     },
52600     
52601     _getConfigHtml : function()
52602     {
52603         var sConfig = '' ;
52604
52605         for ( var o in this.fckconfig ) {
52606             sConfig += sConfig.length > 0  ? '&amp;' : '';
52607             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52608         }
52609
52610         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52611     },
52612     
52613     
52614     _getIFrameHtml : function()
52615     {
52616         var sFile = 'fckeditor.html' ;
52617         /* no idea what this is about..
52618         try
52619         {
52620             if ( (/fcksource=true/i).test( window.top.location.search ) )
52621                 sFile = 'fckeditor.original.html' ;
52622         }
52623         catch (e) { 
52624         */
52625
52626         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52627         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52628         
52629         
52630         var html = '<iframe id="' + this.getId() +
52631             '___Frame" src="' + sLink +
52632             '" width="' + this.width +
52633             '" height="' + this.height + '"' +
52634             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52635             ' frameborder="0" scrolling="no"></iframe>' ;
52636
52637         return html ;
52638     },
52639     
52640     _insertHtmlBefore : function( html, element )
52641     {
52642         if ( element.insertAdjacentHTML )       {
52643             // IE
52644             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52645         } else { // Gecko
52646             var oRange = document.createRange() ;
52647             oRange.setStartBefore( element ) ;
52648             var oFragment = oRange.createContextualFragment( html );
52649             element.parentNode.insertBefore( oFragment, element ) ;
52650         }
52651     }
52652     
52653     
52654   
52655     
52656     
52657     
52658     
52659
52660 });
52661
52662 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52663
52664 function FCKeditor_OnComplete(editorInstance){
52665     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52666     f.fckEditor = editorInstance;
52667     //console.log("loaded");
52668     f.fireEvent('editorinit', f, editorInstance);
52669
52670   
52671
52672  
52673
52674
52675
52676
52677
52678
52679
52680
52681
52682
52683
52684
52685
52686
52687
52688 //<script type="text/javascript">
52689 /**
52690  * @class Roo.form.GridField
52691  * @extends Roo.form.Field
52692  * Embed a grid (or editable grid into a form)
52693  * STATUS ALPHA
52694  * 
52695  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52696  * it needs 
52697  * xgrid.store = Roo.data.Store
52698  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52699  * xgrid.store.reader = Roo.data.JsonReader 
52700  * 
52701  * 
52702  * @constructor
52703  * Creates a new GridField
52704  * @param {Object} config Configuration options
52705  */
52706 Roo.form.GridField = function(config){
52707     Roo.form.GridField.superclass.constructor.call(this, config);
52708      
52709 };
52710
52711 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52712     /**
52713      * @cfg {Number} width  - used to restrict width of grid..
52714      */
52715     width : 100,
52716     /**
52717      * @cfg {Number} height - used to restrict height of grid..
52718      */
52719     height : 50,
52720      /**
52721      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52722          * 
52723          *}
52724      */
52725     xgrid : false, 
52726     /**
52727      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52728      * {tag: "input", type: "checkbox", autocomplete: "off"})
52729      */
52730    // defaultAutoCreate : { tag: 'div' },
52731     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52732     /**
52733      * @cfg {String} addTitle Text to include for adding a title.
52734      */
52735     addTitle : false,
52736     //
52737     onResize : function(){
52738         Roo.form.Field.superclass.onResize.apply(this, arguments);
52739     },
52740
52741     initEvents : function(){
52742         // Roo.form.Checkbox.superclass.initEvents.call(this);
52743         // has no events...
52744        
52745     },
52746
52747
52748     getResizeEl : function(){
52749         return this.wrap;
52750     },
52751
52752     getPositionEl : function(){
52753         return this.wrap;
52754     },
52755
52756     // private
52757     onRender : function(ct, position){
52758         
52759         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52760         var style = this.style;
52761         delete this.style;
52762         
52763         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52764         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52765         this.viewEl = this.wrap.createChild({ tag: 'div' });
52766         if (style) {
52767             this.viewEl.applyStyles(style);
52768         }
52769         if (this.width) {
52770             this.viewEl.setWidth(this.width);
52771         }
52772         if (this.height) {
52773             this.viewEl.setHeight(this.height);
52774         }
52775         //if(this.inputValue !== undefined){
52776         //this.setValue(this.value);
52777         
52778         
52779         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52780         
52781         
52782         this.grid.render();
52783         this.grid.getDataSource().on('remove', this.refreshValue, this);
52784         this.grid.getDataSource().on('update', this.refreshValue, this);
52785         this.grid.on('afteredit', this.refreshValue, this);
52786  
52787     },
52788      
52789     
52790     /**
52791      * Sets the value of the item. 
52792      * @param {String} either an object  or a string..
52793      */
52794     setValue : function(v){
52795         //this.value = v;
52796         v = v || []; // empty set..
52797         // this does not seem smart - it really only affects memoryproxy grids..
52798         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52799             var ds = this.grid.getDataSource();
52800             // assumes a json reader..
52801             var data = {}
52802             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52803             ds.loadData( data);
52804         }
52805         // clear selection so it does not get stale.
52806         if (this.grid.sm) { 
52807             this.grid.sm.clearSelections();
52808         }
52809         
52810         Roo.form.GridField.superclass.setValue.call(this, v);
52811         this.refreshValue();
52812         // should load data in the grid really....
52813     },
52814     
52815     // private
52816     refreshValue: function() {
52817          var val = [];
52818         this.grid.getDataSource().each(function(r) {
52819             val.push(r.data);
52820         });
52821         this.el.dom.value = Roo.encode(val);
52822     }
52823     
52824      
52825     
52826     
52827 });/*
52828  * Based on:
52829  * Ext JS Library 1.1.1
52830  * Copyright(c) 2006-2007, Ext JS, LLC.
52831  *
52832  * Originally Released Under LGPL - original licence link has changed is not relivant.
52833  *
52834  * Fork - LGPL
52835  * <script type="text/javascript">
52836  */
52837 /**
52838  * @class Roo.form.DisplayField
52839  * @extends Roo.form.Field
52840  * A generic Field to display non-editable data.
52841  * @cfg {Boolean} closable (true|false) default false
52842  * @constructor
52843  * Creates a new Display Field item.
52844  * @param {Object} config Configuration options
52845  */
52846 Roo.form.DisplayField = function(config){
52847     Roo.form.DisplayField.superclass.constructor.call(this, config);
52848     
52849     this.addEvents({
52850         /**
52851          * @event close
52852          * Fires after the click the close btn
52853              * @param {Roo.form.DisplayField} this
52854              */
52855         close : true
52856     });
52857 };
52858
52859 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52860     inputType:      'hidden',
52861     allowBlank:     true,
52862     readOnly:         true,
52863     
52864  
52865     /**
52866      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52867      */
52868     focusClass : undefined,
52869     /**
52870      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52871      */
52872     fieldClass: 'x-form-field',
52873     
52874      /**
52875      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52876      */
52877     valueRenderer: undefined,
52878     
52879     width: 100,
52880     /**
52881      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52882      * {tag: "input", type: "checkbox", autocomplete: "off"})
52883      */
52884      
52885  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52886  
52887     closable : false,
52888     
52889     onResize : function(){
52890         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52891         
52892     },
52893
52894     initEvents : function(){
52895         // Roo.form.Checkbox.superclass.initEvents.call(this);
52896         // has no events...
52897         
52898         if(this.closable){
52899             this.closeEl.on('click', this.onClose, this);
52900         }
52901        
52902     },
52903
52904
52905     getResizeEl : function(){
52906         return this.wrap;
52907     },
52908
52909     getPositionEl : function(){
52910         return this.wrap;
52911     },
52912
52913     // private
52914     onRender : function(ct, position){
52915         
52916         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52917         //if(this.inputValue !== undefined){
52918         this.wrap = this.el.wrap();
52919         
52920         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52921         
52922         if(this.closable){
52923             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52924         }
52925         
52926         if (this.bodyStyle) {
52927             this.viewEl.applyStyles(this.bodyStyle);
52928         }
52929         //this.viewEl.setStyle('padding', '2px');
52930         
52931         this.setValue(this.value);
52932         
52933     },
52934 /*
52935     // private
52936     initValue : Roo.emptyFn,
52937
52938   */
52939
52940         // private
52941     onClick : function(){
52942         
52943     },
52944
52945     /**
52946      * Sets the checked state of the checkbox.
52947      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52948      */
52949     setValue : function(v){
52950         this.value = v;
52951         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52952         // this might be called before we have a dom element..
52953         if (!this.viewEl) {
52954             return;
52955         }
52956         this.viewEl.dom.innerHTML = html;
52957         Roo.form.DisplayField.superclass.setValue.call(this, v);
52958
52959     },
52960     
52961     onClose : function(e)
52962     {
52963         e.preventDefault();
52964         
52965         this.fireEvent('close', this);
52966     }
52967 });/*
52968  * 
52969  * Licence- LGPL
52970  * 
52971  */
52972
52973 /**
52974  * @class Roo.form.DayPicker
52975  * @extends Roo.form.Field
52976  * A Day picker show [M] [T] [W] ....
52977  * @constructor
52978  * Creates a new Day Picker
52979  * @param {Object} config Configuration options
52980  */
52981 Roo.form.DayPicker= function(config){
52982     Roo.form.DayPicker.superclass.constructor.call(this, config);
52983      
52984 };
52985
52986 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52987     /**
52988      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52989      */
52990     focusClass : undefined,
52991     /**
52992      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52993      */
52994     fieldClass: "x-form-field",
52995    
52996     /**
52997      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52998      * {tag: "input", type: "checkbox", autocomplete: "off"})
52999      */
53000     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
53001     
53002    
53003     actionMode : 'viewEl', 
53004     //
53005     // private
53006  
53007     inputType : 'hidden',
53008     
53009      
53010     inputElement: false, // real input element?
53011     basedOn: false, // ????
53012     
53013     isFormField: true, // not sure where this is needed!!!!
53014
53015     onResize : function(){
53016         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53017         if(!this.boxLabel){
53018             this.el.alignTo(this.wrap, 'c-c');
53019         }
53020     },
53021
53022     initEvents : function(){
53023         Roo.form.Checkbox.superclass.initEvents.call(this);
53024         this.el.on("click", this.onClick,  this);
53025         this.el.on("change", this.onClick,  this);
53026     },
53027
53028
53029     getResizeEl : function(){
53030         return this.wrap;
53031     },
53032
53033     getPositionEl : function(){
53034         return this.wrap;
53035     },
53036
53037     
53038     // private
53039     onRender : function(ct, position){
53040         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53041        
53042         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53043         
53044         var r1 = '<table><tr>';
53045         var r2 = '<tr class="x-form-daypick-icons">';
53046         for (var i=0; i < 7; i++) {
53047             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53048             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53049         }
53050         
53051         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53052         viewEl.select('img').on('click', this.onClick, this);
53053         this.viewEl = viewEl;   
53054         
53055         
53056         // this will not work on Chrome!!!
53057         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53058         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53059         
53060         
53061           
53062
53063     },
53064
53065     // private
53066     initValue : Roo.emptyFn,
53067
53068     /**
53069      * Returns the checked state of the checkbox.
53070      * @return {Boolean} True if checked, else false
53071      */
53072     getValue : function(){
53073         return this.el.dom.value;
53074         
53075     },
53076
53077         // private
53078     onClick : function(e){ 
53079         //this.setChecked(!this.checked);
53080         Roo.get(e.target).toggleClass('x-menu-item-checked');
53081         this.refreshValue();
53082         //if(this.el.dom.checked != this.checked){
53083         //    this.setValue(this.el.dom.checked);
53084        // }
53085     },
53086     
53087     // private
53088     refreshValue : function()
53089     {
53090         var val = '';
53091         this.viewEl.select('img',true).each(function(e,i,n)  {
53092             val += e.is(".x-menu-item-checked") ? String(n) : '';
53093         });
53094         this.setValue(val, true);
53095     },
53096
53097     /**
53098      * Sets the checked state of the checkbox.
53099      * On is always based on a string comparison between inputValue and the param.
53100      * @param {Boolean/String} value - the value to set 
53101      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53102      */
53103     setValue : function(v,suppressEvent){
53104         if (!this.el.dom) {
53105             return;
53106         }
53107         var old = this.el.dom.value ;
53108         this.el.dom.value = v;
53109         if (suppressEvent) {
53110             return ;
53111         }
53112          
53113         // update display..
53114         this.viewEl.select('img',true).each(function(e,i,n)  {
53115             
53116             var on = e.is(".x-menu-item-checked");
53117             var newv = v.indexOf(String(n)) > -1;
53118             if (on != newv) {
53119                 e.toggleClass('x-menu-item-checked');
53120             }
53121             
53122         });
53123         
53124         
53125         this.fireEvent('change', this, v, old);
53126         
53127         
53128     },
53129    
53130     // handle setting of hidden value by some other method!!?!?
53131     setFromHidden: function()
53132     {
53133         if(!this.el){
53134             return;
53135         }
53136         //console.log("SET FROM HIDDEN");
53137         //alert('setFrom hidden');
53138         this.setValue(this.el.dom.value);
53139     },
53140     
53141     onDestroy : function()
53142     {
53143         if(this.viewEl){
53144             Roo.get(this.viewEl).remove();
53145         }
53146          
53147         Roo.form.DayPicker.superclass.onDestroy.call(this);
53148     }
53149
53150 });/*
53151  * RooJS Library 1.1.1
53152  * Copyright(c) 2008-2011  Alan Knowles
53153  *
53154  * License - LGPL
53155  */
53156  
53157
53158 /**
53159  * @class Roo.form.ComboCheck
53160  * @extends Roo.form.ComboBox
53161  * A combobox for multiple select items.
53162  *
53163  * FIXME - could do with a reset button..
53164  * 
53165  * @constructor
53166  * Create a new ComboCheck
53167  * @param {Object} config Configuration options
53168  */
53169 Roo.form.ComboCheck = function(config){
53170     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53171     // should verify some data...
53172     // like
53173     // hiddenName = required..
53174     // displayField = required
53175     // valudField == required
53176     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53177     var _t = this;
53178     Roo.each(req, function(e) {
53179         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53180             throw "Roo.form.ComboCheck : missing value for: " + e;
53181         }
53182     });
53183     
53184     
53185 };
53186
53187 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53188      
53189      
53190     editable : false,
53191      
53192     selectedClass: 'x-menu-item-checked', 
53193     
53194     // private
53195     onRender : function(ct, position){
53196         var _t = this;
53197         
53198         
53199         
53200         if(!this.tpl){
53201             var cls = 'x-combo-list';
53202
53203             
53204             this.tpl =  new Roo.Template({
53205                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53206                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53207                    '<span>{' + this.displayField + '}</span>' +
53208                     '</div>' 
53209                 
53210             });
53211         }
53212  
53213         
53214         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53215         this.view.singleSelect = false;
53216         this.view.multiSelect = true;
53217         this.view.toggleSelect = true;
53218         this.pageTb.add(new Roo.Toolbar.Fill(), {
53219             
53220             text: 'Done',
53221             handler: function()
53222             {
53223                 _t.collapse();
53224             }
53225         });
53226     },
53227     
53228     onViewOver : function(e, t){
53229         // do nothing...
53230         return;
53231         
53232     },
53233     
53234     onViewClick : function(doFocus,index){
53235         return;
53236         
53237     },
53238     select: function () {
53239         //Roo.log("SELECT CALLED");
53240     },
53241      
53242     selectByValue : function(xv, scrollIntoView){
53243         var ar = this.getValueArray();
53244         var sels = [];
53245         
53246         Roo.each(ar, function(v) {
53247             if(v === undefined || v === null){
53248                 return;
53249             }
53250             var r = this.findRecord(this.valueField, v);
53251             if(r){
53252                 sels.push(this.store.indexOf(r))
53253                 
53254             }
53255         },this);
53256         this.view.select(sels);
53257         return false;
53258     },
53259     
53260     
53261     
53262     onSelect : function(record, index){
53263        // Roo.log("onselect Called");
53264        // this is only called by the clear button now..
53265         this.view.clearSelections();
53266         this.setValue('[]');
53267         if (this.value != this.valueBefore) {
53268             this.fireEvent('change', this, this.value, this.valueBefore);
53269             this.valueBefore = this.value;
53270         }
53271     },
53272     getValueArray : function()
53273     {
53274         var ar = [] ;
53275         
53276         try {
53277             //Roo.log(this.value);
53278             if (typeof(this.value) == 'undefined') {
53279                 return [];
53280             }
53281             var ar = Roo.decode(this.value);
53282             return  ar instanceof Array ? ar : []; //?? valid?
53283             
53284         } catch(e) {
53285             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53286             return [];
53287         }
53288          
53289     },
53290     expand : function ()
53291     {
53292         
53293         Roo.form.ComboCheck.superclass.expand.call(this);
53294         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53295         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53296         
53297
53298     },
53299     
53300     collapse : function(){
53301         Roo.form.ComboCheck.superclass.collapse.call(this);
53302         var sl = this.view.getSelectedIndexes();
53303         var st = this.store;
53304         var nv = [];
53305         var tv = [];
53306         var r;
53307         Roo.each(sl, function(i) {
53308             r = st.getAt(i);
53309             nv.push(r.get(this.valueField));
53310         },this);
53311         this.setValue(Roo.encode(nv));
53312         if (this.value != this.valueBefore) {
53313
53314             this.fireEvent('change', this, this.value, this.valueBefore);
53315             this.valueBefore = this.value;
53316         }
53317         
53318     },
53319     
53320     setValue : function(v){
53321         // Roo.log(v);
53322         this.value = v;
53323         
53324         var vals = this.getValueArray();
53325         var tv = [];
53326         Roo.each(vals, function(k) {
53327             var r = this.findRecord(this.valueField, k);
53328             if(r){
53329                 tv.push(r.data[this.displayField]);
53330             }else if(this.valueNotFoundText !== undefined){
53331                 tv.push( this.valueNotFoundText );
53332             }
53333         },this);
53334        // Roo.log(tv);
53335         
53336         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53337         this.hiddenField.value = v;
53338         this.value = v;
53339     }
53340     
53341 });/*
53342  * Based on:
53343  * Ext JS Library 1.1.1
53344  * Copyright(c) 2006-2007, Ext JS, LLC.
53345  *
53346  * Originally Released Under LGPL - original licence link has changed is not relivant.
53347  *
53348  * Fork - LGPL
53349  * <script type="text/javascript">
53350  */
53351  
53352 /**
53353  * @class Roo.form.Signature
53354  * @extends Roo.form.Field
53355  * Signature field.  
53356  * @constructor
53357  * 
53358  * @param {Object} config Configuration options
53359  */
53360
53361 Roo.form.Signature = function(config){
53362     Roo.form.Signature.superclass.constructor.call(this, config);
53363     
53364     this.addEvents({// not in used??
53365          /**
53366          * @event confirm
53367          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53368              * @param {Roo.form.Signature} combo This combo box
53369              */
53370         'confirm' : true,
53371         /**
53372          * @event reset
53373          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53374              * @param {Roo.form.ComboBox} combo This combo box
53375              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53376              */
53377         'reset' : true
53378     });
53379 };
53380
53381 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53382     /**
53383      * @cfg {Object} labels Label to use when rendering a form.
53384      * defaults to 
53385      * labels : { 
53386      *      clear : "Clear",
53387      *      confirm : "Confirm"
53388      *  }
53389      */
53390     labels : { 
53391         clear : "Clear",
53392         confirm : "Confirm"
53393     },
53394     /**
53395      * @cfg {Number} width The signature panel width (defaults to 300)
53396      */
53397     width: 300,
53398     /**
53399      * @cfg {Number} height The signature panel height (defaults to 100)
53400      */
53401     height : 100,
53402     /**
53403      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53404      */
53405     allowBlank : false,
53406     
53407     //private
53408     // {Object} signPanel The signature SVG panel element (defaults to {})
53409     signPanel : {},
53410     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53411     isMouseDown : false,
53412     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53413     isConfirmed : false,
53414     // {String} signatureTmp SVG mapping string (defaults to empty string)
53415     signatureTmp : '',
53416     
53417     
53418     defaultAutoCreate : { // modified by initCompnoent..
53419         tag: "input",
53420         type:"hidden"
53421     },
53422
53423     // private
53424     onRender : function(ct, position){
53425         
53426         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53427         
53428         this.wrap = this.el.wrap({
53429             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53430         });
53431         
53432         this.createToolbar(this);
53433         this.signPanel = this.wrap.createChild({
53434                 tag: 'div',
53435                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53436             }, this.el
53437         );
53438             
53439         this.svgID = Roo.id();
53440         this.svgEl = this.signPanel.createChild({
53441               xmlns : 'http://www.w3.org/2000/svg',
53442               tag : 'svg',
53443               id : this.svgID + "-svg",
53444               width: this.width,
53445               height: this.height,
53446               viewBox: '0 0 '+this.width+' '+this.height,
53447               cn : [
53448                 {
53449                     tag: "rect",
53450                     id: this.svgID + "-svg-r",
53451                     width: this.width,
53452                     height: this.height,
53453                     fill: "#ffa"
53454                 },
53455                 {
53456                     tag: "line",
53457                     id: this.svgID + "-svg-l",
53458                     x1: "0", // start
53459                     y1: (this.height*0.8), // start set the line in 80% of height
53460                     x2: this.width, // end
53461                     y2: (this.height*0.8), // end set the line in 80% of height
53462                     'stroke': "#666",
53463                     'stroke-width': "1",
53464                     'stroke-dasharray': "3",
53465                     'shape-rendering': "crispEdges",
53466                     'pointer-events': "none"
53467                 },
53468                 {
53469                     tag: "path",
53470                     id: this.svgID + "-svg-p",
53471                     'stroke': "navy",
53472                     'stroke-width': "3",
53473                     'fill': "none",
53474                     'pointer-events': 'none'
53475                 }
53476               ]
53477         });
53478         this.createSVG();
53479         this.svgBox = this.svgEl.dom.getScreenCTM();
53480     },
53481     createSVG : function(){ 
53482         var svg = this.signPanel;
53483         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53484         var t = this;
53485
53486         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53487         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53488         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53489         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53490         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53491         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53492         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53493         
53494     },
53495     isTouchEvent : function(e){
53496         return e.type.match(/^touch/);
53497     },
53498     getCoords : function (e) {
53499         var pt    = this.svgEl.dom.createSVGPoint();
53500         pt.x = e.clientX; 
53501         pt.y = e.clientY;
53502         if (this.isTouchEvent(e)) {
53503             pt.x =  e.targetTouches[0].clientX;
53504             pt.y = e.targetTouches[0].clientY;
53505         }
53506         var a = this.svgEl.dom.getScreenCTM();
53507         var b = a.inverse();
53508         var mx = pt.matrixTransform(b);
53509         return mx.x + ',' + mx.y;
53510     },
53511     //mouse event headler 
53512     down : function (e) {
53513         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53514         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53515         
53516         this.isMouseDown = true;
53517         
53518         e.preventDefault();
53519     },
53520     move : function (e) {
53521         if (this.isMouseDown) {
53522             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53523             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53524         }
53525         
53526         e.preventDefault();
53527     },
53528     up : function (e) {
53529         this.isMouseDown = false;
53530         var sp = this.signatureTmp.split(' ');
53531         
53532         if(sp.length > 1){
53533             if(!sp[sp.length-2].match(/^L/)){
53534                 sp.pop();
53535                 sp.pop();
53536                 sp.push("");
53537                 this.signatureTmp = sp.join(" ");
53538             }
53539         }
53540         if(this.getValue() != this.signatureTmp){
53541             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53542             this.isConfirmed = false;
53543         }
53544         e.preventDefault();
53545     },
53546     
53547     /**
53548      * Protected method that will not generally be called directly. It
53549      * is called when the editor creates its toolbar. Override this method if you need to
53550      * add custom toolbar buttons.
53551      * @param {HtmlEditor} editor
53552      */
53553     createToolbar : function(editor){
53554          function btn(id, toggle, handler){
53555             var xid = fid + '-'+ id ;
53556             return {
53557                 id : xid,
53558                 cmd : id,
53559                 cls : 'x-btn-icon x-edit-'+id,
53560                 enableToggle:toggle !== false,
53561                 scope: editor, // was editor...
53562                 handler:handler||editor.relayBtnCmd,
53563                 clickEvent:'mousedown',
53564                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53565                 tabIndex:-1
53566             };
53567         }
53568         
53569         
53570         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53571         this.tb = tb;
53572         this.tb.add(
53573            {
53574                 cls : ' x-signature-btn x-signature-'+id,
53575                 scope: editor, // was editor...
53576                 handler: this.reset,
53577                 clickEvent:'mousedown',
53578                 text: this.labels.clear
53579             },
53580             {
53581                  xtype : 'Fill',
53582                  xns: Roo.Toolbar
53583             }, 
53584             {
53585                 cls : '  x-signature-btn x-signature-'+id,
53586                 scope: editor, // was editor...
53587                 handler: this.confirmHandler,
53588                 clickEvent:'mousedown',
53589                 text: this.labels.confirm
53590             }
53591         );
53592     
53593     },
53594     //public
53595     /**
53596      * when user is clicked confirm then show this image.....
53597      * 
53598      * @return {String} Image Data URI
53599      */
53600     getImageDataURI : function(){
53601         var svg = this.svgEl.dom.parentNode.innerHTML;
53602         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53603         return src; 
53604     },
53605     /**
53606      * 
53607      * @return {Boolean} this.isConfirmed
53608      */
53609     getConfirmed : function(){
53610         return this.isConfirmed;
53611     },
53612     /**
53613      * 
53614      * @return {Number} this.width
53615      */
53616     getWidth : function(){
53617         return this.width;
53618     },
53619     /**
53620      * 
53621      * @return {Number} this.height
53622      */
53623     getHeight : function(){
53624         return this.height;
53625     },
53626     // private
53627     getSignature : function(){
53628         return this.signatureTmp;
53629     },
53630     // private
53631     reset : function(){
53632         this.signatureTmp = '';
53633         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53634         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53635         this.isConfirmed = false;
53636         Roo.form.Signature.superclass.reset.call(this);
53637     },
53638     setSignature : function(s){
53639         this.signatureTmp = s;
53640         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53641         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53642         this.setValue(s);
53643         this.isConfirmed = false;
53644         Roo.form.Signature.superclass.reset.call(this);
53645     }, 
53646     test : function(){
53647 //        Roo.log(this.signPanel.dom.contentWindow.up())
53648     },
53649     //private
53650     setConfirmed : function(){
53651         
53652         
53653         
53654 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53655     },
53656     // private
53657     confirmHandler : function(){
53658         if(!this.getSignature()){
53659             return;
53660         }
53661         
53662         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53663         this.setValue(this.getSignature());
53664         this.isConfirmed = true;
53665         
53666         this.fireEvent('confirm', this);
53667     },
53668     // private
53669     // Subclasses should provide the validation implementation by overriding this
53670     validateValue : function(value){
53671         if(this.allowBlank){
53672             return true;
53673         }
53674         
53675         if(this.isConfirmed){
53676             return true;
53677         }
53678         return false;
53679     }
53680 });/*
53681  * Based on:
53682  * Ext JS Library 1.1.1
53683  * Copyright(c) 2006-2007, Ext JS, LLC.
53684  *
53685  * Originally Released Under LGPL - original licence link has changed is not relivant.
53686  *
53687  * Fork - LGPL
53688  * <script type="text/javascript">
53689  */
53690  
53691
53692 /**
53693  * @class Roo.form.ComboBox
53694  * @extends Roo.form.TriggerField
53695  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53696  * @constructor
53697  * Create a new ComboBox.
53698  * @param {Object} config Configuration options
53699  */
53700 Roo.form.Select = function(config){
53701     Roo.form.Select.superclass.constructor.call(this, config);
53702      
53703 };
53704
53705 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53706     /**
53707      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53708      */
53709     /**
53710      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53711      * rendering into an Roo.Editor, defaults to false)
53712      */
53713     /**
53714      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53715      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53716      */
53717     /**
53718      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53719      */
53720     /**
53721      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53722      * the dropdown list (defaults to undefined, with no header element)
53723      */
53724
53725      /**
53726      * @cfg {String/Roo.Template} tpl The template to use to render the output
53727      */
53728      
53729     // private
53730     defaultAutoCreate : {tag: "select"  },
53731     /**
53732      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53733      */
53734     listWidth: undefined,
53735     /**
53736      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53737      * mode = 'remote' or 'text' if mode = 'local')
53738      */
53739     displayField: undefined,
53740     /**
53741      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53742      * mode = 'remote' or 'value' if mode = 'local'). 
53743      * Note: use of a valueField requires the user make a selection
53744      * in order for a value to be mapped.
53745      */
53746     valueField: undefined,
53747     
53748     
53749     /**
53750      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53751      * field's data value (defaults to the underlying DOM element's name)
53752      */
53753     hiddenName: undefined,
53754     /**
53755      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53756      */
53757     listClass: '',
53758     /**
53759      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53760      */
53761     selectedClass: 'x-combo-selected',
53762     /**
53763      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53764      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53765      * which displays a downward arrow icon).
53766      */
53767     triggerClass : 'x-form-arrow-trigger',
53768     /**
53769      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53770      */
53771     shadow:'sides',
53772     /**
53773      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53774      * anchor positions (defaults to 'tl-bl')
53775      */
53776     listAlign: 'tl-bl?',
53777     /**
53778      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53779      */
53780     maxHeight: 300,
53781     /**
53782      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53783      * query specified by the allQuery config option (defaults to 'query')
53784      */
53785     triggerAction: 'query',
53786     /**
53787      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53788      * (defaults to 4, does not apply if editable = false)
53789      */
53790     minChars : 4,
53791     /**
53792      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53793      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53794      */
53795     typeAhead: false,
53796     /**
53797      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53798      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53799      */
53800     queryDelay: 500,
53801     /**
53802      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53803      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53804      */
53805     pageSize: 0,
53806     /**
53807      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53808      * when editable = true (defaults to false)
53809      */
53810     selectOnFocus:false,
53811     /**
53812      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53813      */
53814     queryParam: 'query',
53815     /**
53816      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53817      * when mode = 'remote' (defaults to 'Loading...')
53818      */
53819     loadingText: 'Loading...',
53820     /**
53821      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53822      */
53823     resizable: false,
53824     /**
53825      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53826      */
53827     handleHeight : 8,
53828     /**
53829      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53830      * traditional select (defaults to true)
53831      */
53832     editable: true,
53833     /**
53834      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53835      */
53836     allQuery: '',
53837     /**
53838      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53839      */
53840     mode: 'remote',
53841     /**
53842      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53843      * listWidth has a higher value)
53844      */
53845     minListWidth : 70,
53846     /**
53847      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53848      * allow the user to set arbitrary text into the field (defaults to false)
53849      */
53850     forceSelection:false,
53851     /**
53852      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53853      * if typeAhead = true (defaults to 250)
53854      */
53855     typeAheadDelay : 250,
53856     /**
53857      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53858      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53859      */
53860     valueNotFoundText : undefined,
53861     
53862     /**
53863      * @cfg {String} defaultValue The value displayed after loading the store.
53864      */
53865     defaultValue: '',
53866     
53867     /**
53868      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53869      */
53870     blockFocus : false,
53871     
53872     /**
53873      * @cfg {Boolean} disableClear Disable showing of clear button.
53874      */
53875     disableClear : false,
53876     /**
53877      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53878      */
53879     alwaysQuery : false,
53880     
53881     //private
53882     addicon : false,
53883     editicon: false,
53884     
53885     // element that contains real text value.. (when hidden is used..)
53886      
53887     // private
53888     onRender : function(ct, position){
53889         Roo.form.Field.prototype.onRender.call(this, ct, position);
53890         
53891         if(this.store){
53892             this.store.on('beforeload', this.onBeforeLoad, this);
53893             this.store.on('load', this.onLoad, this);
53894             this.store.on('loadexception', this.onLoadException, this);
53895             this.store.load({});
53896         }
53897         
53898         
53899         
53900     },
53901
53902     // private
53903     initEvents : function(){
53904         //Roo.form.ComboBox.superclass.initEvents.call(this);
53905  
53906     },
53907
53908     onDestroy : function(){
53909        
53910         if(this.store){
53911             this.store.un('beforeload', this.onBeforeLoad, this);
53912             this.store.un('load', this.onLoad, this);
53913             this.store.un('loadexception', this.onLoadException, this);
53914         }
53915         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53916     },
53917
53918     // private
53919     fireKey : function(e){
53920         if(e.isNavKeyPress() && !this.list.isVisible()){
53921             this.fireEvent("specialkey", this, e);
53922         }
53923     },
53924
53925     // private
53926     onResize: function(w, h){
53927         
53928         return; 
53929     
53930         
53931     },
53932
53933     /**
53934      * Allow or prevent the user from directly editing the field text.  If false is passed,
53935      * the user will only be able to select from the items defined in the dropdown list.  This method
53936      * is the runtime equivalent of setting the 'editable' config option at config time.
53937      * @param {Boolean} value True to allow the user to directly edit the field text
53938      */
53939     setEditable : function(value){
53940          
53941     },
53942
53943     // private
53944     onBeforeLoad : function(){
53945         
53946         Roo.log("Select before load");
53947         return;
53948     
53949         this.innerList.update(this.loadingText ?
53950                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53951         //this.restrictHeight();
53952         this.selectedIndex = -1;
53953     },
53954
53955     // private
53956     onLoad : function(){
53957
53958     
53959         var dom = this.el.dom;
53960         dom.innerHTML = '';
53961          var od = dom.ownerDocument;
53962          
53963         if (this.emptyText) {
53964             var op = od.createElement('option');
53965             op.setAttribute('value', '');
53966             op.innerHTML = String.format('{0}', this.emptyText);
53967             dom.appendChild(op);
53968         }
53969         if(this.store.getCount() > 0){
53970            
53971             var vf = this.valueField;
53972             var df = this.displayField;
53973             this.store.data.each(function(r) {
53974                 // which colmsn to use... testing - cdoe / title..
53975                 var op = od.createElement('option');
53976                 op.setAttribute('value', r.data[vf]);
53977                 op.innerHTML = String.format('{0}', r.data[df]);
53978                 dom.appendChild(op);
53979             });
53980             if (typeof(this.defaultValue != 'undefined')) {
53981                 this.setValue(this.defaultValue);
53982             }
53983             
53984              
53985         }else{
53986             //this.onEmptyResults();
53987         }
53988         //this.el.focus();
53989     },
53990     // private
53991     onLoadException : function()
53992     {
53993         dom.innerHTML = '';
53994             
53995         Roo.log("Select on load exception");
53996         return;
53997     
53998         this.collapse();
53999         Roo.log(this.store.reader.jsonData);
54000         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54001             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54002         }
54003         
54004         
54005     },
54006     // private
54007     onTypeAhead : function(){
54008          
54009     },
54010
54011     // private
54012     onSelect : function(record, index){
54013         Roo.log('on select?');
54014         return;
54015         if(this.fireEvent('beforeselect', this, record, index) !== false){
54016             this.setFromData(index > -1 ? record.data : false);
54017             this.collapse();
54018             this.fireEvent('select', this, record, index);
54019         }
54020     },
54021
54022     /**
54023      * Returns the currently selected field value or empty string if no value is set.
54024      * @return {String} value The selected value
54025      */
54026     getValue : function(){
54027         var dom = this.el.dom;
54028         this.value = dom.options[dom.selectedIndex].value;
54029         return this.value;
54030         
54031     },
54032
54033     /**
54034      * Clears any text/value currently set in the field
54035      */
54036     clearValue : function(){
54037         this.value = '';
54038         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54039         
54040     },
54041
54042     /**
54043      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54044      * will be displayed in the field.  If the value does not match the data value of an existing item,
54045      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54046      * Otherwise the field will be blank (although the value will still be set).
54047      * @param {String} value The value to match
54048      */
54049     setValue : function(v){
54050         var d = this.el.dom;
54051         for (var i =0; i < d.options.length;i++) {
54052             if (v == d.options[i].value) {
54053                 d.selectedIndex = i;
54054                 this.value = v;
54055                 return;
54056             }
54057         }
54058         this.clearValue();
54059     },
54060     /**
54061      * @property {Object} the last set data for the element
54062      */
54063     
54064     lastData : false,
54065     /**
54066      * Sets the value of the field based on a object which is related to the record format for the store.
54067      * @param {Object} value the value to set as. or false on reset?
54068      */
54069     setFromData : function(o){
54070         Roo.log('setfrom data?');
54071          
54072         
54073         
54074     },
54075     // private
54076     reset : function(){
54077         this.clearValue();
54078     },
54079     // private
54080     findRecord : function(prop, value){
54081         
54082         return false;
54083     
54084         var record;
54085         if(this.store.getCount() > 0){
54086             this.store.each(function(r){
54087                 if(r.data[prop] == value){
54088                     record = r;
54089                     return false;
54090                 }
54091                 return true;
54092             });
54093         }
54094         return record;
54095     },
54096     
54097     getName: function()
54098     {
54099         // returns hidden if it's set..
54100         if (!this.rendered) {return ''};
54101         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54102         
54103     },
54104      
54105
54106     
54107
54108     // private
54109     onEmptyResults : function(){
54110         Roo.log('empty results');
54111         //this.collapse();
54112     },
54113
54114     /**
54115      * Returns true if the dropdown list is expanded, else false.
54116      */
54117     isExpanded : function(){
54118         return false;
54119     },
54120
54121     /**
54122      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54123      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54124      * @param {String} value The data value of the item to select
54125      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54126      * selected item if it is not currently in view (defaults to true)
54127      * @return {Boolean} True if the value matched an item in the list, else false
54128      */
54129     selectByValue : function(v, scrollIntoView){
54130         Roo.log('select By Value');
54131         return false;
54132     
54133         if(v !== undefined && v !== null){
54134             var r = this.findRecord(this.valueField || this.displayField, v);
54135             if(r){
54136                 this.select(this.store.indexOf(r), scrollIntoView);
54137                 return true;
54138             }
54139         }
54140         return false;
54141     },
54142
54143     /**
54144      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54145      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54146      * @param {Number} index The zero-based index of the list item to select
54147      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54148      * selected item if it is not currently in view (defaults to true)
54149      */
54150     select : function(index, scrollIntoView){
54151         Roo.log('select ');
54152         return  ;
54153         
54154         this.selectedIndex = index;
54155         this.view.select(index);
54156         if(scrollIntoView !== false){
54157             var el = this.view.getNode(index);
54158             if(el){
54159                 this.innerList.scrollChildIntoView(el, false);
54160             }
54161         }
54162     },
54163
54164       
54165
54166     // private
54167     validateBlur : function(){
54168         
54169         return;
54170         
54171     },
54172
54173     // private
54174     initQuery : function(){
54175         this.doQuery(this.getRawValue());
54176     },
54177
54178     // private
54179     doForce : function(){
54180         if(this.el.dom.value.length > 0){
54181             this.el.dom.value =
54182                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54183              
54184         }
54185     },
54186
54187     /**
54188      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54189      * query allowing the query action to be canceled if needed.
54190      * @param {String} query The SQL query to execute
54191      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54192      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54193      * saved in the current store (defaults to false)
54194      */
54195     doQuery : function(q, forceAll){
54196         
54197         Roo.log('doQuery?');
54198         if(q === undefined || q === null){
54199             q = '';
54200         }
54201         var qe = {
54202             query: q,
54203             forceAll: forceAll,
54204             combo: this,
54205             cancel:false
54206         };
54207         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54208             return false;
54209         }
54210         q = qe.query;
54211         forceAll = qe.forceAll;
54212         if(forceAll === true || (q.length >= this.minChars)){
54213             if(this.lastQuery != q || this.alwaysQuery){
54214                 this.lastQuery = q;
54215                 if(this.mode == 'local'){
54216                     this.selectedIndex = -1;
54217                     if(forceAll){
54218                         this.store.clearFilter();
54219                     }else{
54220                         this.store.filter(this.displayField, q);
54221                     }
54222                     this.onLoad();
54223                 }else{
54224                     this.store.baseParams[this.queryParam] = q;
54225                     this.store.load({
54226                         params: this.getParams(q)
54227                     });
54228                     this.expand();
54229                 }
54230             }else{
54231                 this.selectedIndex = -1;
54232                 this.onLoad();   
54233             }
54234         }
54235     },
54236
54237     // private
54238     getParams : function(q){
54239         var p = {};
54240         //p[this.queryParam] = q;
54241         if(this.pageSize){
54242             p.start = 0;
54243             p.limit = this.pageSize;
54244         }
54245         return p;
54246     },
54247
54248     /**
54249      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54250      */
54251     collapse : function(){
54252         
54253     },
54254
54255     // private
54256     collapseIf : function(e){
54257         
54258     },
54259
54260     /**
54261      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54262      */
54263     expand : function(){
54264         
54265     } ,
54266
54267     // private
54268      
54269
54270     /** 
54271     * @cfg {Boolean} grow 
54272     * @hide 
54273     */
54274     /** 
54275     * @cfg {Number} growMin 
54276     * @hide 
54277     */
54278     /** 
54279     * @cfg {Number} growMax 
54280     * @hide 
54281     */
54282     /**
54283      * @hide
54284      * @method autoSize
54285      */
54286     
54287     setWidth : function()
54288     {
54289         
54290     },
54291     getResizeEl : function(){
54292         return this.el;
54293     }
54294 });//<script type="text/javasscript">
54295  
54296
54297 /**
54298  * @class Roo.DDView
54299  * A DnD enabled version of Roo.View.
54300  * @param {Element/String} container The Element in which to create the View.
54301  * @param {String} tpl The template string used to create the markup for each element of the View
54302  * @param {Object} config The configuration properties. These include all the config options of
54303  * {@link Roo.View} plus some specific to this class.<br>
54304  * <p>
54305  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54306  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54307  * <p>
54308  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54309 .x-view-drag-insert-above {
54310         border-top:1px dotted #3366cc;
54311 }
54312 .x-view-drag-insert-below {
54313         border-bottom:1px dotted #3366cc;
54314 }
54315 </code></pre>
54316  * 
54317  */
54318  
54319 Roo.DDView = function(container, tpl, config) {
54320     Roo.DDView.superclass.constructor.apply(this, arguments);
54321     this.getEl().setStyle("outline", "0px none");
54322     this.getEl().unselectable();
54323     if (this.dragGroup) {
54324         this.setDraggable(this.dragGroup.split(","));
54325     }
54326     if (this.dropGroup) {
54327         this.setDroppable(this.dropGroup.split(","));
54328     }
54329     if (this.deletable) {
54330         this.setDeletable();
54331     }
54332     this.isDirtyFlag = false;
54333         this.addEvents({
54334                 "drop" : true
54335         });
54336 };
54337
54338 Roo.extend(Roo.DDView, Roo.View, {
54339 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54340 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54341 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54342 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54343
54344         isFormField: true,
54345
54346         reset: Roo.emptyFn,
54347         
54348         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54349
54350         validate: function() {
54351                 return true;
54352         },
54353         
54354         destroy: function() {
54355                 this.purgeListeners();
54356                 this.getEl.removeAllListeners();
54357                 this.getEl().remove();
54358                 if (this.dragZone) {
54359                         if (this.dragZone.destroy) {
54360                                 this.dragZone.destroy();
54361                         }
54362                 }
54363                 if (this.dropZone) {
54364                         if (this.dropZone.destroy) {
54365                                 this.dropZone.destroy();
54366                         }
54367                 }
54368         },
54369
54370 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54371         getName: function() {
54372                 return this.name;
54373         },
54374
54375 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54376         setValue: function(v) {
54377                 if (!this.store) {
54378                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54379                 }
54380                 var data = {};
54381                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54382                 this.store.proxy = new Roo.data.MemoryProxy(data);
54383                 this.store.load();
54384         },
54385
54386 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54387         getValue: function() {
54388                 var result = '(';
54389                 this.store.each(function(rec) {
54390                         result += rec.id + ',';
54391                 });
54392                 return result.substr(0, result.length - 1) + ')';
54393         },
54394         
54395         getIds: function() {
54396                 var i = 0, result = new Array(this.store.getCount());
54397                 this.store.each(function(rec) {
54398                         result[i++] = rec.id;
54399                 });
54400                 return result;
54401         },
54402         
54403         isDirty: function() {
54404                 return this.isDirtyFlag;
54405         },
54406
54407 /**
54408  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54409  *      whole Element becomes the target, and this causes the drop gesture to append.
54410  */
54411     getTargetFromEvent : function(e) {
54412                 var target = e.getTarget();
54413                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54414                 target = target.parentNode;
54415                 }
54416                 if (!target) {
54417                         target = this.el.dom.lastChild || this.el.dom;
54418                 }
54419                 return target;
54420     },
54421
54422 /**
54423  *      Create the drag data which consists of an object which has the property "ddel" as
54424  *      the drag proxy element. 
54425  */
54426     getDragData : function(e) {
54427         var target = this.findItemFromChild(e.getTarget());
54428                 if(target) {
54429                         this.handleSelection(e);
54430                         var selNodes = this.getSelectedNodes();
54431             var dragData = {
54432                 source: this,
54433                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54434                 nodes: selNodes,
54435                 records: []
54436                         };
54437                         var selectedIndices = this.getSelectedIndexes();
54438                         for (var i = 0; i < selectedIndices.length; i++) {
54439                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54440                         }
54441                         if (selNodes.length == 1) {
54442                                 dragData.ddel = target.cloneNode(true); // the div element
54443                         } else {
54444                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54445                                 div.className = 'multi-proxy';
54446                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54447                                         div.appendChild(selNodes[i].cloneNode(true));
54448                                 }
54449                                 dragData.ddel = div;
54450                         }
54451             //console.log(dragData)
54452             //console.log(dragData.ddel.innerHTML)
54453                         return dragData;
54454                 }
54455         //console.log('nodragData')
54456                 return false;
54457     },
54458     
54459 /**     Specify to which ddGroup items in this DDView may be dragged. */
54460     setDraggable: function(ddGroup) {
54461         if (ddGroup instanceof Array) {
54462                 Roo.each(ddGroup, this.setDraggable, this);
54463                 return;
54464         }
54465         if (this.dragZone) {
54466                 this.dragZone.addToGroup(ddGroup);
54467         } else {
54468                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54469                                 containerScroll: true,
54470                                 ddGroup: ddGroup 
54471
54472                         });
54473 //                      Draggability implies selection. DragZone's mousedown selects the element.
54474                         if (!this.multiSelect) { this.singleSelect = true; }
54475
54476 //                      Wire the DragZone's handlers up to methods in *this*
54477                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54478                 }
54479     },
54480
54481 /**     Specify from which ddGroup this DDView accepts drops. */
54482     setDroppable: function(ddGroup) {
54483         if (ddGroup instanceof Array) {
54484                 Roo.each(ddGroup, this.setDroppable, this);
54485                 return;
54486         }
54487         if (this.dropZone) {
54488                 this.dropZone.addToGroup(ddGroup);
54489         } else {
54490                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54491                                 containerScroll: true,
54492                                 ddGroup: ddGroup
54493                         });
54494
54495 //                      Wire the DropZone's handlers up to methods in *this*
54496                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54497                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54498                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54499                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54500                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54501                 }
54502     },
54503
54504 /**     Decide whether to drop above or below a View node. */
54505     getDropPoint : function(e, n, dd){
54506         if (n == this.el.dom) { return "above"; }
54507                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54508                 var c = t + (b - t) / 2;
54509                 var y = Roo.lib.Event.getPageY(e);
54510                 if(y <= c) {
54511                         return "above";
54512                 }else{
54513                         return "below";
54514                 }
54515     },
54516
54517     onNodeEnter : function(n, dd, e, data){
54518                 return false;
54519     },
54520     
54521     onNodeOver : function(n, dd, e, data){
54522                 var pt = this.getDropPoint(e, n, dd);
54523                 // set the insert point style on the target node
54524                 var dragElClass = this.dropNotAllowed;
54525                 if (pt) {
54526                         var targetElClass;
54527                         if (pt == "above"){
54528                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54529                                 targetElClass = "x-view-drag-insert-above";
54530                         } else {
54531                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54532                                 targetElClass = "x-view-drag-insert-below";
54533                         }
54534                         if (this.lastInsertClass != targetElClass){
54535                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54536                                 this.lastInsertClass = targetElClass;
54537                         }
54538                 }
54539                 return dragElClass;
54540         },
54541
54542     onNodeOut : function(n, dd, e, data){
54543                 this.removeDropIndicators(n);
54544     },
54545
54546     onNodeDrop : function(n, dd, e, data){
54547         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54548                 return false;
54549         }
54550         var pt = this.getDropPoint(e, n, dd);
54551                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54552                 if (pt == "below") { insertAt++; }
54553                 for (var i = 0; i < data.records.length; i++) {
54554                         var r = data.records[i];
54555                         var dup = this.store.getById(r.id);
54556                         if (dup && (dd != this.dragZone)) {
54557                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54558                         } else {
54559                                 if (data.copy) {
54560                                         this.store.insert(insertAt++, r.copy());
54561                                 } else {
54562                                         data.source.isDirtyFlag = true;
54563                                         r.store.remove(r);
54564                                         this.store.insert(insertAt++, r);
54565                                 }
54566                                 this.isDirtyFlag = true;
54567                         }
54568                 }
54569                 this.dragZone.cachedTarget = null;
54570                 return true;
54571     },
54572
54573     removeDropIndicators : function(n){
54574                 if(n){
54575                         Roo.fly(n).removeClass([
54576                                 "x-view-drag-insert-above",
54577                                 "x-view-drag-insert-below"]);
54578                         this.lastInsertClass = "_noclass";
54579                 }
54580     },
54581
54582 /**
54583  *      Utility method. Add a delete option to the DDView's context menu.
54584  *      @param {String} imageUrl The URL of the "delete" icon image.
54585  */
54586         setDeletable: function(imageUrl) {
54587                 if (!this.singleSelect && !this.multiSelect) {
54588                         this.singleSelect = true;
54589                 }
54590                 var c = this.getContextMenu();
54591                 this.contextMenu.on("itemclick", function(item) {
54592                         switch (item.id) {
54593                                 case "delete":
54594                                         this.remove(this.getSelectedIndexes());
54595                                         break;
54596                         }
54597                 }, this);
54598                 this.contextMenu.add({
54599                         icon: imageUrl,
54600                         id: "delete",
54601                         text: 'Delete'
54602                 });
54603         },
54604         
54605 /**     Return the context menu for this DDView. */
54606         getContextMenu: function() {
54607                 if (!this.contextMenu) {
54608 //                      Create the View's context menu
54609                         this.contextMenu = new Roo.menu.Menu({
54610                                 id: this.id + "-contextmenu"
54611                         });
54612                         this.el.on("contextmenu", this.showContextMenu, this);
54613                 }
54614                 return this.contextMenu;
54615         },
54616         
54617         disableContextMenu: function() {
54618                 if (this.contextMenu) {
54619                         this.el.un("contextmenu", this.showContextMenu, this);
54620                 }
54621         },
54622
54623         showContextMenu: function(e, item) {
54624         item = this.findItemFromChild(e.getTarget());
54625                 if (item) {
54626                         e.stopEvent();
54627                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54628                         this.contextMenu.showAt(e.getXY());
54629             }
54630     },
54631
54632 /**
54633  *      Remove {@link Roo.data.Record}s at the specified indices.
54634  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54635  */
54636     remove: function(selectedIndices) {
54637                 selectedIndices = [].concat(selectedIndices);
54638                 for (var i = 0; i < selectedIndices.length; i++) {
54639                         var rec = this.store.getAt(selectedIndices[i]);
54640                         this.store.remove(rec);
54641                 }
54642     },
54643
54644 /**
54645  *      Double click fires the event, but also, if this is draggable, and there is only one other
54646  *      related DropZone, it transfers the selected node.
54647  */
54648     onDblClick : function(e){
54649         var item = this.findItemFromChild(e.getTarget());
54650         if(item){
54651             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54652                 return false;
54653             }
54654             if (this.dragGroup) {
54655                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54656                     while (targets.indexOf(this.dropZone) > -1) {
54657                             targets.remove(this.dropZone);
54658                                 }
54659                     if (targets.length == 1) {
54660                                         this.dragZone.cachedTarget = null;
54661                         var el = Roo.get(targets[0].getEl());
54662                         var box = el.getBox(true);
54663                         targets[0].onNodeDrop(el.dom, {
54664                                 target: el.dom,
54665                                 xy: [box.x, box.y + box.height - 1]
54666                         }, null, this.getDragData(e));
54667                     }
54668                 }
54669         }
54670     },
54671     
54672     handleSelection: function(e) {
54673                 this.dragZone.cachedTarget = null;
54674         var item = this.findItemFromChild(e.getTarget());
54675         if (!item) {
54676                 this.clearSelections(true);
54677                 return;
54678         }
54679                 if (item && (this.multiSelect || this.singleSelect)){
54680                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54681                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54682                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54683                                 this.unselect(item);
54684                         } else {
54685                                 this.select(item, this.multiSelect && e.ctrlKey);
54686                                 this.lastSelection = item;
54687                         }
54688                 }
54689     },
54690
54691     onItemClick : function(item, index, e){
54692                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54693                         return false;
54694                 }
54695                 return true;
54696     },
54697
54698     unselect : function(nodeInfo, suppressEvent){
54699                 var node = this.getNode(nodeInfo);
54700                 if(node && this.isSelected(node)){
54701                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54702                                 Roo.fly(node).removeClass(this.selectedClass);
54703                                 this.selections.remove(node);
54704                                 if(!suppressEvent){
54705                                         this.fireEvent("selectionchange", this, this.selections);
54706                                 }
54707                         }
54708                 }
54709     }
54710 });
54711 /*
54712  * Based on:
54713  * Ext JS Library 1.1.1
54714  * Copyright(c) 2006-2007, Ext JS, LLC.
54715  *
54716  * Originally Released Under LGPL - original licence link has changed is not relivant.
54717  *
54718  * Fork - LGPL
54719  * <script type="text/javascript">
54720  */
54721  
54722 /**
54723  * @class Roo.LayoutManager
54724  * @extends Roo.util.Observable
54725  * Base class for layout managers.
54726  */
54727 Roo.LayoutManager = function(container, config){
54728     Roo.LayoutManager.superclass.constructor.call(this);
54729     this.el = Roo.get(container);
54730     // ie scrollbar fix
54731     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54732         document.body.scroll = "no";
54733     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54734         this.el.position('relative');
54735     }
54736     this.id = this.el.id;
54737     this.el.addClass("x-layout-container");
54738     /** false to disable window resize monitoring @type Boolean */
54739     this.monitorWindowResize = true;
54740     this.regions = {};
54741     this.addEvents({
54742         /**
54743          * @event layout
54744          * Fires when a layout is performed. 
54745          * @param {Roo.LayoutManager} this
54746          */
54747         "layout" : true,
54748         /**
54749          * @event regionresized
54750          * Fires when the user resizes a region. 
54751          * @param {Roo.LayoutRegion} region The resized region
54752          * @param {Number} newSize The new size (width for east/west, height for north/south)
54753          */
54754         "regionresized" : true,
54755         /**
54756          * @event regioncollapsed
54757          * Fires when a region is collapsed. 
54758          * @param {Roo.LayoutRegion} region The collapsed region
54759          */
54760         "regioncollapsed" : true,
54761         /**
54762          * @event regionexpanded
54763          * Fires when a region is expanded.  
54764          * @param {Roo.LayoutRegion} region The expanded region
54765          */
54766         "regionexpanded" : true
54767     });
54768     this.updating = false;
54769     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54770 };
54771
54772 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54773     /**
54774      * Returns true if this layout is currently being updated
54775      * @return {Boolean}
54776      */
54777     isUpdating : function(){
54778         return this.updating; 
54779     },
54780     
54781     /**
54782      * Suspend the LayoutManager from doing auto-layouts while
54783      * making multiple add or remove calls
54784      */
54785     beginUpdate : function(){
54786         this.updating = true;    
54787     },
54788     
54789     /**
54790      * Restore auto-layouts and optionally disable the manager from performing a layout
54791      * @param {Boolean} noLayout true to disable a layout update 
54792      */
54793     endUpdate : function(noLayout){
54794         this.updating = false;
54795         if(!noLayout){
54796             this.layout();
54797         }    
54798     },
54799     
54800     layout: function(){
54801         
54802     },
54803     
54804     onRegionResized : function(region, newSize){
54805         this.fireEvent("regionresized", region, newSize);
54806         this.layout();
54807     },
54808     
54809     onRegionCollapsed : function(region){
54810         this.fireEvent("regioncollapsed", region);
54811     },
54812     
54813     onRegionExpanded : function(region){
54814         this.fireEvent("regionexpanded", region);
54815     },
54816         
54817     /**
54818      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54819      * performs box-model adjustments.
54820      * @return {Object} The size as an object {width: (the width), height: (the height)}
54821      */
54822     getViewSize : function(){
54823         var size;
54824         if(this.el.dom != document.body){
54825             size = this.el.getSize();
54826         }else{
54827             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54828         }
54829         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54830         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54831         return size;
54832     },
54833     
54834     /**
54835      * Returns the Element this layout is bound to.
54836      * @return {Roo.Element}
54837      */
54838     getEl : function(){
54839         return this.el;
54840     },
54841     
54842     /**
54843      * Returns the specified region.
54844      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54845      * @return {Roo.LayoutRegion}
54846      */
54847     getRegion : function(target){
54848         return this.regions[target.toLowerCase()];
54849     },
54850     
54851     onWindowResize : function(){
54852         if(this.monitorWindowResize){
54853             this.layout();
54854         }
54855     }
54856 });/*
54857  * Based on:
54858  * Ext JS Library 1.1.1
54859  * Copyright(c) 2006-2007, Ext JS, LLC.
54860  *
54861  * Originally Released Under LGPL - original licence link has changed is not relivant.
54862  *
54863  * Fork - LGPL
54864  * <script type="text/javascript">
54865  */
54866 /**
54867  * @class Roo.BorderLayout
54868  * @extends Roo.LayoutManager
54869  * @children Roo.ContentPanel
54870  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54871  * please see: <br><br>
54872  * <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>
54873  * <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>
54874  * Example:
54875  <pre><code>
54876  var layout = new Roo.BorderLayout(document.body, {
54877     north: {
54878         initialSize: 25,
54879         titlebar: false
54880     },
54881     west: {
54882         split:true,
54883         initialSize: 200,
54884         minSize: 175,
54885         maxSize: 400,
54886         titlebar: true,
54887         collapsible: true
54888     },
54889     east: {
54890         split:true,
54891         initialSize: 202,
54892         minSize: 175,
54893         maxSize: 400,
54894         titlebar: true,
54895         collapsible: true
54896     },
54897     south: {
54898         split:true,
54899         initialSize: 100,
54900         minSize: 100,
54901         maxSize: 200,
54902         titlebar: true,
54903         collapsible: true
54904     },
54905     center: {
54906         titlebar: true,
54907         autoScroll:true,
54908         resizeTabs: true,
54909         minTabWidth: 50,
54910         preferredTabWidth: 150
54911     }
54912 });
54913
54914 // shorthand
54915 var CP = Roo.ContentPanel;
54916
54917 layout.beginUpdate();
54918 layout.add("north", new CP("north", "North"));
54919 layout.add("south", new CP("south", {title: "South", closable: true}));
54920 layout.add("west", new CP("west", {title: "West"}));
54921 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54922 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54923 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54924 layout.getRegion("center").showPanel("center1");
54925 layout.endUpdate();
54926 </code></pre>
54927
54928 <b>The container the layout is rendered into can be either the body element or any other element.
54929 If it is not the body element, the container needs to either be an absolute positioned element,
54930 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54931 the container size if it is not the body element.</b>
54932
54933 * @constructor
54934 * Create a new BorderLayout
54935 * @param {String/HTMLElement/Element} container The container this layout is bound to
54936 * @param {Object} config Configuration options
54937  */
54938 Roo.BorderLayout = function(container, config){
54939     config = config || {};
54940     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54941     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54942     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54943         var target = this.factory.validRegions[i];
54944         if(config[target]){
54945             this.addRegion(target, config[target]);
54946         }
54947     }
54948 };
54949
54950 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54951         
54952         /**
54953          * @cfg {Roo.LayoutRegion} east
54954          */
54955         /**
54956          * @cfg {Roo.LayoutRegion} west
54957          */
54958         /**
54959          * @cfg {Roo.LayoutRegion} north
54960          */
54961         /**
54962          * @cfg {Roo.LayoutRegion} south
54963          */
54964         /**
54965          * @cfg {Roo.LayoutRegion} center
54966          */
54967     /**
54968      * Creates and adds a new region if it doesn't already exist.
54969      * @param {String} target The target region key (north, south, east, west or center).
54970      * @param {Object} config The regions config object
54971      * @return {BorderLayoutRegion} The new region
54972      */
54973     addRegion : function(target, config){
54974         if(!this.regions[target]){
54975             var r = this.factory.create(target, this, config);
54976             this.bindRegion(target, r);
54977         }
54978         return this.regions[target];
54979     },
54980
54981     // private (kinda)
54982     bindRegion : function(name, r){
54983         this.regions[name] = r;
54984         r.on("visibilitychange", this.layout, this);
54985         r.on("paneladded", this.layout, this);
54986         r.on("panelremoved", this.layout, this);
54987         r.on("invalidated", this.layout, this);
54988         r.on("resized", this.onRegionResized, this);
54989         r.on("collapsed", this.onRegionCollapsed, this);
54990         r.on("expanded", this.onRegionExpanded, this);
54991     },
54992
54993     /**
54994      * Performs a layout update.
54995      */
54996     layout : function(){
54997         if(this.updating) {
54998             return;
54999         }
55000         var size = this.getViewSize();
55001         var w = size.width;
55002         var h = size.height;
55003         var centerW = w;
55004         var centerH = h;
55005         var centerY = 0;
55006         var centerX = 0;
55007         //var x = 0, y = 0;
55008
55009         var rs = this.regions;
55010         var north = rs["north"];
55011         var south = rs["south"]; 
55012         var west = rs["west"];
55013         var east = rs["east"];
55014         var center = rs["center"];
55015         //if(this.hideOnLayout){ // not supported anymore
55016             //c.el.setStyle("display", "none");
55017         //}
55018         if(north && north.isVisible()){
55019             var b = north.getBox();
55020             var m = north.getMargins();
55021             b.width = w - (m.left+m.right);
55022             b.x = m.left;
55023             b.y = m.top;
55024             centerY = b.height + b.y + m.bottom;
55025             centerH -= centerY;
55026             north.updateBox(this.safeBox(b));
55027         }
55028         if(south && south.isVisible()){
55029             var b = south.getBox();
55030             var m = south.getMargins();
55031             b.width = w - (m.left+m.right);
55032             b.x = m.left;
55033             var totalHeight = (b.height + m.top + m.bottom);
55034             b.y = h - totalHeight + m.top;
55035             centerH -= totalHeight;
55036             south.updateBox(this.safeBox(b));
55037         }
55038         if(west && west.isVisible()){
55039             var b = west.getBox();
55040             var m = west.getMargins();
55041             b.height = centerH - (m.top+m.bottom);
55042             b.x = m.left;
55043             b.y = centerY + m.top;
55044             var totalWidth = (b.width + m.left + m.right);
55045             centerX += totalWidth;
55046             centerW -= totalWidth;
55047             west.updateBox(this.safeBox(b));
55048         }
55049         if(east && east.isVisible()){
55050             var b = east.getBox();
55051             var m = east.getMargins();
55052             b.height = centerH - (m.top+m.bottom);
55053             var totalWidth = (b.width + m.left + m.right);
55054             b.x = w - totalWidth + m.left;
55055             b.y = centerY + m.top;
55056             centerW -= totalWidth;
55057             east.updateBox(this.safeBox(b));
55058         }
55059         if(center){
55060             var m = center.getMargins();
55061             var centerBox = {
55062                 x: centerX + m.left,
55063                 y: centerY + m.top,
55064                 width: centerW - (m.left+m.right),
55065                 height: centerH - (m.top+m.bottom)
55066             };
55067             //if(this.hideOnLayout){
55068                 //center.el.setStyle("display", "block");
55069             //}
55070             center.updateBox(this.safeBox(centerBox));
55071         }
55072         this.el.repaint();
55073         this.fireEvent("layout", this);
55074     },
55075
55076     // private
55077     safeBox : function(box){
55078         box.width = Math.max(0, box.width);
55079         box.height = Math.max(0, box.height);
55080         return box;
55081     },
55082
55083     /**
55084      * Adds a ContentPanel (or subclass) to this layout.
55085      * @param {String} target The target region key (north, south, east, west or center).
55086      * @param {Roo.ContentPanel} panel The panel to add
55087      * @return {Roo.ContentPanel} The added panel
55088      */
55089     add : function(target, panel){
55090          
55091         target = target.toLowerCase();
55092         return this.regions[target].add(panel);
55093     },
55094
55095     /**
55096      * Remove a ContentPanel (or subclass) to this layout.
55097      * @param {String} target The target region key (north, south, east, west or center).
55098      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55099      * @return {Roo.ContentPanel} The removed panel
55100      */
55101     remove : function(target, panel){
55102         target = target.toLowerCase();
55103         return this.regions[target].remove(panel);
55104     },
55105
55106     /**
55107      * Searches all regions for a panel with the specified id
55108      * @param {String} panelId
55109      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55110      */
55111     findPanel : function(panelId){
55112         var rs = this.regions;
55113         for(var target in rs){
55114             if(typeof rs[target] != "function"){
55115                 var p = rs[target].getPanel(panelId);
55116                 if(p){
55117                     return p;
55118                 }
55119             }
55120         }
55121         return null;
55122     },
55123
55124     /**
55125      * Searches all regions for a panel with the specified id and activates (shows) it.
55126      * @param {String/ContentPanel} panelId The panels id or the panel itself
55127      * @return {Roo.ContentPanel} The shown panel or null
55128      */
55129     showPanel : function(panelId) {
55130       var rs = this.regions;
55131       for(var target in rs){
55132          var r = rs[target];
55133          if(typeof r != "function"){
55134             if(r.hasPanel(panelId)){
55135                return r.showPanel(panelId);
55136             }
55137          }
55138       }
55139       return null;
55140    },
55141
55142    /**
55143      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55144      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55145      */
55146     restoreState : function(provider){
55147         if(!provider){
55148             provider = Roo.state.Manager;
55149         }
55150         var sm = new Roo.LayoutStateManager();
55151         sm.init(this, provider);
55152     },
55153
55154     /**
55155      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55156      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55157      * a valid ContentPanel config object.  Example:
55158      * <pre><code>
55159 // Create the main layout
55160 var layout = new Roo.BorderLayout('main-ct', {
55161     west: {
55162         split:true,
55163         minSize: 175,
55164         titlebar: true
55165     },
55166     center: {
55167         title:'Components'
55168     }
55169 }, 'main-ct');
55170
55171 // Create and add multiple ContentPanels at once via configs
55172 layout.batchAdd({
55173    west: {
55174        id: 'source-files',
55175        autoCreate:true,
55176        title:'Ext Source Files',
55177        autoScroll:true,
55178        fitToFrame:true
55179    },
55180    center : {
55181        el: cview,
55182        autoScroll:true,
55183        fitToFrame:true,
55184        toolbar: tb,
55185        resizeEl:'cbody'
55186    }
55187 });
55188 </code></pre>
55189      * @param {Object} regions An object containing ContentPanel configs by region name
55190      */
55191     batchAdd : function(regions){
55192         this.beginUpdate();
55193         for(var rname in regions){
55194             var lr = this.regions[rname];
55195             if(lr){
55196                 this.addTypedPanels(lr, regions[rname]);
55197             }
55198         }
55199         this.endUpdate();
55200     },
55201
55202     // private
55203     addTypedPanels : function(lr, ps){
55204         if(typeof ps == 'string'){
55205             lr.add(new Roo.ContentPanel(ps));
55206         }
55207         else if(ps instanceof Array){
55208             for(var i =0, len = ps.length; i < len; i++){
55209                 this.addTypedPanels(lr, ps[i]);
55210             }
55211         }
55212         else if(!ps.events){ // raw config?
55213             var el = ps.el;
55214             delete ps.el; // prevent conflict
55215             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55216         }
55217         else {  // panel object assumed!
55218             lr.add(ps);
55219         }
55220     },
55221     /**
55222      * Adds a xtype elements to the layout.
55223      * <pre><code>
55224
55225 layout.addxtype({
55226        xtype : 'ContentPanel',
55227        region: 'west',
55228        items: [ .... ]
55229    }
55230 );
55231
55232 layout.addxtype({
55233         xtype : 'NestedLayoutPanel',
55234         region: 'west',
55235         layout: {
55236            center: { },
55237            west: { }   
55238         },
55239         items : [ ... list of content panels or nested layout panels.. ]
55240    }
55241 );
55242 </code></pre>
55243      * @param {Object} cfg Xtype definition of item to add.
55244      */
55245     addxtype : function(cfg)
55246     {
55247         // basically accepts a pannel...
55248         // can accept a layout region..!?!?
55249         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55250         
55251         if (!cfg.xtype.match(/Panel$/)) {
55252             return false;
55253         }
55254         var ret = false;
55255         
55256         if (typeof(cfg.region) == 'undefined') {
55257             Roo.log("Failed to add Panel, region was not set");
55258             Roo.log(cfg);
55259             return false;
55260         }
55261         var region = cfg.region;
55262         delete cfg.region;
55263         
55264           
55265         var xitems = [];
55266         if (cfg.items) {
55267             xitems = cfg.items;
55268             delete cfg.items;
55269         }
55270         var nb = false;
55271         
55272         switch(cfg.xtype) 
55273         {
55274             case 'ContentPanel':  // ContentPanel (el, cfg)
55275             case 'ScrollPanel':  // ContentPanel (el, cfg)
55276             case 'ViewPanel': 
55277                 if(cfg.autoCreate) {
55278                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55279                 } else {
55280                     var el = this.el.createChild();
55281                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55282                 }
55283                 
55284                 this.add(region, ret);
55285                 break;
55286             
55287             
55288             case 'TreePanel': // our new panel!
55289                 cfg.el = this.el.createChild();
55290                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55291                 this.add(region, ret);
55292                 break;
55293             
55294             case 'NestedLayoutPanel': 
55295                 // create a new Layout (which is  a Border Layout...
55296                 var el = this.el.createChild();
55297                 var clayout = cfg.layout;
55298                 delete cfg.layout;
55299                 clayout.items   = clayout.items  || [];
55300                 // replace this exitems with the clayout ones..
55301                 xitems = clayout.items;
55302                  
55303                 
55304                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55305                     cfg.background = false;
55306                 }
55307                 var layout = new Roo.BorderLayout(el, clayout);
55308                 
55309                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55310                 //console.log('adding nested layout panel '  + cfg.toSource());
55311                 this.add(region, ret);
55312                 nb = {}; /// find first...
55313                 break;
55314                 
55315             case 'GridPanel': 
55316             
55317                 // needs grid and region
55318                 
55319                 //var el = this.getRegion(region).el.createChild();
55320                 var el = this.el.createChild();
55321                 // create the grid first...
55322                 
55323                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55324                 delete cfg.grid;
55325                 if (region == 'center' && this.active ) {
55326                     cfg.background = false;
55327                 }
55328                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55329                 
55330                 this.add(region, ret);
55331                 if (cfg.background) {
55332                     ret.on('activate', function(gp) {
55333                         if (!gp.grid.rendered) {
55334                             gp.grid.render();
55335                         }
55336                     });
55337                 } else {
55338                     grid.render();
55339                 }
55340                 break;
55341            
55342            
55343            
55344                 
55345                 
55346                 
55347             default:
55348                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55349                     
55350                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55351                     this.add(region, ret);
55352                 } else {
55353                 
55354                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55355                     return null;
55356                 }
55357                 
55358              // GridPanel (grid, cfg)
55359             
55360         }
55361         this.beginUpdate();
55362         // add children..
55363         var region = '';
55364         var abn = {};
55365         Roo.each(xitems, function(i)  {
55366             region = nb && i.region ? i.region : false;
55367             
55368             var add = ret.addxtype(i);
55369            
55370             if (region) {
55371                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55372                 if (!i.background) {
55373                     abn[region] = nb[region] ;
55374                 }
55375             }
55376             
55377         });
55378         this.endUpdate();
55379
55380         // make the last non-background panel active..
55381         //if (nb) { Roo.log(abn); }
55382         if (nb) {
55383             
55384             for(var r in abn) {
55385                 region = this.getRegion(r);
55386                 if (region) {
55387                     // tried using nb[r], but it does not work..
55388                      
55389                     region.showPanel(abn[r]);
55390                    
55391                 }
55392             }
55393         }
55394         return ret;
55395         
55396     }
55397 });
55398
55399 /**
55400  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55401  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55402  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55403  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55404  * <pre><code>
55405 // shorthand
55406 var CP = Roo.ContentPanel;
55407
55408 var layout = Roo.BorderLayout.create({
55409     north: {
55410         initialSize: 25,
55411         titlebar: false,
55412         panels: [new CP("north", "North")]
55413     },
55414     west: {
55415         split:true,
55416         initialSize: 200,
55417         minSize: 175,
55418         maxSize: 400,
55419         titlebar: true,
55420         collapsible: true,
55421         panels: [new CP("west", {title: "West"})]
55422     },
55423     east: {
55424         split:true,
55425         initialSize: 202,
55426         minSize: 175,
55427         maxSize: 400,
55428         titlebar: true,
55429         collapsible: true,
55430         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55431     },
55432     south: {
55433         split:true,
55434         initialSize: 100,
55435         minSize: 100,
55436         maxSize: 200,
55437         titlebar: true,
55438         collapsible: true,
55439         panels: [new CP("south", {title: "South", closable: true})]
55440     },
55441     center: {
55442         titlebar: true,
55443         autoScroll:true,
55444         resizeTabs: true,
55445         minTabWidth: 50,
55446         preferredTabWidth: 150,
55447         panels: [
55448             new CP("center1", {title: "Close Me", closable: true}),
55449             new CP("center2", {title: "Center Panel", closable: false})
55450         ]
55451     }
55452 }, document.body);
55453
55454 layout.getRegion("center").showPanel("center1");
55455 </code></pre>
55456  * @param config
55457  * @param targetEl
55458  */
55459 Roo.BorderLayout.create = function(config, targetEl){
55460     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55461     layout.beginUpdate();
55462     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55463     for(var j = 0, jlen = regions.length; j < jlen; j++){
55464         var lr = regions[j];
55465         if(layout.regions[lr] && config[lr].panels){
55466             var r = layout.regions[lr];
55467             var ps = config[lr].panels;
55468             layout.addTypedPanels(r, ps);
55469         }
55470     }
55471     layout.endUpdate();
55472     return layout;
55473 };
55474
55475 // private
55476 Roo.BorderLayout.RegionFactory = {
55477     // private
55478     validRegions : ["north","south","east","west","center"],
55479
55480     // private
55481     create : function(target, mgr, config){
55482         target = target.toLowerCase();
55483         if(config.lightweight || config.basic){
55484             return new Roo.BasicLayoutRegion(mgr, config, target);
55485         }
55486         switch(target){
55487             case "north":
55488                 return new Roo.NorthLayoutRegion(mgr, config);
55489             case "south":
55490                 return new Roo.SouthLayoutRegion(mgr, config);
55491             case "east":
55492                 return new Roo.EastLayoutRegion(mgr, config);
55493             case "west":
55494                 return new Roo.WestLayoutRegion(mgr, config);
55495             case "center":
55496                 return new Roo.CenterLayoutRegion(mgr, config);
55497         }
55498         throw 'Layout region "'+target+'" not supported.';
55499     }
55500 };/*
55501  * Based on:
55502  * Ext JS Library 1.1.1
55503  * Copyright(c) 2006-2007, Ext JS, LLC.
55504  *
55505  * Originally Released Under LGPL - original licence link has changed is not relivant.
55506  *
55507  * Fork - LGPL
55508  * <script type="text/javascript">
55509  */
55510  
55511 /**
55512  * @class Roo.BasicLayoutRegion
55513  * @extends Roo.util.Observable
55514  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55515  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55516  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55517  */
55518 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55519     this.mgr = mgr;
55520     this.position  = pos;
55521     this.events = {
55522         /**
55523          * @scope Roo.BasicLayoutRegion
55524          */
55525         
55526         /**
55527          * @event beforeremove
55528          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55529          * @param {Roo.LayoutRegion} this
55530          * @param {Roo.ContentPanel} panel The panel
55531          * @param {Object} e The cancel event object
55532          */
55533         "beforeremove" : true,
55534         /**
55535          * @event invalidated
55536          * Fires when the layout for this region is changed.
55537          * @param {Roo.LayoutRegion} this
55538          */
55539         "invalidated" : true,
55540         /**
55541          * @event visibilitychange
55542          * Fires when this region is shown or hidden 
55543          * @param {Roo.LayoutRegion} this
55544          * @param {Boolean} visibility true or false
55545          */
55546         "visibilitychange" : true,
55547         /**
55548          * @event paneladded
55549          * Fires when a panel is added. 
55550          * @param {Roo.LayoutRegion} this
55551          * @param {Roo.ContentPanel} panel The panel
55552          */
55553         "paneladded" : true,
55554         /**
55555          * @event panelremoved
55556          * Fires when a panel is removed. 
55557          * @param {Roo.LayoutRegion} this
55558          * @param {Roo.ContentPanel} panel The panel
55559          */
55560         "panelremoved" : true,
55561         /**
55562          * @event beforecollapse
55563          * Fires when this region before collapse.
55564          * @param {Roo.LayoutRegion} this
55565          */
55566         "beforecollapse" : true,
55567         /**
55568          * @event collapsed
55569          * Fires when this region is collapsed.
55570          * @param {Roo.LayoutRegion} this
55571          */
55572         "collapsed" : true,
55573         /**
55574          * @event expanded
55575          * Fires when this region is expanded.
55576          * @param {Roo.LayoutRegion} this
55577          */
55578         "expanded" : true,
55579         /**
55580          * @event slideshow
55581          * Fires when this region is slid into view.
55582          * @param {Roo.LayoutRegion} this
55583          */
55584         "slideshow" : true,
55585         /**
55586          * @event slidehide
55587          * Fires when this region slides out of view. 
55588          * @param {Roo.LayoutRegion} this
55589          */
55590         "slidehide" : true,
55591         /**
55592          * @event panelactivated
55593          * Fires when a panel is activated. 
55594          * @param {Roo.LayoutRegion} this
55595          * @param {Roo.ContentPanel} panel The activated panel
55596          */
55597         "panelactivated" : true,
55598         /**
55599          * @event resized
55600          * Fires when the user resizes this region. 
55601          * @param {Roo.LayoutRegion} this
55602          * @param {Number} newSize The new size (width for east/west, height for north/south)
55603          */
55604         "resized" : true
55605     };
55606     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55607     this.panels = new Roo.util.MixedCollection();
55608     this.panels.getKey = this.getPanelId.createDelegate(this);
55609     this.box = null;
55610     this.activePanel = null;
55611     // ensure listeners are added...
55612     
55613     if (config.listeners || config.events) {
55614         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55615             listeners : config.listeners || {},
55616             events : config.events || {}
55617         });
55618     }
55619     
55620     if(skipConfig !== true){
55621         this.applyConfig(config);
55622     }
55623 };
55624
55625 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55626     getPanelId : function(p){
55627         return p.getId();
55628     },
55629     
55630     applyConfig : function(config){
55631         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55632         this.config = config;
55633         
55634     },
55635     
55636     /**
55637      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55638      * the width, for horizontal (north, south) the height.
55639      * @param {Number} newSize The new width or height
55640      */
55641     resizeTo : function(newSize){
55642         var el = this.el ? this.el :
55643                  (this.activePanel ? this.activePanel.getEl() : null);
55644         if(el){
55645             switch(this.position){
55646                 case "east":
55647                 case "west":
55648                     el.setWidth(newSize);
55649                     this.fireEvent("resized", this, newSize);
55650                 break;
55651                 case "north":
55652                 case "south":
55653                     el.setHeight(newSize);
55654                     this.fireEvent("resized", this, newSize);
55655                 break;                
55656             }
55657         }
55658     },
55659     
55660     getBox : function(){
55661         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55662     },
55663     
55664     getMargins : function(){
55665         return this.margins;
55666     },
55667     
55668     updateBox : function(box){
55669         this.box = box;
55670         var el = this.activePanel.getEl();
55671         el.dom.style.left = box.x + "px";
55672         el.dom.style.top = box.y + "px";
55673         this.activePanel.setSize(box.width, box.height);
55674     },
55675     
55676     /**
55677      * Returns the container element for this region.
55678      * @return {Roo.Element}
55679      */
55680     getEl : function(){
55681         return this.activePanel;
55682     },
55683     
55684     /**
55685      * Returns true if this region is currently visible.
55686      * @return {Boolean}
55687      */
55688     isVisible : function(){
55689         return this.activePanel ? true : false;
55690     },
55691     
55692     setActivePanel : function(panel){
55693         panel = this.getPanel(panel);
55694         if(this.activePanel && this.activePanel != panel){
55695             this.activePanel.setActiveState(false);
55696             this.activePanel.getEl().setLeftTop(-10000,-10000);
55697         }
55698         this.activePanel = panel;
55699         panel.setActiveState(true);
55700         if(this.box){
55701             panel.setSize(this.box.width, this.box.height);
55702         }
55703         this.fireEvent("panelactivated", this, panel);
55704         this.fireEvent("invalidated");
55705     },
55706     
55707     /**
55708      * Show the specified panel.
55709      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55710      * @return {Roo.ContentPanel} The shown panel or null
55711      */
55712     showPanel : function(panel){
55713         if(panel = this.getPanel(panel)){
55714             this.setActivePanel(panel);
55715         }
55716         return panel;
55717     },
55718     
55719     /**
55720      * Get the active panel for this region.
55721      * @return {Roo.ContentPanel} The active panel or null
55722      */
55723     getActivePanel : function(){
55724         return this.activePanel;
55725     },
55726     
55727     /**
55728      * Add the passed ContentPanel(s)
55729      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55730      * @return {Roo.ContentPanel} The panel added (if only one was added)
55731      */
55732     add : function(panel){
55733         if(arguments.length > 1){
55734             for(var i = 0, len = arguments.length; i < len; i++) {
55735                 this.add(arguments[i]);
55736             }
55737             return null;
55738         }
55739         if(this.hasPanel(panel)){
55740             this.showPanel(panel);
55741             return panel;
55742         }
55743         var el = panel.getEl();
55744         if(el.dom.parentNode != this.mgr.el.dom){
55745             this.mgr.el.dom.appendChild(el.dom);
55746         }
55747         if(panel.setRegion){
55748             panel.setRegion(this);
55749         }
55750         this.panels.add(panel);
55751         el.setStyle("position", "absolute");
55752         if(!panel.background){
55753             this.setActivePanel(panel);
55754             if(this.config.initialSize && this.panels.getCount()==1){
55755                 this.resizeTo(this.config.initialSize);
55756             }
55757         }
55758         this.fireEvent("paneladded", this, panel);
55759         return panel;
55760     },
55761     
55762     /**
55763      * Returns true if the panel is in this region.
55764      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55765      * @return {Boolean}
55766      */
55767     hasPanel : function(panel){
55768         if(typeof panel == "object"){ // must be panel obj
55769             panel = panel.getId();
55770         }
55771         return this.getPanel(panel) ? true : false;
55772     },
55773     
55774     /**
55775      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55776      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55777      * @param {Boolean} preservePanel Overrides the config preservePanel option
55778      * @return {Roo.ContentPanel} The panel that was removed
55779      */
55780     remove : function(panel, preservePanel){
55781         panel = this.getPanel(panel);
55782         if(!panel){
55783             return null;
55784         }
55785         var e = {};
55786         this.fireEvent("beforeremove", this, panel, e);
55787         if(e.cancel === true){
55788             return null;
55789         }
55790         var panelId = panel.getId();
55791         this.panels.removeKey(panelId);
55792         return panel;
55793     },
55794     
55795     /**
55796      * Returns the panel specified or null if it's not in this region.
55797      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55798      * @return {Roo.ContentPanel}
55799      */
55800     getPanel : function(id){
55801         if(typeof id == "object"){ // must be panel obj
55802             return id;
55803         }
55804         return this.panels.get(id);
55805     },
55806     
55807     /**
55808      * Returns this regions position (north/south/east/west/center).
55809      * @return {String} 
55810      */
55811     getPosition: function(){
55812         return this.position;    
55813     }
55814 });/*
55815  * Based on:
55816  * Ext JS Library 1.1.1
55817  * Copyright(c) 2006-2007, Ext JS, LLC.
55818  *
55819  * Originally Released Under LGPL - original licence link has changed is not relivant.
55820  *
55821  * Fork - LGPL
55822  * <script type="text/javascript">
55823  */
55824  
55825 /**
55826  * @class Roo.LayoutRegion
55827  * @extends Roo.BasicLayoutRegion
55828  * This class represents a region in a layout manager.
55829  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55830  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55831  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55832  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55833  * @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})
55834  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55835  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55836  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55837  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55838  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55839  * @cfg {String}    title           The title for the region (overrides panel titles)
55840  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55841  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55842  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55843  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55844  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55845  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55846  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55847  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55848  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55849  * @cfg {Boolean}   showPin         True to show a pin button
55850  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55851  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55852  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55853  * @cfg {Number}    width           For East/West panels
55854  * @cfg {Number}    height          For North/South panels
55855  * @cfg {Boolean}   split           To show the splitter
55856  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55857  */
55858 Roo.LayoutRegion = function(mgr, config, pos){
55859     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55860     var dh = Roo.DomHelper;
55861     /** This region's container element 
55862     * @type Roo.Element */
55863     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55864     /** This region's title element 
55865     * @type Roo.Element */
55866
55867     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55868         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55869         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55870     ]}, true);
55871     this.titleEl.enableDisplayMode();
55872     /** This region's title text element 
55873     * @type HTMLElement */
55874     this.titleTextEl = this.titleEl.dom.firstChild;
55875     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55876     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55877     this.closeBtn.enableDisplayMode();
55878     this.closeBtn.on("click", this.closeClicked, this);
55879     this.closeBtn.hide();
55880
55881     this.createBody(config);
55882     this.visible = true;
55883     this.collapsed = false;
55884
55885     if(config.hideWhenEmpty){
55886         this.hide();
55887         this.on("paneladded", this.validateVisibility, this);
55888         this.on("panelremoved", this.validateVisibility, this);
55889     }
55890     this.applyConfig(config);
55891 };
55892
55893 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55894
55895     createBody : function(){
55896         /** This region's body element 
55897         * @type Roo.Element */
55898         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55899     },
55900
55901     applyConfig : function(c){
55902         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55903             var dh = Roo.DomHelper;
55904             if(c.titlebar !== false){
55905                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55906                 this.collapseBtn.on("click", this.collapse, this);
55907                 this.collapseBtn.enableDisplayMode();
55908
55909                 if(c.showPin === true || this.showPin){
55910                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55911                     this.stickBtn.enableDisplayMode();
55912                     this.stickBtn.on("click", this.expand, this);
55913                     this.stickBtn.hide();
55914                 }
55915             }
55916             /** This region's collapsed element
55917             * @type Roo.Element */
55918             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55919                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55920             ]}, true);
55921             if(c.floatable !== false){
55922                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55923                this.collapsedEl.on("click", this.collapseClick, this);
55924             }
55925
55926             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55927                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55928                    id: "message", unselectable: "on", style:{"float":"left"}});
55929                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55930              }
55931             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55932             this.expandBtn.on("click", this.expand, this);
55933         }
55934         if(this.collapseBtn){
55935             this.collapseBtn.setVisible(c.collapsible == true);
55936         }
55937         this.cmargins = c.cmargins || this.cmargins ||
55938                          (this.position == "west" || this.position == "east" ?
55939                              {top: 0, left: 2, right:2, bottom: 0} :
55940                              {top: 2, left: 0, right:0, bottom: 2});
55941         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55942         this.bottomTabs = c.tabPosition != "top";
55943         this.autoScroll = c.autoScroll || false;
55944         if(this.autoScroll){
55945             this.bodyEl.setStyle("overflow", "auto");
55946         }else{
55947             this.bodyEl.setStyle("overflow", "hidden");
55948         }
55949         //if(c.titlebar !== false){
55950             if((!c.titlebar && !c.title) || c.titlebar === false){
55951                 this.titleEl.hide();
55952             }else{
55953                 this.titleEl.show();
55954                 if(c.title){
55955                     this.titleTextEl.innerHTML = c.title;
55956                 }
55957             }
55958         //}
55959         this.duration = c.duration || .30;
55960         this.slideDuration = c.slideDuration || .45;
55961         this.config = c;
55962         if(c.collapsed){
55963             this.collapse(true);
55964         }
55965         if(c.hidden){
55966             this.hide();
55967         }
55968     },
55969     /**
55970      * Returns true if this region is currently visible.
55971      * @return {Boolean}
55972      */
55973     isVisible : function(){
55974         return this.visible;
55975     },
55976
55977     /**
55978      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55979      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55980      */
55981     setCollapsedTitle : function(title){
55982         title = title || "&#160;";
55983         if(this.collapsedTitleTextEl){
55984             this.collapsedTitleTextEl.innerHTML = title;
55985         }
55986     },
55987
55988     getBox : function(){
55989         var b;
55990         if(!this.collapsed){
55991             b = this.el.getBox(false, true);
55992         }else{
55993             b = this.collapsedEl.getBox(false, true);
55994         }
55995         return b;
55996     },
55997
55998     getMargins : function(){
55999         return this.collapsed ? this.cmargins : this.margins;
56000     },
56001
56002     highlight : function(){
56003         this.el.addClass("x-layout-panel-dragover");
56004     },
56005
56006     unhighlight : function(){
56007         this.el.removeClass("x-layout-panel-dragover");
56008     },
56009
56010     updateBox : function(box){
56011         this.box = box;
56012         if(!this.collapsed){
56013             this.el.dom.style.left = box.x + "px";
56014             this.el.dom.style.top = box.y + "px";
56015             this.updateBody(box.width, box.height);
56016         }else{
56017             this.collapsedEl.dom.style.left = box.x + "px";
56018             this.collapsedEl.dom.style.top = box.y + "px";
56019             this.collapsedEl.setSize(box.width, box.height);
56020         }
56021         if(this.tabs){
56022             this.tabs.autoSizeTabs();
56023         }
56024     },
56025
56026     updateBody : function(w, h){
56027         if(w !== null){
56028             this.el.setWidth(w);
56029             w -= this.el.getBorderWidth("rl");
56030             if(this.config.adjustments){
56031                 w += this.config.adjustments[0];
56032             }
56033         }
56034         if(h !== null){
56035             this.el.setHeight(h);
56036             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56037             h -= this.el.getBorderWidth("tb");
56038             if(this.config.adjustments){
56039                 h += this.config.adjustments[1];
56040             }
56041             this.bodyEl.setHeight(h);
56042             if(this.tabs){
56043                 h = this.tabs.syncHeight(h);
56044             }
56045         }
56046         if(this.panelSize){
56047             w = w !== null ? w : this.panelSize.width;
56048             h = h !== null ? h : this.panelSize.height;
56049         }
56050         if(this.activePanel){
56051             var el = this.activePanel.getEl();
56052             w = w !== null ? w : el.getWidth();
56053             h = h !== null ? h : el.getHeight();
56054             this.panelSize = {width: w, height: h};
56055             this.activePanel.setSize(w, h);
56056         }
56057         if(Roo.isIE && this.tabs){
56058             this.tabs.el.repaint();
56059         }
56060     },
56061
56062     /**
56063      * Returns the container element for this region.
56064      * @return {Roo.Element}
56065      */
56066     getEl : function(){
56067         return this.el;
56068     },
56069
56070     /**
56071      * Hides this region.
56072      */
56073     hide : function(){
56074         if(!this.collapsed){
56075             this.el.dom.style.left = "-2000px";
56076             this.el.hide();
56077         }else{
56078             this.collapsedEl.dom.style.left = "-2000px";
56079             this.collapsedEl.hide();
56080         }
56081         this.visible = false;
56082         this.fireEvent("visibilitychange", this, false);
56083     },
56084
56085     /**
56086      * Shows this region if it was previously hidden.
56087      */
56088     show : function(){
56089         if(!this.collapsed){
56090             this.el.show();
56091         }else{
56092             this.collapsedEl.show();
56093         }
56094         this.visible = true;
56095         this.fireEvent("visibilitychange", this, true);
56096     },
56097
56098     closeClicked : function(){
56099         if(this.activePanel){
56100             this.remove(this.activePanel);
56101         }
56102     },
56103
56104     collapseClick : function(e){
56105         if(this.isSlid){
56106            e.stopPropagation();
56107            this.slideIn();
56108         }else{
56109            e.stopPropagation();
56110            this.slideOut();
56111         }
56112     },
56113
56114     /**
56115      * Collapses this region.
56116      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56117      */
56118     collapse : function(skipAnim, skipCheck){
56119         if(this.collapsed) {
56120             return;
56121         }
56122         
56123         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56124             
56125             this.collapsed = true;
56126             if(this.split){
56127                 this.split.el.hide();
56128             }
56129             if(this.config.animate && skipAnim !== true){
56130                 this.fireEvent("invalidated", this);
56131                 this.animateCollapse();
56132             }else{
56133                 this.el.setLocation(-20000,-20000);
56134                 this.el.hide();
56135                 this.collapsedEl.show();
56136                 this.fireEvent("collapsed", this);
56137                 this.fireEvent("invalidated", this);
56138             }
56139         }
56140         
56141     },
56142
56143     animateCollapse : function(){
56144         // overridden
56145     },
56146
56147     /**
56148      * Expands this region if it was previously collapsed.
56149      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56150      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56151      */
56152     expand : function(e, skipAnim){
56153         if(e) {
56154             e.stopPropagation();
56155         }
56156         if(!this.collapsed || this.el.hasActiveFx()) {
56157             return;
56158         }
56159         if(this.isSlid){
56160             this.afterSlideIn();
56161             skipAnim = true;
56162         }
56163         this.collapsed = false;
56164         if(this.config.animate && skipAnim !== true){
56165             this.animateExpand();
56166         }else{
56167             this.el.show();
56168             if(this.split){
56169                 this.split.el.show();
56170             }
56171             this.collapsedEl.setLocation(-2000,-2000);
56172             this.collapsedEl.hide();
56173             this.fireEvent("invalidated", this);
56174             this.fireEvent("expanded", this);
56175         }
56176     },
56177
56178     animateExpand : function(){
56179         // overridden
56180     },
56181
56182     initTabs : function()
56183     {
56184         this.bodyEl.setStyle("overflow", "hidden");
56185         var ts = new Roo.TabPanel(
56186                 this.bodyEl.dom,
56187                 {
56188                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56189                     disableTooltips: this.config.disableTabTips,
56190                     toolbar : this.config.toolbar
56191                 }
56192         );
56193         if(this.config.hideTabs){
56194             ts.stripWrap.setDisplayed(false);
56195         }
56196         this.tabs = ts;
56197         ts.resizeTabs = this.config.resizeTabs === true;
56198         ts.minTabWidth = this.config.minTabWidth || 40;
56199         ts.maxTabWidth = this.config.maxTabWidth || 250;
56200         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56201         ts.monitorResize = false;
56202         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56203         ts.bodyEl.addClass('x-layout-tabs-body');
56204         this.panels.each(this.initPanelAsTab, this);
56205     },
56206
56207     initPanelAsTab : function(panel){
56208         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56209                     this.config.closeOnTab && panel.isClosable());
56210         if(panel.tabTip !== undefined){
56211             ti.setTooltip(panel.tabTip);
56212         }
56213         ti.on("activate", function(){
56214               this.setActivePanel(panel);
56215         }, this);
56216         if(this.config.closeOnTab){
56217             ti.on("beforeclose", function(t, e){
56218                 e.cancel = true;
56219                 this.remove(panel);
56220             }, this);
56221         }
56222         return ti;
56223     },
56224
56225     updatePanelTitle : function(panel, title){
56226         if(this.activePanel == panel){
56227             this.updateTitle(title);
56228         }
56229         if(this.tabs){
56230             var ti = this.tabs.getTab(panel.getEl().id);
56231             ti.setText(title);
56232             if(panel.tabTip !== undefined){
56233                 ti.setTooltip(panel.tabTip);
56234             }
56235         }
56236     },
56237
56238     updateTitle : function(title){
56239         if(this.titleTextEl && !this.config.title){
56240             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56241         }
56242     },
56243
56244     setActivePanel : function(panel){
56245         panel = this.getPanel(panel);
56246         if(this.activePanel && this.activePanel != panel){
56247             this.activePanel.setActiveState(false);
56248         }
56249         this.activePanel = panel;
56250         panel.setActiveState(true);
56251         if(this.panelSize){
56252             panel.setSize(this.panelSize.width, this.panelSize.height);
56253         }
56254         if(this.closeBtn){
56255             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56256         }
56257         this.updateTitle(panel.getTitle());
56258         if(this.tabs){
56259             this.fireEvent("invalidated", this);
56260         }
56261         this.fireEvent("panelactivated", this, panel);
56262     },
56263
56264     /**
56265      * Shows the specified panel.
56266      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56267      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56268      */
56269     showPanel : function(panel)
56270     {
56271         panel = this.getPanel(panel);
56272         if(panel){
56273             if(this.tabs){
56274                 var tab = this.tabs.getTab(panel.getEl().id);
56275                 if(tab.isHidden()){
56276                     this.tabs.unhideTab(tab.id);
56277                 }
56278                 tab.activate();
56279             }else{
56280                 this.setActivePanel(panel);
56281             }
56282         }
56283         return panel;
56284     },
56285
56286     /**
56287      * Get the active panel for this region.
56288      * @return {Roo.ContentPanel} The active panel or null
56289      */
56290     getActivePanel : function(){
56291         return this.activePanel;
56292     },
56293
56294     validateVisibility : function(){
56295         if(this.panels.getCount() < 1){
56296             this.updateTitle("&#160;");
56297             this.closeBtn.hide();
56298             this.hide();
56299         }else{
56300             if(!this.isVisible()){
56301                 this.show();
56302             }
56303         }
56304     },
56305
56306     /**
56307      * Adds the passed ContentPanel(s) to this region.
56308      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56309      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56310      */
56311     add : function(panel){
56312         if(arguments.length > 1){
56313             for(var i = 0, len = arguments.length; i < len; i++) {
56314                 this.add(arguments[i]);
56315             }
56316             return null;
56317         }
56318         if(this.hasPanel(panel)){
56319             this.showPanel(panel);
56320             return panel;
56321         }
56322         panel.setRegion(this);
56323         this.panels.add(panel);
56324         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56325             this.bodyEl.dom.appendChild(panel.getEl().dom);
56326             if(panel.background !== true){
56327                 this.setActivePanel(panel);
56328             }
56329             this.fireEvent("paneladded", this, panel);
56330             return panel;
56331         }
56332         if(!this.tabs){
56333             this.initTabs();
56334         }else{
56335             this.initPanelAsTab(panel);
56336         }
56337         if(panel.background !== true){
56338             this.tabs.activate(panel.getEl().id);
56339         }
56340         this.fireEvent("paneladded", this, panel);
56341         return panel;
56342     },
56343
56344     /**
56345      * Hides the tab for the specified panel.
56346      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56347      */
56348     hidePanel : function(panel){
56349         if(this.tabs && (panel = this.getPanel(panel))){
56350             this.tabs.hideTab(panel.getEl().id);
56351         }
56352     },
56353
56354     /**
56355      * Unhides the tab for a previously hidden panel.
56356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56357      */
56358     unhidePanel : function(panel){
56359         if(this.tabs && (panel = this.getPanel(panel))){
56360             this.tabs.unhideTab(panel.getEl().id);
56361         }
56362     },
56363
56364     clearPanels : function(){
56365         while(this.panels.getCount() > 0){
56366              this.remove(this.panels.first());
56367         }
56368     },
56369
56370     /**
56371      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56372      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56373      * @param {Boolean} preservePanel Overrides the config preservePanel option
56374      * @return {Roo.ContentPanel} The panel that was removed
56375      */
56376     remove : function(panel, preservePanel){
56377         panel = this.getPanel(panel);
56378         if(!panel){
56379             return null;
56380         }
56381         var e = {};
56382         this.fireEvent("beforeremove", this, panel, e);
56383         if(e.cancel === true){
56384             return null;
56385         }
56386         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56387         var panelId = panel.getId();
56388         this.panels.removeKey(panelId);
56389         if(preservePanel){
56390             document.body.appendChild(panel.getEl().dom);
56391         }
56392         if(this.tabs){
56393             this.tabs.removeTab(panel.getEl().id);
56394         }else if (!preservePanel){
56395             this.bodyEl.dom.removeChild(panel.getEl().dom);
56396         }
56397         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56398             var p = this.panels.first();
56399             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56400             tempEl.appendChild(p.getEl().dom);
56401             this.bodyEl.update("");
56402             this.bodyEl.dom.appendChild(p.getEl().dom);
56403             tempEl = null;
56404             this.updateTitle(p.getTitle());
56405             this.tabs = null;
56406             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56407             this.setActivePanel(p);
56408         }
56409         panel.setRegion(null);
56410         if(this.activePanel == panel){
56411             this.activePanel = null;
56412         }
56413         if(this.config.autoDestroy !== false && preservePanel !== true){
56414             try{panel.destroy();}catch(e){}
56415         }
56416         this.fireEvent("panelremoved", this, panel);
56417         return panel;
56418     },
56419
56420     /**
56421      * Returns the TabPanel component used by this region
56422      * @return {Roo.TabPanel}
56423      */
56424     getTabs : function(){
56425         return this.tabs;
56426     },
56427
56428     createTool : function(parentEl, className){
56429         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56430             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56431         btn.addClassOnOver("x-layout-tools-button-over");
56432         return btn;
56433     }
56434 });/*
56435  * Based on:
56436  * Ext JS Library 1.1.1
56437  * Copyright(c) 2006-2007, Ext JS, LLC.
56438  *
56439  * Originally Released Under LGPL - original licence link has changed is not relivant.
56440  *
56441  * Fork - LGPL
56442  * <script type="text/javascript">
56443  */
56444  
56445
56446
56447 /**
56448  * @class Roo.SplitLayoutRegion
56449  * @extends Roo.LayoutRegion
56450  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56451  */
56452 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56453     this.cursor = cursor;
56454     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56455 };
56456
56457 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56458     splitTip : "Drag to resize.",
56459     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56460     useSplitTips : false,
56461
56462     applyConfig : function(config){
56463         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56464         if(config.split){
56465             if(!this.split){
56466                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56467                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56468                 /** The SplitBar for this region 
56469                 * @type Roo.SplitBar */
56470                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56471                 this.split.on("moved", this.onSplitMove, this);
56472                 this.split.useShim = config.useShim === true;
56473                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56474                 if(this.useSplitTips){
56475                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56476                 }
56477                 if(config.collapsible){
56478                     this.split.el.on("dblclick", this.collapse,  this);
56479                 }
56480             }
56481             if(typeof config.minSize != "undefined"){
56482                 this.split.minSize = config.minSize;
56483             }
56484             if(typeof config.maxSize != "undefined"){
56485                 this.split.maxSize = config.maxSize;
56486             }
56487             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56488                 this.hideSplitter();
56489             }
56490         }
56491     },
56492
56493     getHMaxSize : function(){
56494          var cmax = this.config.maxSize || 10000;
56495          var center = this.mgr.getRegion("center");
56496          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56497     },
56498
56499     getVMaxSize : function(){
56500          var cmax = this.config.maxSize || 10000;
56501          var center = this.mgr.getRegion("center");
56502          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56503     },
56504
56505     onSplitMove : function(split, newSize){
56506         this.fireEvent("resized", this, newSize);
56507     },
56508     
56509     /** 
56510      * Returns the {@link Roo.SplitBar} for this region.
56511      * @return {Roo.SplitBar}
56512      */
56513     getSplitBar : function(){
56514         return this.split;
56515     },
56516     
56517     hide : function(){
56518         this.hideSplitter();
56519         Roo.SplitLayoutRegion.superclass.hide.call(this);
56520     },
56521
56522     hideSplitter : function(){
56523         if(this.split){
56524             this.split.el.setLocation(-2000,-2000);
56525             this.split.el.hide();
56526         }
56527     },
56528
56529     show : function(){
56530         if(this.split){
56531             this.split.el.show();
56532         }
56533         Roo.SplitLayoutRegion.superclass.show.call(this);
56534     },
56535     
56536     beforeSlide: function(){
56537         if(Roo.isGecko){// firefox overflow auto bug workaround
56538             this.bodyEl.clip();
56539             if(this.tabs) {
56540                 this.tabs.bodyEl.clip();
56541             }
56542             if(this.activePanel){
56543                 this.activePanel.getEl().clip();
56544                 
56545                 if(this.activePanel.beforeSlide){
56546                     this.activePanel.beforeSlide();
56547                 }
56548             }
56549         }
56550     },
56551     
56552     afterSlide : function(){
56553         if(Roo.isGecko){// firefox overflow auto bug workaround
56554             this.bodyEl.unclip();
56555             if(this.tabs) {
56556                 this.tabs.bodyEl.unclip();
56557             }
56558             if(this.activePanel){
56559                 this.activePanel.getEl().unclip();
56560                 if(this.activePanel.afterSlide){
56561                     this.activePanel.afterSlide();
56562                 }
56563             }
56564         }
56565     },
56566
56567     initAutoHide : function(){
56568         if(this.autoHide !== false){
56569             if(!this.autoHideHd){
56570                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56571                 this.autoHideHd = {
56572                     "mouseout": function(e){
56573                         if(!e.within(this.el, true)){
56574                             st.delay(500);
56575                         }
56576                     },
56577                     "mouseover" : function(e){
56578                         st.cancel();
56579                     },
56580                     scope : this
56581                 };
56582             }
56583             this.el.on(this.autoHideHd);
56584         }
56585     },
56586
56587     clearAutoHide : function(){
56588         if(this.autoHide !== false){
56589             this.el.un("mouseout", this.autoHideHd.mouseout);
56590             this.el.un("mouseover", this.autoHideHd.mouseover);
56591         }
56592     },
56593
56594     clearMonitor : function(){
56595         Roo.get(document).un("click", this.slideInIf, this);
56596     },
56597
56598     // these names are backwards but not changed for compat
56599     slideOut : function(){
56600         if(this.isSlid || this.el.hasActiveFx()){
56601             return;
56602         }
56603         this.isSlid = true;
56604         if(this.collapseBtn){
56605             this.collapseBtn.hide();
56606         }
56607         this.closeBtnState = this.closeBtn.getStyle('display');
56608         this.closeBtn.hide();
56609         if(this.stickBtn){
56610             this.stickBtn.show();
56611         }
56612         this.el.show();
56613         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56614         this.beforeSlide();
56615         this.el.setStyle("z-index", 10001);
56616         this.el.slideIn(this.getSlideAnchor(), {
56617             callback: function(){
56618                 this.afterSlide();
56619                 this.initAutoHide();
56620                 Roo.get(document).on("click", this.slideInIf, this);
56621                 this.fireEvent("slideshow", this);
56622             },
56623             scope: this,
56624             block: true
56625         });
56626     },
56627
56628     afterSlideIn : function(){
56629         this.clearAutoHide();
56630         this.isSlid = false;
56631         this.clearMonitor();
56632         this.el.setStyle("z-index", "");
56633         if(this.collapseBtn){
56634             this.collapseBtn.show();
56635         }
56636         this.closeBtn.setStyle('display', this.closeBtnState);
56637         if(this.stickBtn){
56638             this.stickBtn.hide();
56639         }
56640         this.fireEvent("slidehide", this);
56641     },
56642
56643     slideIn : function(cb){
56644         if(!this.isSlid || this.el.hasActiveFx()){
56645             Roo.callback(cb);
56646             return;
56647         }
56648         this.isSlid = false;
56649         this.beforeSlide();
56650         this.el.slideOut(this.getSlideAnchor(), {
56651             callback: function(){
56652                 this.el.setLeftTop(-10000, -10000);
56653                 this.afterSlide();
56654                 this.afterSlideIn();
56655                 Roo.callback(cb);
56656             },
56657             scope: this,
56658             block: true
56659         });
56660     },
56661     
56662     slideInIf : function(e){
56663         if(!e.within(this.el)){
56664             this.slideIn();
56665         }
56666     },
56667
56668     animateCollapse : function(){
56669         this.beforeSlide();
56670         this.el.setStyle("z-index", 20000);
56671         var anchor = this.getSlideAnchor();
56672         this.el.slideOut(anchor, {
56673             callback : function(){
56674                 this.el.setStyle("z-index", "");
56675                 this.collapsedEl.slideIn(anchor, {duration:.3});
56676                 this.afterSlide();
56677                 this.el.setLocation(-10000,-10000);
56678                 this.el.hide();
56679                 this.fireEvent("collapsed", this);
56680             },
56681             scope: this,
56682             block: true
56683         });
56684     },
56685
56686     animateExpand : function(){
56687         this.beforeSlide();
56688         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56689         this.el.setStyle("z-index", 20000);
56690         this.collapsedEl.hide({
56691             duration:.1
56692         });
56693         this.el.slideIn(this.getSlideAnchor(), {
56694             callback : function(){
56695                 this.el.setStyle("z-index", "");
56696                 this.afterSlide();
56697                 if(this.split){
56698                     this.split.el.show();
56699                 }
56700                 this.fireEvent("invalidated", this);
56701                 this.fireEvent("expanded", this);
56702             },
56703             scope: this,
56704             block: true
56705         });
56706     },
56707
56708     anchors : {
56709         "west" : "left",
56710         "east" : "right",
56711         "north" : "top",
56712         "south" : "bottom"
56713     },
56714
56715     sanchors : {
56716         "west" : "l",
56717         "east" : "r",
56718         "north" : "t",
56719         "south" : "b"
56720     },
56721
56722     canchors : {
56723         "west" : "tl-tr",
56724         "east" : "tr-tl",
56725         "north" : "tl-bl",
56726         "south" : "bl-tl"
56727     },
56728
56729     getAnchor : function(){
56730         return this.anchors[this.position];
56731     },
56732
56733     getCollapseAnchor : function(){
56734         return this.canchors[this.position];
56735     },
56736
56737     getSlideAnchor : function(){
56738         return this.sanchors[this.position];
56739     },
56740
56741     getAlignAdj : function(){
56742         var cm = this.cmargins;
56743         switch(this.position){
56744             case "west":
56745                 return [0, 0];
56746             break;
56747             case "east":
56748                 return [0, 0];
56749             break;
56750             case "north":
56751                 return [0, 0];
56752             break;
56753             case "south":
56754                 return [0, 0];
56755             break;
56756         }
56757     },
56758
56759     getExpandAdj : function(){
56760         var c = this.collapsedEl, cm = this.cmargins;
56761         switch(this.position){
56762             case "west":
56763                 return [-(cm.right+c.getWidth()+cm.left), 0];
56764             break;
56765             case "east":
56766                 return [cm.right+c.getWidth()+cm.left, 0];
56767             break;
56768             case "north":
56769                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56770             break;
56771             case "south":
56772                 return [0, cm.top+cm.bottom+c.getHeight()];
56773             break;
56774         }
56775     }
56776 });/*
56777  * Based on:
56778  * Ext JS Library 1.1.1
56779  * Copyright(c) 2006-2007, Ext JS, LLC.
56780  *
56781  * Originally Released Under LGPL - original licence link has changed is not relivant.
56782  *
56783  * Fork - LGPL
56784  * <script type="text/javascript">
56785  */
56786 /*
56787  * These classes are private internal classes
56788  */
56789 Roo.CenterLayoutRegion = function(mgr, config){
56790     Roo.LayoutRegion.call(this, mgr, config, "center");
56791     this.visible = true;
56792     this.minWidth = config.minWidth || 20;
56793     this.minHeight = config.minHeight || 20;
56794 };
56795
56796 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56797     hide : function(){
56798         // center panel can't be hidden
56799     },
56800     
56801     show : function(){
56802         // center panel can't be hidden
56803     },
56804     
56805     getMinWidth: function(){
56806         return this.minWidth;
56807     },
56808     
56809     getMinHeight: function(){
56810         return this.minHeight;
56811     }
56812 });
56813
56814
56815 Roo.NorthLayoutRegion = function(mgr, config){
56816     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56817     if(this.split){
56818         this.split.placement = Roo.SplitBar.TOP;
56819         this.split.orientation = Roo.SplitBar.VERTICAL;
56820         this.split.el.addClass("x-layout-split-v");
56821     }
56822     var size = config.initialSize || config.height;
56823     if(typeof size != "undefined"){
56824         this.el.setHeight(size);
56825     }
56826 };
56827 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56828     orientation: Roo.SplitBar.VERTICAL,
56829     getBox : function(){
56830         if(this.collapsed){
56831             return this.collapsedEl.getBox();
56832         }
56833         var box = this.el.getBox();
56834         if(this.split){
56835             box.height += this.split.el.getHeight();
56836         }
56837         return box;
56838     },
56839     
56840     updateBox : function(box){
56841         if(this.split && !this.collapsed){
56842             box.height -= this.split.el.getHeight();
56843             this.split.el.setLeft(box.x);
56844             this.split.el.setTop(box.y+box.height);
56845             this.split.el.setWidth(box.width);
56846         }
56847         if(this.collapsed){
56848             this.updateBody(box.width, null);
56849         }
56850         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56851     }
56852 });
56853
56854 Roo.SouthLayoutRegion = function(mgr, config){
56855     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56856     if(this.split){
56857         this.split.placement = Roo.SplitBar.BOTTOM;
56858         this.split.orientation = Roo.SplitBar.VERTICAL;
56859         this.split.el.addClass("x-layout-split-v");
56860     }
56861     var size = config.initialSize || config.height;
56862     if(typeof size != "undefined"){
56863         this.el.setHeight(size);
56864     }
56865 };
56866 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56867     orientation: Roo.SplitBar.VERTICAL,
56868     getBox : function(){
56869         if(this.collapsed){
56870             return this.collapsedEl.getBox();
56871         }
56872         var box = this.el.getBox();
56873         if(this.split){
56874             var sh = this.split.el.getHeight();
56875             box.height += sh;
56876             box.y -= sh;
56877         }
56878         return box;
56879     },
56880     
56881     updateBox : function(box){
56882         if(this.split && !this.collapsed){
56883             var sh = this.split.el.getHeight();
56884             box.height -= sh;
56885             box.y += sh;
56886             this.split.el.setLeft(box.x);
56887             this.split.el.setTop(box.y-sh);
56888             this.split.el.setWidth(box.width);
56889         }
56890         if(this.collapsed){
56891             this.updateBody(box.width, null);
56892         }
56893         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56894     }
56895 });
56896
56897 Roo.EastLayoutRegion = function(mgr, config){
56898     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56899     if(this.split){
56900         this.split.placement = Roo.SplitBar.RIGHT;
56901         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56902         this.split.el.addClass("x-layout-split-h");
56903     }
56904     var size = config.initialSize || config.width;
56905     if(typeof size != "undefined"){
56906         this.el.setWidth(size);
56907     }
56908 };
56909 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56910     orientation: Roo.SplitBar.HORIZONTAL,
56911     getBox : function(){
56912         if(this.collapsed){
56913             return this.collapsedEl.getBox();
56914         }
56915         var box = this.el.getBox();
56916         if(this.split){
56917             var sw = this.split.el.getWidth();
56918             box.width += sw;
56919             box.x -= sw;
56920         }
56921         return box;
56922     },
56923
56924     updateBox : function(box){
56925         if(this.split && !this.collapsed){
56926             var sw = this.split.el.getWidth();
56927             box.width -= sw;
56928             this.split.el.setLeft(box.x);
56929             this.split.el.setTop(box.y);
56930             this.split.el.setHeight(box.height);
56931             box.x += sw;
56932         }
56933         if(this.collapsed){
56934             this.updateBody(null, box.height);
56935         }
56936         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56937     }
56938 });
56939
56940 Roo.WestLayoutRegion = function(mgr, config){
56941     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56942     if(this.split){
56943         this.split.placement = Roo.SplitBar.LEFT;
56944         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56945         this.split.el.addClass("x-layout-split-h");
56946     }
56947     var size = config.initialSize || config.width;
56948     if(typeof size != "undefined"){
56949         this.el.setWidth(size);
56950     }
56951 };
56952 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56953     orientation: Roo.SplitBar.HORIZONTAL,
56954     getBox : function(){
56955         if(this.collapsed){
56956             return this.collapsedEl.getBox();
56957         }
56958         var box = this.el.getBox();
56959         if(this.split){
56960             box.width += this.split.el.getWidth();
56961         }
56962         return box;
56963     },
56964     
56965     updateBox : function(box){
56966         if(this.split && !this.collapsed){
56967             var sw = this.split.el.getWidth();
56968             box.width -= sw;
56969             this.split.el.setLeft(box.x+box.width);
56970             this.split.el.setTop(box.y);
56971             this.split.el.setHeight(box.height);
56972         }
56973         if(this.collapsed){
56974             this.updateBody(null, box.height);
56975         }
56976         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56977     }
56978 });
56979 /*
56980  * Based on:
56981  * Ext JS Library 1.1.1
56982  * Copyright(c) 2006-2007, Ext JS, LLC.
56983  *
56984  * Originally Released Under LGPL - original licence link has changed is not relivant.
56985  *
56986  * Fork - LGPL
56987  * <script type="text/javascript">
56988  */
56989  
56990  
56991 /*
56992  * Private internal class for reading and applying state
56993  */
56994 Roo.LayoutStateManager = function(layout){
56995      // default empty state
56996      this.state = {
56997         north: {},
56998         south: {},
56999         east: {},
57000         west: {}       
57001     };
57002 };
57003
57004 Roo.LayoutStateManager.prototype = {
57005     init : function(layout, provider){
57006         this.provider = provider;
57007         var state = provider.get(layout.id+"-layout-state");
57008         if(state){
57009             var wasUpdating = layout.isUpdating();
57010             if(!wasUpdating){
57011                 layout.beginUpdate();
57012             }
57013             for(var key in state){
57014                 if(typeof state[key] != "function"){
57015                     var rstate = state[key];
57016                     var r = layout.getRegion(key);
57017                     if(r && rstate){
57018                         if(rstate.size){
57019                             r.resizeTo(rstate.size);
57020                         }
57021                         if(rstate.collapsed == true){
57022                             r.collapse(true);
57023                         }else{
57024                             r.expand(null, true);
57025                         }
57026                     }
57027                 }
57028             }
57029             if(!wasUpdating){
57030                 layout.endUpdate();
57031             }
57032             this.state = state; 
57033         }
57034         this.layout = layout;
57035         layout.on("regionresized", this.onRegionResized, this);
57036         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57037         layout.on("regionexpanded", this.onRegionExpanded, this);
57038     },
57039     
57040     storeState : function(){
57041         this.provider.set(this.layout.id+"-layout-state", this.state);
57042     },
57043     
57044     onRegionResized : function(region, newSize){
57045         this.state[region.getPosition()].size = newSize;
57046         this.storeState();
57047     },
57048     
57049     onRegionCollapsed : function(region){
57050         this.state[region.getPosition()].collapsed = true;
57051         this.storeState();
57052     },
57053     
57054     onRegionExpanded : function(region){
57055         this.state[region.getPosition()].collapsed = false;
57056         this.storeState();
57057     }
57058 };/*
57059  * Based on:
57060  * Ext JS Library 1.1.1
57061  * Copyright(c) 2006-2007, Ext JS, LLC.
57062  *
57063  * Originally Released Under LGPL - original licence link has changed is not relivant.
57064  *
57065  * Fork - LGPL
57066  * <script type="text/javascript">
57067  */
57068 /**
57069  * @class Roo.ContentPanel
57070  * @extends Roo.util.Observable
57071  * @children Roo.form.Form Roo.JsonView Roo.View
57072  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57073  * A basic ContentPanel element.
57074  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57075  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57076  * @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
57077  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57078  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57079  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57080  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57081  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57082  * @cfg {String} title          The title for this panel
57083  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57084  * @cfg {String} url            Calls {@link #setUrl} with this value
57085  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57086  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57087  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57088  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57089  * @cfg {String}    style  Extra style to add to the content panel
57090  * @cfg {Roo.menu.Menu} menu  popup menu
57091
57092  * @constructor
57093  * Create a new ContentPanel.
57094  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57095  * @param {String/Object} config A string to set only the title or a config object
57096  * @param {String} content (optional) Set the HTML content for this panel
57097  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57098  */
57099 Roo.ContentPanel = function(el, config, content){
57100     
57101      
57102     /*
57103     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57104         config = el;
57105         el = Roo.id();
57106     }
57107     if (config && config.parentLayout) { 
57108         el = config.parentLayout.el.createChild(); 
57109     }
57110     */
57111     if(el.autoCreate){ // xtype is available if this is called from factory
57112         config = el;
57113         el = Roo.id();
57114     }
57115     this.el = Roo.get(el);
57116     if(!this.el && config && config.autoCreate){
57117         if(typeof config.autoCreate == "object"){
57118             if(!config.autoCreate.id){
57119                 config.autoCreate.id = config.id||el;
57120             }
57121             this.el = Roo.DomHelper.append(document.body,
57122                         config.autoCreate, true);
57123         }else{
57124             this.el = Roo.DomHelper.append(document.body,
57125                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57126         }
57127     }
57128     
57129     
57130     this.closable = false;
57131     this.loaded = false;
57132     this.active = false;
57133     if(typeof config == "string"){
57134         this.title = config;
57135     }else{
57136         Roo.apply(this, config);
57137     }
57138     
57139     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57140         this.wrapEl = this.el.wrap();
57141         this.toolbar.container = this.el.insertSibling(false, 'before');
57142         this.toolbar = new Roo.Toolbar(this.toolbar);
57143     }
57144     
57145     // xtype created footer. - not sure if will work as we normally have to render first..
57146     if (this.footer && !this.footer.el && this.footer.xtype) {
57147         if (!this.wrapEl) {
57148             this.wrapEl = this.el.wrap();
57149         }
57150     
57151         this.footer.container = this.wrapEl.createChild();
57152          
57153         this.footer = Roo.factory(this.footer, Roo);
57154         
57155     }
57156     
57157     if(this.resizeEl){
57158         this.resizeEl = Roo.get(this.resizeEl, true);
57159     }else{
57160         this.resizeEl = this.el;
57161     }
57162     // handle view.xtype
57163     
57164  
57165     
57166     
57167     this.addEvents({
57168         /**
57169          * @event activate
57170          * Fires when this panel is activated. 
57171          * @param {Roo.ContentPanel} this
57172          */
57173         "activate" : true,
57174         /**
57175          * @event deactivate
57176          * Fires when this panel is activated. 
57177          * @param {Roo.ContentPanel} this
57178          */
57179         "deactivate" : true,
57180
57181         /**
57182          * @event resize
57183          * Fires when this panel is resized if fitToFrame is true.
57184          * @param {Roo.ContentPanel} this
57185          * @param {Number} width The width after any component adjustments
57186          * @param {Number} height The height after any component adjustments
57187          */
57188         "resize" : true,
57189         
57190          /**
57191          * @event render
57192          * Fires when this tab is created
57193          * @param {Roo.ContentPanel} this
57194          */
57195         "render" : true
57196          
57197         
57198     });
57199     
57200
57201     
57202     
57203     if(this.autoScroll){
57204         this.resizeEl.setStyle("overflow", "auto");
57205     } else {
57206         // fix randome scrolling
57207         this.el.on('scroll', function() {
57208             Roo.log('fix random scolling');
57209             this.scrollTo('top',0); 
57210         });
57211     }
57212     content = content || this.content;
57213     if(content){
57214         this.setContent(content);
57215     }
57216     if(config && config.url){
57217         this.setUrl(this.url, this.params, this.loadOnce);
57218     }
57219     
57220     
57221     
57222     Roo.ContentPanel.superclass.constructor.call(this);
57223     
57224     if (this.view && typeof(this.view.xtype) != 'undefined') {
57225         this.view.el = this.el.appendChild(document.createElement("div"));
57226         this.view = Roo.factory(this.view); 
57227         this.view.render  &&  this.view.render(false, '');  
57228     }
57229     
57230     
57231     this.fireEvent('render', this);
57232 };
57233
57234 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57235     tabTip:'',
57236     setRegion : function(region){
57237         this.region = region;
57238         if(region){
57239            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57240         }else{
57241            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57242         } 
57243     },
57244     
57245     /**
57246      * Returns the toolbar for this Panel if one was configured. 
57247      * @return {Roo.Toolbar} 
57248      */
57249     getToolbar : function(){
57250         return this.toolbar;
57251     },
57252     
57253     setActiveState : function(active){
57254         this.active = active;
57255         if(!active){
57256             this.fireEvent("deactivate", this);
57257         }else{
57258             this.fireEvent("activate", this);
57259         }
57260     },
57261     /**
57262      * Updates this panel's element
57263      * @param {String} content The new content
57264      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57265     */
57266     setContent : function(content, loadScripts){
57267         this.el.update(content, loadScripts);
57268     },
57269
57270     ignoreResize : function(w, h){
57271         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57272             return true;
57273         }else{
57274             this.lastSize = {width: w, height: h};
57275             return false;
57276         }
57277     },
57278     /**
57279      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57280      * @return {Roo.UpdateManager} The UpdateManager
57281      */
57282     getUpdateManager : function(){
57283         return this.el.getUpdateManager();
57284     },
57285      /**
57286      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57287      * @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:
57288 <pre><code>
57289 panel.load({
57290     url: "your-url.php",
57291     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57292     callback: yourFunction,
57293     scope: yourObject, //(optional scope)
57294     discardUrl: false,
57295     nocache: false,
57296     text: "Loading...",
57297     timeout: 30,
57298     scripts: false
57299 });
57300 </code></pre>
57301      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57302      * 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.
57303      * @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}
57304      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57305      * @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.
57306      * @return {Roo.ContentPanel} this
57307      */
57308     load : function(){
57309         var um = this.el.getUpdateManager();
57310         um.update.apply(um, arguments);
57311         return this;
57312     },
57313
57314
57315     /**
57316      * 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.
57317      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57318      * @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)
57319      * @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)
57320      * @return {Roo.UpdateManager} The UpdateManager
57321      */
57322     setUrl : function(url, params, loadOnce){
57323         if(this.refreshDelegate){
57324             this.removeListener("activate", this.refreshDelegate);
57325         }
57326         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57327         this.on("activate", this.refreshDelegate);
57328         return this.el.getUpdateManager();
57329     },
57330     
57331     _handleRefresh : function(url, params, loadOnce){
57332         if(!loadOnce || !this.loaded){
57333             var updater = this.el.getUpdateManager();
57334             updater.update(url, params, this._setLoaded.createDelegate(this));
57335         }
57336     },
57337     
57338     _setLoaded : function(){
57339         this.loaded = true;
57340     }, 
57341     
57342     /**
57343      * Returns this panel's id
57344      * @return {String} 
57345      */
57346     getId : function(){
57347         return this.el.id;
57348     },
57349     
57350     /** 
57351      * Returns this panel's element - used by regiosn to add.
57352      * @return {Roo.Element} 
57353      */
57354     getEl : function(){
57355         return this.wrapEl || this.el;
57356     },
57357     
57358     adjustForComponents : function(width, height)
57359     {
57360         //Roo.log('adjustForComponents ');
57361         if(this.resizeEl != this.el){
57362             width -= this.el.getFrameWidth('lr');
57363             height -= this.el.getFrameWidth('tb');
57364         }
57365         if(this.toolbar){
57366             var te = this.toolbar.getEl();
57367             height -= te.getHeight();
57368             te.setWidth(width);
57369         }
57370         if(this.footer){
57371             var te = this.footer.getEl();
57372             //Roo.log("footer:" + te.getHeight());
57373             
57374             height -= te.getHeight();
57375             te.setWidth(width);
57376         }
57377         
57378         
57379         if(this.adjustments){
57380             width += this.adjustments[0];
57381             height += this.adjustments[1];
57382         }
57383         return {"width": width, "height": height};
57384     },
57385     
57386     setSize : function(width, height){
57387         if(this.fitToFrame && !this.ignoreResize(width, height)){
57388             if(this.fitContainer && this.resizeEl != this.el){
57389                 this.el.setSize(width, height);
57390             }
57391             var size = this.adjustForComponents(width, height);
57392             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57393             this.fireEvent('resize', this, size.width, size.height);
57394         }
57395     },
57396     
57397     /**
57398      * Returns this panel's title
57399      * @return {String} 
57400      */
57401     getTitle : function(){
57402         return this.title;
57403     },
57404     
57405     /**
57406      * Set this panel's title
57407      * @param {String} title
57408      */
57409     setTitle : function(title){
57410         this.title = title;
57411         if(this.region){
57412             this.region.updatePanelTitle(this, title);
57413         }
57414     },
57415     
57416     /**
57417      * Returns true is this panel was configured to be closable
57418      * @return {Boolean} 
57419      */
57420     isClosable : function(){
57421         return this.closable;
57422     },
57423     
57424     beforeSlide : function(){
57425         this.el.clip();
57426         this.resizeEl.clip();
57427     },
57428     
57429     afterSlide : function(){
57430         this.el.unclip();
57431         this.resizeEl.unclip();
57432     },
57433     
57434     /**
57435      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57436      *   Will fail silently if the {@link #setUrl} method has not been called.
57437      *   This does not activate the panel, just updates its content.
57438      */
57439     refresh : function(){
57440         if(this.refreshDelegate){
57441            this.loaded = false;
57442            this.refreshDelegate();
57443         }
57444     },
57445     
57446     /**
57447      * Destroys this panel
57448      */
57449     destroy : function(){
57450         this.el.removeAllListeners();
57451         var tempEl = document.createElement("span");
57452         tempEl.appendChild(this.el.dom);
57453         tempEl.innerHTML = "";
57454         this.el.remove();
57455         this.el = null;
57456     },
57457     
57458     /**
57459      * form - if the content panel contains a form - this is a reference to it.
57460      * @type {Roo.form.Form}
57461      */
57462     form : false,
57463     /**
57464      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57465      *    This contains a reference to it.
57466      * @type {Roo.View}
57467      */
57468     view : false,
57469     
57470       /**
57471      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57472      * <pre><code>
57473
57474 layout.addxtype({
57475        xtype : 'Form',
57476        items: [ .... ]
57477    }
57478 );
57479
57480 </code></pre>
57481      * @param {Object} cfg Xtype definition of item to add.
57482      */
57483     
57484     addxtype : function(cfg) {
57485         // add form..
57486         if (cfg.xtype.match(/^Form$/)) {
57487             
57488             var el;
57489             //if (this.footer) {
57490             //    el = this.footer.container.insertSibling(false, 'before');
57491             //} else {
57492                 el = this.el.createChild();
57493             //}
57494
57495             this.form = new  Roo.form.Form(cfg);
57496             
57497             
57498             if ( this.form.allItems.length) {
57499                 this.form.render(el.dom);
57500             }
57501             return this.form;
57502         }
57503         // should only have one of theses..
57504         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57505             // views.. should not be just added - used named prop 'view''
57506             
57507             cfg.el = this.el.appendChild(document.createElement("div"));
57508             // factory?
57509             
57510             var ret = new Roo.factory(cfg);
57511              
57512              ret.render && ret.render(false, ''); // render blank..
57513             this.view = ret;
57514             return ret;
57515         }
57516         return false;
57517     }
57518 });
57519
57520 /**
57521  * @class Roo.GridPanel
57522  * @extends Roo.ContentPanel
57523  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57524  * @constructor
57525  * Create a new GridPanel.
57526  * @cfg {Roo.grid.Grid} grid The grid for this panel
57527  */
57528 Roo.GridPanel = function(grid, config){
57529     
57530     // universal ctor...
57531     if (typeof(grid.grid) != 'undefined') {
57532         config = grid;
57533         grid = config.grid;
57534     }
57535     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57536         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57537         
57538     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57539     
57540     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57541     
57542     if(this.toolbar){
57543         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57544     }
57545     // xtype created footer. - not sure if will work as we normally have to render first..
57546     if (this.footer && !this.footer.el && this.footer.xtype) {
57547         
57548         this.footer.container = this.grid.getView().getFooterPanel(true);
57549         this.footer.dataSource = this.grid.dataSource;
57550         this.footer = Roo.factory(this.footer, Roo);
57551         
57552     }
57553     
57554     grid.monitorWindowResize = false; // turn off autosizing
57555     grid.autoHeight = false;
57556     grid.autoWidth = false;
57557     this.grid = grid;
57558     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57559 };
57560
57561 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57562     getId : function(){
57563         return this.grid.id;
57564     },
57565     
57566     /**
57567      * Returns the grid for this panel
57568      * @return {Roo.grid.Grid} 
57569      */
57570     getGrid : function(){
57571         return this.grid;    
57572     },
57573     
57574     setSize : function(width, height){
57575         if(!this.ignoreResize(width, height)){
57576             var grid = this.grid;
57577             var size = this.adjustForComponents(width, height);
57578             grid.getGridEl().setSize(size.width, size.height);
57579             grid.autoSize();
57580         }
57581     },
57582     
57583     beforeSlide : function(){
57584         this.grid.getView().scroller.clip();
57585     },
57586     
57587     afterSlide : function(){
57588         this.grid.getView().scroller.unclip();
57589     },
57590     
57591     destroy : function(){
57592         this.grid.destroy();
57593         delete this.grid;
57594         Roo.GridPanel.superclass.destroy.call(this); 
57595     }
57596 });
57597
57598
57599 /**
57600  * @class Roo.NestedLayoutPanel
57601  * @extends Roo.ContentPanel
57602  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57603  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57604  *
57605  * 
57606  * @constructor
57607  * Create a new NestedLayoutPanel.
57608  * 
57609  * 
57610  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57611  * @param {String/Object} config A string to set only the title or a config object
57612  */
57613 Roo.NestedLayoutPanel = function(layout, config)
57614 {
57615     // construct with only one argument..
57616     /* FIXME - implement nicer consturctors
57617     if (layout.layout) {
57618         config = layout;
57619         layout = config.layout;
57620         delete config.layout;
57621     }
57622     if (layout.xtype && !layout.getEl) {
57623         // then layout needs constructing..
57624         layout = Roo.factory(layout, Roo);
57625     }
57626     */
57627     
57628     
57629     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57630     
57631     layout.monitorWindowResize = false; // turn off autosizing
57632     this.layout = layout;
57633     this.layout.getEl().addClass("x-layout-nested-layout");
57634     
57635     
57636     
57637     
57638 };
57639
57640 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57641
57642     setSize : function(width, height){
57643         if(!this.ignoreResize(width, height)){
57644             var size = this.adjustForComponents(width, height);
57645             var el = this.layout.getEl();
57646             el.setSize(size.width, size.height);
57647             var touch = el.dom.offsetWidth;
57648             this.layout.layout();
57649             // ie requires a double layout on the first pass
57650             if(Roo.isIE && !this.initialized){
57651                 this.initialized = true;
57652                 this.layout.layout();
57653             }
57654         }
57655     },
57656     
57657     // activate all subpanels if not currently active..
57658     
57659     setActiveState : function(active){
57660         this.active = active;
57661         if(!active){
57662             this.fireEvent("deactivate", this);
57663             return;
57664         }
57665         
57666         this.fireEvent("activate", this);
57667         // not sure if this should happen before or after..
57668         if (!this.layout) {
57669             return; // should not happen..
57670         }
57671         var reg = false;
57672         for (var r in this.layout.regions) {
57673             reg = this.layout.getRegion(r);
57674             if (reg.getActivePanel()) {
57675                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57676                 reg.setActivePanel(reg.getActivePanel());
57677                 continue;
57678             }
57679             if (!reg.panels.length) {
57680                 continue;
57681             }
57682             reg.showPanel(reg.getPanel(0));
57683         }
57684         
57685         
57686         
57687         
57688     },
57689     
57690     /**
57691      * Returns the nested BorderLayout for this panel
57692      * @return {Roo.BorderLayout} 
57693      */
57694     getLayout : function(){
57695         return this.layout;
57696     },
57697     
57698      /**
57699      * Adds a xtype elements to the layout of the nested panel
57700      * <pre><code>
57701
57702 panel.addxtype({
57703        xtype : 'ContentPanel',
57704        region: 'west',
57705        items: [ .... ]
57706    }
57707 );
57708
57709 panel.addxtype({
57710         xtype : 'NestedLayoutPanel',
57711         region: 'west',
57712         layout: {
57713            center: { },
57714            west: { }   
57715         },
57716         items : [ ... list of content panels or nested layout panels.. ]
57717    }
57718 );
57719 </code></pre>
57720      * @param {Object} cfg Xtype definition of item to add.
57721      */
57722     addxtype : function(cfg) {
57723         return this.layout.addxtype(cfg);
57724     
57725     }
57726 });
57727
57728 Roo.ScrollPanel = function(el, config, content){
57729     config = config || {};
57730     config.fitToFrame = true;
57731     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57732     
57733     this.el.dom.style.overflow = "hidden";
57734     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57735     this.el.removeClass("x-layout-inactive-content");
57736     this.el.on("mousewheel", this.onWheel, this);
57737
57738     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57739     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57740     up.unselectable(); down.unselectable();
57741     up.on("click", this.scrollUp, this);
57742     down.on("click", this.scrollDown, this);
57743     up.addClassOnOver("x-scroller-btn-over");
57744     down.addClassOnOver("x-scroller-btn-over");
57745     up.addClassOnClick("x-scroller-btn-click");
57746     down.addClassOnClick("x-scroller-btn-click");
57747     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57748
57749     this.resizeEl = this.el;
57750     this.el = wrap; this.up = up; this.down = down;
57751 };
57752
57753 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57754     increment : 100,
57755     wheelIncrement : 5,
57756     scrollUp : function(){
57757         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57758     },
57759
57760     scrollDown : function(){
57761         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57762     },
57763
57764     afterScroll : function(){
57765         var el = this.resizeEl;
57766         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57767         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57768         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57769     },
57770
57771     setSize : function(){
57772         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57773         this.afterScroll();
57774     },
57775
57776     onWheel : function(e){
57777         var d = e.getWheelDelta();
57778         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57779         this.afterScroll();
57780         e.stopEvent();
57781     },
57782
57783     setContent : function(content, loadScripts){
57784         this.resizeEl.update(content, loadScripts);
57785     }
57786
57787 });
57788
57789
57790
57791 /**
57792  * @class Roo.TreePanel
57793  * @extends Roo.ContentPanel
57794  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57795  * Treepanel component
57796  * 
57797  * @constructor
57798  * Create a new TreePanel. - defaults to fit/scoll contents.
57799  * @param {String/Object} config A string to set only the panel's title, or a config object
57800  */
57801 Roo.TreePanel = function(config){
57802     var el = config.el;
57803     var tree = config.tree;
57804     delete config.tree; 
57805     delete config.el; // hopefull!
57806     
57807     // wrapper for IE7 strict & safari scroll issue
57808     
57809     var treeEl = el.createChild();
57810     config.resizeEl = treeEl;
57811     
57812     
57813     
57814     Roo.TreePanel.superclass.constructor.call(this, el, config);
57815  
57816  
57817     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57818     //console.log(tree);
57819     this.on('activate', function()
57820     {
57821         if (this.tree.rendered) {
57822             return;
57823         }
57824         //console.log('render tree');
57825         this.tree.render();
57826     });
57827     // this should not be needed.. - it's actually the 'el' that resizes?
57828     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57829     
57830     //this.on('resize',  function (cp, w, h) {
57831     //        this.tree.innerCt.setWidth(w);
57832     //        this.tree.innerCt.setHeight(h);
57833     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57834     //});
57835
57836         
57837     
57838 };
57839
57840 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57841     fitToFrame : true,
57842     autoScroll : true,
57843     /*
57844      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57845      */
57846     tree : false
57847
57848 });
57849
57850
57851
57852
57853
57854
57855
57856
57857
57858
57859
57860 /*
57861  * Based on:
57862  * Ext JS Library 1.1.1
57863  * Copyright(c) 2006-2007, Ext JS, LLC.
57864  *
57865  * Originally Released Under LGPL - original licence link has changed is not relivant.
57866  *
57867  * Fork - LGPL
57868  * <script type="text/javascript">
57869  */
57870  
57871
57872 /**
57873  * @class Roo.ReaderLayout
57874  * @extends Roo.BorderLayout
57875  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57876  * center region containing two nested regions (a top one for a list view and one for item preview below),
57877  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57878  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57879  * expedites the setup of the overall layout and regions for this common application style.
57880  * Example:
57881  <pre><code>
57882 var reader = new Roo.ReaderLayout();
57883 var CP = Roo.ContentPanel;  // shortcut for adding
57884
57885 reader.beginUpdate();
57886 reader.add("north", new CP("north", "North"));
57887 reader.add("west", new CP("west", {title: "West"}));
57888 reader.add("east", new CP("east", {title: "East"}));
57889
57890 reader.regions.listView.add(new CP("listView", "List"));
57891 reader.regions.preview.add(new CP("preview", "Preview"));
57892 reader.endUpdate();
57893 </code></pre>
57894 * @constructor
57895 * Create a new ReaderLayout
57896 * @param {Object} config Configuration options
57897 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57898 * document.body if omitted)
57899 */
57900 Roo.ReaderLayout = function(config, renderTo){
57901     var c = config || {size:{}};
57902     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57903         north: c.north !== false ? Roo.apply({
57904             split:false,
57905             initialSize: 32,
57906             titlebar: false
57907         }, c.north) : false,
57908         west: c.west !== false ? Roo.apply({
57909             split:true,
57910             initialSize: 200,
57911             minSize: 175,
57912             maxSize: 400,
57913             titlebar: true,
57914             collapsible: true,
57915             animate: true,
57916             margins:{left:5,right:0,bottom:5,top:5},
57917             cmargins:{left:5,right:5,bottom:5,top:5}
57918         }, c.west) : false,
57919         east: c.east !== false ? Roo.apply({
57920             split:true,
57921             initialSize: 200,
57922             minSize: 175,
57923             maxSize: 400,
57924             titlebar: true,
57925             collapsible: true,
57926             animate: true,
57927             margins:{left:0,right:5,bottom:5,top:5},
57928             cmargins:{left:5,right:5,bottom:5,top:5}
57929         }, c.east) : false,
57930         center: Roo.apply({
57931             tabPosition: 'top',
57932             autoScroll:false,
57933             closeOnTab: true,
57934             titlebar:false,
57935             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57936         }, c.center)
57937     });
57938
57939     this.el.addClass('x-reader');
57940
57941     this.beginUpdate();
57942
57943     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57944         south: c.preview !== false ? Roo.apply({
57945             split:true,
57946             initialSize: 200,
57947             minSize: 100,
57948             autoScroll:true,
57949             collapsible:true,
57950             titlebar: true,
57951             cmargins:{top:5,left:0, right:0, bottom:0}
57952         }, c.preview) : false,
57953         center: Roo.apply({
57954             autoScroll:false,
57955             titlebar:false,
57956             minHeight:200
57957         }, c.listView)
57958     });
57959     this.add('center', new Roo.NestedLayoutPanel(inner,
57960             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57961
57962     this.endUpdate();
57963
57964     this.regions.preview = inner.getRegion('south');
57965     this.regions.listView = inner.getRegion('center');
57966 };
57967
57968 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57969  * Based on:
57970  * Ext JS Library 1.1.1
57971  * Copyright(c) 2006-2007, Ext JS, LLC.
57972  *
57973  * Originally Released Under LGPL - original licence link has changed is not relivant.
57974  *
57975  * Fork - LGPL
57976  * <script type="text/javascript">
57977  */
57978  
57979 /**
57980  * @class Roo.grid.Grid
57981  * @extends Roo.util.Observable
57982  * This class represents the primary interface of a component based grid control.
57983  * <br><br>Usage:<pre><code>
57984  var grid = new Roo.grid.Grid("my-container-id", {
57985      ds: myDataStore,
57986      cm: myColModel,
57987      selModel: mySelectionModel,
57988      autoSizeColumns: true,
57989      monitorWindowResize: false,
57990      trackMouseOver: true
57991  });
57992  // set any options
57993  grid.render();
57994  * </code></pre>
57995  * <b>Common Problems:</b><br/>
57996  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57997  * element will correct this<br/>
57998  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57999  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
58000  * are unpredictable.<br/>
58001  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
58002  * grid to calculate dimensions/offsets.<br/>
58003   * @constructor
58004  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58005  * The container MUST have some type of size defined for the grid to fill. The container will be
58006  * automatically set to position relative if it isn't already.
58007  * @param {Object} config A config object that sets properties on this grid.
58008  */
58009 Roo.grid.Grid = function(container, config){
58010         // initialize the container
58011         this.container = Roo.get(container);
58012         this.container.update("");
58013         this.container.setStyle("overflow", "hidden");
58014     this.container.addClass('x-grid-container');
58015
58016     this.id = this.container.id;
58017
58018     Roo.apply(this, config);
58019     // check and correct shorthanded configs
58020     if(this.ds){
58021         this.dataSource = this.ds;
58022         delete this.ds;
58023     }
58024     if(this.cm){
58025         this.colModel = this.cm;
58026         delete this.cm;
58027     }
58028     if(this.sm){
58029         this.selModel = this.sm;
58030         delete this.sm;
58031     }
58032
58033     if (this.selModel) {
58034         this.selModel = Roo.factory(this.selModel, Roo.grid);
58035         this.sm = this.selModel;
58036         this.sm.xmodule = this.xmodule || false;
58037     }
58038     if (typeof(this.colModel.config) == 'undefined') {
58039         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58040         this.cm = this.colModel;
58041         this.cm.xmodule = this.xmodule || false;
58042     }
58043     if (this.dataSource) {
58044         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58045         this.ds = this.dataSource;
58046         this.ds.xmodule = this.xmodule || false;
58047          
58048     }
58049     
58050     
58051     
58052     if(this.width){
58053         this.container.setWidth(this.width);
58054     }
58055
58056     if(this.height){
58057         this.container.setHeight(this.height);
58058     }
58059     /** @private */
58060         this.addEvents({
58061         // raw events
58062         /**
58063          * @event click
58064          * The raw click event for the entire grid.
58065          * @param {Roo.EventObject} e
58066          */
58067         "click" : true,
58068         /**
58069          * @event dblclick
58070          * The raw dblclick event for the entire grid.
58071          * @param {Roo.EventObject} e
58072          */
58073         "dblclick" : true,
58074         /**
58075          * @event contextmenu
58076          * The raw contextmenu event for the entire grid.
58077          * @param {Roo.EventObject} e
58078          */
58079         "contextmenu" : true,
58080         /**
58081          * @event mousedown
58082          * The raw mousedown event for the entire grid.
58083          * @param {Roo.EventObject} e
58084          */
58085         "mousedown" : true,
58086         /**
58087          * @event mouseup
58088          * The raw mouseup event for the entire grid.
58089          * @param {Roo.EventObject} e
58090          */
58091         "mouseup" : true,
58092         /**
58093          * @event mouseover
58094          * The raw mouseover event for the entire grid.
58095          * @param {Roo.EventObject} e
58096          */
58097         "mouseover" : true,
58098         /**
58099          * @event mouseout
58100          * The raw mouseout event for the entire grid.
58101          * @param {Roo.EventObject} e
58102          */
58103         "mouseout" : true,
58104         /**
58105          * @event keypress
58106          * The raw keypress event for the entire grid.
58107          * @param {Roo.EventObject} e
58108          */
58109         "keypress" : true,
58110         /**
58111          * @event keydown
58112          * The raw keydown event for the entire grid.
58113          * @param {Roo.EventObject} e
58114          */
58115         "keydown" : true,
58116
58117         // custom events
58118
58119         /**
58120          * @event cellclick
58121          * Fires when a cell is clicked
58122          * @param {Grid} this
58123          * @param {Number} rowIndex
58124          * @param {Number} columnIndex
58125          * @param {Roo.EventObject} e
58126          */
58127         "cellclick" : true,
58128         /**
58129          * @event celldblclick
58130          * Fires when a cell is double clicked
58131          * @param {Grid} this
58132          * @param {Number} rowIndex
58133          * @param {Number} columnIndex
58134          * @param {Roo.EventObject} e
58135          */
58136         "celldblclick" : true,
58137         /**
58138          * @event rowclick
58139          * Fires when a row is clicked
58140          * @param {Grid} this
58141          * @param {Number} rowIndex
58142          * @param {Roo.EventObject} e
58143          */
58144         "rowclick" : true,
58145         /**
58146          * @event rowdblclick
58147          * Fires when a row is double clicked
58148          * @param {Grid} this
58149          * @param {Number} rowIndex
58150          * @param {Roo.EventObject} e
58151          */
58152         "rowdblclick" : true,
58153         /**
58154          * @event headerclick
58155          * Fires when a header is clicked
58156          * @param {Grid} this
58157          * @param {Number} columnIndex
58158          * @param {Roo.EventObject} e
58159          */
58160         "headerclick" : true,
58161         /**
58162          * @event headerdblclick
58163          * Fires when a header cell is double clicked
58164          * @param {Grid} this
58165          * @param {Number} columnIndex
58166          * @param {Roo.EventObject} e
58167          */
58168         "headerdblclick" : true,
58169         /**
58170          * @event rowcontextmenu
58171          * Fires when a row is right clicked
58172          * @param {Grid} this
58173          * @param {Number} rowIndex
58174          * @param {Roo.EventObject} e
58175          */
58176         "rowcontextmenu" : true,
58177         /**
58178          * @event cellcontextmenu
58179          * Fires when a cell is right clicked
58180          * @param {Grid} this
58181          * @param {Number} rowIndex
58182          * @param {Number} cellIndex
58183          * @param {Roo.EventObject} e
58184          */
58185          "cellcontextmenu" : true,
58186         /**
58187          * @event headercontextmenu
58188          * Fires when a header is right clicked
58189          * @param {Grid} this
58190          * @param {Number} columnIndex
58191          * @param {Roo.EventObject} e
58192          */
58193         "headercontextmenu" : true,
58194         /**
58195          * @event bodyscroll
58196          * Fires when the body element is scrolled
58197          * @param {Number} scrollLeft
58198          * @param {Number} scrollTop
58199          */
58200         "bodyscroll" : true,
58201         /**
58202          * @event columnresize
58203          * Fires when the user resizes a column
58204          * @param {Number} columnIndex
58205          * @param {Number} newSize
58206          */
58207         "columnresize" : true,
58208         /**
58209          * @event columnmove
58210          * Fires when the user moves a column
58211          * @param {Number} oldIndex
58212          * @param {Number} newIndex
58213          */
58214         "columnmove" : true,
58215         /**
58216          * @event startdrag
58217          * Fires when row(s) start being dragged
58218          * @param {Grid} this
58219          * @param {Roo.GridDD} dd The drag drop object
58220          * @param {event} e The raw browser event
58221          */
58222         "startdrag" : true,
58223         /**
58224          * @event enddrag
58225          * Fires when a drag operation is complete
58226          * @param {Grid} this
58227          * @param {Roo.GridDD} dd The drag drop object
58228          * @param {event} e The raw browser event
58229          */
58230         "enddrag" : true,
58231         /**
58232          * @event dragdrop
58233          * Fires when dragged row(s) are dropped on a valid DD target
58234          * @param {Grid} this
58235          * @param {Roo.GridDD} dd The drag drop object
58236          * @param {String} targetId The target drag drop object
58237          * @param {event} e The raw browser event
58238          */
58239         "dragdrop" : true,
58240         /**
58241          * @event dragover
58242          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58243          * @param {Grid} this
58244          * @param {Roo.GridDD} dd The drag drop object
58245          * @param {String} targetId The target drag drop object
58246          * @param {event} e The raw browser event
58247          */
58248         "dragover" : true,
58249         /**
58250          * @event dragenter
58251          *  Fires when the dragged row(s) first cross another DD target while being dragged
58252          * @param {Grid} this
58253          * @param {Roo.GridDD} dd The drag drop object
58254          * @param {String} targetId The target drag drop object
58255          * @param {event} e The raw browser event
58256          */
58257         "dragenter" : true,
58258         /**
58259          * @event dragout
58260          * Fires when the dragged row(s) leave another DD target while being dragged
58261          * @param {Grid} this
58262          * @param {Roo.GridDD} dd The drag drop object
58263          * @param {String} targetId The target drag drop object
58264          * @param {event} e The raw browser event
58265          */
58266         "dragout" : true,
58267         /**
58268          * @event rowclass
58269          * Fires when a row is rendered, so you can change add a style to it.
58270          * @param {GridView} gridview   The grid view
58271          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58272          */
58273         'rowclass' : true,
58274
58275         /**
58276          * @event render
58277          * Fires when the grid is rendered
58278          * @param {Grid} grid
58279          */
58280         'render' : true
58281     });
58282
58283     Roo.grid.Grid.superclass.constructor.call(this);
58284 };
58285 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58286     
58287     /**
58288          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58289          */
58290         /**
58291          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58292          */
58293         /**
58294          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58295          */
58296         /**
58297          * @cfg {Roo.grid.Store} ds The data store for the grid
58298          */
58299         /**
58300          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58301          */
58302         /**
58303      * @cfg {String} ddGroup - drag drop group.
58304      */
58305       /**
58306      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58307      */
58308
58309     /**
58310      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58311      */
58312     minColumnWidth : 25,
58313
58314     /**
58315      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58316      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58317      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58318      */
58319     autoSizeColumns : false,
58320
58321     /**
58322      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58323      */
58324     autoSizeHeaders : true,
58325
58326     /**
58327      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58328      */
58329     monitorWindowResize : true,
58330
58331     /**
58332      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58333      * rows measured to get a columns size. Default is 0 (all rows).
58334      */
58335     maxRowsToMeasure : 0,
58336
58337     /**
58338      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58339      */
58340     trackMouseOver : true,
58341
58342     /**
58343     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58344     */
58345       /**
58346     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58347     */
58348     
58349     /**
58350     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58351     */
58352     enableDragDrop : false,
58353     
58354     /**
58355     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58356     */
58357     enableColumnMove : true,
58358     
58359     /**
58360     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58361     */
58362     enableColumnHide : true,
58363     
58364     /**
58365     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58366     */
58367     enableRowHeightSync : false,
58368     
58369     /**
58370     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58371     */
58372     stripeRows : true,
58373     
58374     /**
58375     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58376     */
58377     autoHeight : false,
58378
58379     /**
58380      * @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.
58381      */
58382     autoExpandColumn : false,
58383
58384     /**
58385     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58386     * Default is 50.
58387     */
58388     autoExpandMin : 50,
58389
58390     /**
58391     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58392     */
58393     autoExpandMax : 1000,
58394
58395     /**
58396     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58397     */
58398     view : null,
58399
58400     /**
58401     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58402     */
58403     loadMask : false,
58404     /**
58405     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58406     */
58407     dropTarget: false,
58408      /**
58409     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58410     */ 
58411     sortColMenu : false,
58412     
58413     // private
58414     rendered : false,
58415
58416     /**
58417     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58418     * of a fixed width. Default is false.
58419     */
58420     /**
58421     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58422     */
58423     
58424     
58425     /**
58426     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58427     * %0 is replaced with the number of selected rows.
58428     */
58429     ddText : "{0} selected row{1}",
58430     
58431     
58432     /**
58433      * Called once after all setup has been completed and the grid is ready to be rendered.
58434      * @return {Roo.grid.Grid} this
58435      */
58436     render : function()
58437     {
58438         var c = this.container;
58439         // try to detect autoHeight/width mode
58440         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58441             this.autoHeight = true;
58442         }
58443         var view = this.getView();
58444         view.init(this);
58445
58446         c.on("click", this.onClick, this);
58447         c.on("dblclick", this.onDblClick, this);
58448         c.on("contextmenu", this.onContextMenu, this);
58449         c.on("keydown", this.onKeyDown, this);
58450         if (Roo.isTouch) {
58451             c.on("touchstart", this.onTouchStart, this);
58452         }
58453
58454         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58455
58456         this.getSelectionModel().init(this);
58457
58458         view.render();
58459
58460         if(this.loadMask){
58461             this.loadMask = new Roo.LoadMask(this.container,
58462                     Roo.apply({store:this.dataSource}, this.loadMask));
58463         }
58464         
58465         
58466         if (this.toolbar && this.toolbar.xtype) {
58467             this.toolbar.container = this.getView().getHeaderPanel(true);
58468             this.toolbar = new Roo.Toolbar(this.toolbar);
58469         }
58470         if (this.footer && this.footer.xtype) {
58471             this.footer.dataSource = this.getDataSource();
58472             this.footer.container = this.getView().getFooterPanel(true);
58473             this.footer = Roo.factory(this.footer, Roo);
58474         }
58475         if (this.dropTarget && this.dropTarget.xtype) {
58476             delete this.dropTarget.xtype;
58477             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58478         }
58479         
58480         
58481         this.rendered = true;
58482         this.fireEvent('render', this);
58483         return this;
58484     },
58485
58486     /**
58487      * Reconfigures the grid to use a different Store and Column Model.
58488      * The View will be bound to the new objects and refreshed.
58489      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58490      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58491      */
58492     reconfigure : function(dataSource, colModel){
58493         if(this.loadMask){
58494             this.loadMask.destroy();
58495             this.loadMask = new Roo.LoadMask(this.container,
58496                     Roo.apply({store:dataSource}, this.loadMask));
58497         }
58498         this.view.bind(dataSource, colModel);
58499         this.dataSource = dataSource;
58500         this.colModel = colModel;
58501         this.view.refresh(true);
58502     },
58503     /**
58504      * addColumns
58505      * Add's a column, default at the end..
58506      
58507      * @param {int} position to add (default end)
58508      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58509      */
58510     addColumns : function(pos, ar)
58511     {
58512         
58513         for (var i =0;i< ar.length;i++) {
58514             var cfg = ar[i];
58515             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58516             this.cm.lookup[cfg.id] = cfg;
58517         }
58518         
58519         
58520         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58521             pos = this.cm.config.length; //this.cm.config.push(cfg);
58522         } 
58523         pos = Math.max(0,pos);
58524         ar.unshift(0);
58525         ar.unshift(pos);
58526         this.cm.config.splice.apply(this.cm.config, ar);
58527         
58528         
58529         
58530         this.view.generateRules(this.cm);
58531         this.view.refresh(true);
58532         
58533     },
58534     
58535     
58536     
58537     
58538     // private
58539     onKeyDown : function(e){
58540         this.fireEvent("keydown", e);
58541     },
58542
58543     /**
58544      * Destroy this grid.
58545      * @param {Boolean} removeEl True to remove the element
58546      */
58547     destroy : function(removeEl, keepListeners){
58548         if(this.loadMask){
58549             this.loadMask.destroy();
58550         }
58551         var c = this.container;
58552         c.removeAllListeners();
58553         this.view.destroy();
58554         this.colModel.purgeListeners();
58555         if(!keepListeners){
58556             this.purgeListeners();
58557         }
58558         c.update("");
58559         if(removeEl === true){
58560             c.remove();
58561         }
58562     },
58563
58564     // private
58565     processEvent : function(name, e){
58566         // does this fire select???
58567         //Roo.log('grid:processEvent '  + name);
58568         
58569         if (name != 'touchstart' ) {
58570             this.fireEvent(name, e);    
58571         }
58572         
58573         var t = e.getTarget();
58574         var v = this.view;
58575         var header = v.findHeaderIndex(t);
58576         if(header !== false){
58577             var ename = name == 'touchstart' ? 'click' : name;
58578              
58579             this.fireEvent("header" + ename, this, header, e);
58580         }else{
58581             var row = v.findRowIndex(t);
58582             var cell = v.findCellIndex(t);
58583             if (name == 'touchstart') {
58584                 // first touch is always a click.
58585                 // hopefull this happens after selection is updated.?
58586                 name = false;
58587                 
58588                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58589                     var cs = this.selModel.getSelectedCell();
58590                     if (row == cs[0] && cell == cs[1]){
58591                         name = 'dblclick';
58592                     }
58593                 }
58594                 if (typeof(this.selModel.getSelections) != 'undefined') {
58595                     var cs = this.selModel.getSelections();
58596                     var ds = this.dataSource;
58597                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58598                         name = 'dblclick';
58599                     }
58600                 }
58601                 if (!name) {
58602                     return;
58603                 }
58604             }
58605             
58606             
58607             if(row !== false){
58608                 this.fireEvent("row" + name, this, row, e);
58609                 if(cell !== false){
58610                     this.fireEvent("cell" + name, this, row, cell, e);
58611                 }
58612             }
58613         }
58614     },
58615
58616     // private
58617     onClick : function(e){
58618         this.processEvent("click", e);
58619     },
58620    // private
58621     onTouchStart : function(e){
58622         this.processEvent("touchstart", e);
58623     },
58624
58625     // private
58626     onContextMenu : function(e, t){
58627         this.processEvent("contextmenu", e);
58628     },
58629
58630     // private
58631     onDblClick : function(e){
58632         this.processEvent("dblclick", e);
58633     },
58634
58635     // private
58636     walkCells : function(row, col, step, fn, scope){
58637         var cm = this.colModel, clen = cm.getColumnCount();
58638         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58639         if(step < 0){
58640             if(col < 0){
58641                 row--;
58642                 first = false;
58643             }
58644             while(row >= 0){
58645                 if(!first){
58646                     col = clen-1;
58647                 }
58648                 first = false;
58649                 while(col >= 0){
58650                     if(fn.call(scope || this, row, col, cm) === true){
58651                         return [row, col];
58652                     }
58653                     col--;
58654                 }
58655                 row--;
58656             }
58657         } else {
58658             if(col >= clen){
58659                 row++;
58660                 first = false;
58661             }
58662             while(row < rlen){
58663                 if(!first){
58664                     col = 0;
58665                 }
58666                 first = false;
58667                 while(col < clen){
58668                     if(fn.call(scope || this, row, col, cm) === true){
58669                         return [row, col];
58670                     }
58671                     col++;
58672                 }
58673                 row++;
58674             }
58675         }
58676         return null;
58677     },
58678
58679     // private
58680     getSelections : function(){
58681         return this.selModel.getSelections();
58682     },
58683
58684     /**
58685      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58686      * but if manual update is required this method will initiate it.
58687      */
58688     autoSize : function(){
58689         if(this.rendered){
58690             this.view.layout();
58691             if(this.view.adjustForScroll){
58692                 this.view.adjustForScroll();
58693             }
58694         }
58695     },
58696
58697     /**
58698      * Returns the grid's underlying element.
58699      * @return {Element} The element
58700      */
58701     getGridEl : function(){
58702         return this.container;
58703     },
58704
58705     // private for compatibility, overridden by editor grid
58706     stopEditing : function(){},
58707
58708     /**
58709      * Returns the grid's SelectionModel.
58710      * @return {SelectionModel}
58711      */
58712     getSelectionModel : function(){
58713         if(!this.selModel){
58714             this.selModel = new Roo.grid.RowSelectionModel();
58715         }
58716         return this.selModel;
58717     },
58718
58719     /**
58720      * Returns the grid's DataSource.
58721      * @return {DataSource}
58722      */
58723     getDataSource : function(){
58724         return this.dataSource;
58725     },
58726
58727     /**
58728      * Returns the grid's ColumnModel.
58729      * @return {ColumnModel}
58730      */
58731     getColumnModel : function(){
58732         return this.colModel;
58733     },
58734
58735     /**
58736      * Returns the grid's GridView object.
58737      * @return {GridView}
58738      */
58739     getView : function(){
58740         if(!this.view){
58741             this.view = new Roo.grid.GridView(this.viewConfig);
58742             this.relayEvents(this.view, [
58743                 "beforerowremoved", "beforerowsinserted",
58744                 "beforerefresh", "rowremoved",
58745                 "rowsinserted", "rowupdated" ,"refresh"
58746             ]);
58747         }
58748         return this.view;
58749     },
58750     /**
58751      * Called to get grid's drag proxy text, by default returns this.ddText.
58752      * Override this to put something different in the dragged text.
58753      * @return {String}
58754      */
58755     getDragDropText : function(){
58756         var count = this.selModel.getCount();
58757         return String.format(this.ddText, count, count == 1 ? '' : 's');
58758     }
58759 });
58760 /*
58761  * Based on:
58762  * Ext JS Library 1.1.1
58763  * Copyright(c) 2006-2007, Ext JS, LLC.
58764  *
58765  * Originally Released Under LGPL - original licence link has changed is not relivant.
58766  *
58767  * Fork - LGPL
58768  * <script type="text/javascript">
58769  */
58770  /**
58771  * @class Roo.grid.AbstractGridView
58772  * @extends Roo.util.Observable
58773  * @abstract
58774  * Abstract base class for grid Views
58775  * @constructor
58776  */
58777 Roo.grid.AbstractGridView = function(){
58778         this.grid = null;
58779         
58780         this.events = {
58781             "beforerowremoved" : true,
58782             "beforerowsinserted" : true,
58783             "beforerefresh" : true,
58784             "rowremoved" : true,
58785             "rowsinserted" : true,
58786             "rowupdated" : true,
58787             "refresh" : true
58788         };
58789     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58790 };
58791
58792 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58793     rowClass : "x-grid-row",
58794     cellClass : "x-grid-cell",
58795     tdClass : "x-grid-td",
58796     hdClass : "x-grid-hd",
58797     splitClass : "x-grid-hd-split",
58798     
58799     init: function(grid){
58800         this.grid = grid;
58801                 var cid = this.grid.getGridEl().id;
58802         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58803         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58804         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58805         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58806         },
58807         
58808     getColumnRenderers : function(){
58809         var renderers = [];
58810         var cm = this.grid.colModel;
58811         var colCount = cm.getColumnCount();
58812         for(var i = 0; i < colCount; i++){
58813             renderers[i] = cm.getRenderer(i);
58814         }
58815         return renderers;
58816     },
58817     
58818     getColumnIds : function(){
58819         var ids = [];
58820         var cm = this.grid.colModel;
58821         var colCount = cm.getColumnCount();
58822         for(var i = 0; i < colCount; i++){
58823             ids[i] = cm.getColumnId(i);
58824         }
58825         return ids;
58826     },
58827     
58828     getDataIndexes : function(){
58829         if(!this.indexMap){
58830             this.indexMap = this.buildIndexMap();
58831         }
58832         return this.indexMap.colToData;
58833     },
58834     
58835     getColumnIndexByDataIndex : function(dataIndex){
58836         if(!this.indexMap){
58837             this.indexMap = this.buildIndexMap();
58838         }
58839         return this.indexMap.dataToCol[dataIndex];
58840     },
58841     
58842     /**
58843      * Set a css style for a column dynamically. 
58844      * @param {Number} colIndex The index of the column
58845      * @param {String} name The css property name
58846      * @param {String} value The css value
58847      */
58848     setCSSStyle : function(colIndex, name, value){
58849         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58850         Roo.util.CSS.updateRule(selector, name, value);
58851     },
58852     
58853     generateRules : function(cm){
58854         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58855         Roo.util.CSS.removeStyleSheet(rulesId);
58856         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58857             var cid = cm.getColumnId(i);
58858             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58859                          this.tdSelector, cid, " {\n}\n",
58860                          this.hdSelector, cid, " {\n}\n",
58861                          this.splitSelector, cid, " {\n}\n");
58862         }
58863         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58864     }
58865 });/*
58866  * Based on:
58867  * Ext JS Library 1.1.1
58868  * Copyright(c) 2006-2007, Ext JS, LLC.
58869  *
58870  * Originally Released Under LGPL - original licence link has changed is not relivant.
58871  *
58872  * Fork - LGPL
58873  * <script type="text/javascript">
58874  */
58875
58876 // private
58877 // This is a support class used internally by the Grid components
58878 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58879     this.grid = grid;
58880     this.view = grid.getView();
58881     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58882     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58883     if(hd2){
58884         this.setHandleElId(Roo.id(hd));
58885         this.setOuterHandleElId(Roo.id(hd2));
58886     }
58887     this.scroll = false;
58888 };
58889 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58890     maxDragWidth: 120,
58891     getDragData : function(e){
58892         var t = Roo.lib.Event.getTarget(e);
58893         var h = this.view.findHeaderCell(t);
58894         if(h){
58895             return {ddel: h.firstChild, header:h};
58896         }
58897         return false;
58898     },
58899
58900     onInitDrag : function(e){
58901         this.view.headersDisabled = true;
58902         var clone = this.dragData.ddel.cloneNode(true);
58903         clone.id = Roo.id();
58904         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58905         this.proxy.update(clone);
58906         return true;
58907     },
58908
58909     afterValidDrop : function(){
58910         var v = this.view;
58911         setTimeout(function(){
58912             v.headersDisabled = false;
58913         }, 50);
58914     },
58915
58916     afterInvalidDrop : function(){
58917         var v = this.view;
58918         setTimeout(function(){
58919             v.headersDisabled = false;
58920         }, 50);
58921     }
58922 });
58923 /*
58924  * Based on:
58925  * Ext JS Library 1.1.1
58926  * Copyright(c) 2006-2007, Ext JS, LLC.
58927  *
58928  * Originally Released Under LGPL - original licence link has changed is not relivant.
58929  *
58930  * Fork - LGPL
58931  * <script type="text/javascript">
58932  */
58933 // private
58934 // This is a support class used internally by the Grid components
58935 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58936     this.grid = grid;
58937     this.view = grid.getView();
58938     // split the proxies so they don't interfere with mouse events
58939     this.proxyTop = Roo.DomHelper.append(document.body, {
58940         cls:"col-move-top", html:"&#160;"
58941     }, true);
58942     this.proxyBottom = Roo.DomHelper.append(document.body, {
58943         cls:"col-move-bottom", html:"&#160;"
58944     }, true);
58945     this.proxyTop.hide = this.proxyBottom.hide = function(){
58946         this.setLeftTop(-100,-100);
58947         this.setStyle("visibility", "hidden");
58948     };
58949     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58950     // temporarily disabled
58951     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58952     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58953 };
58954 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58955     proxyOffsets : [-4, -9],
58956     fly: Roo.Element.fly,
58957
58958     getTargetFromEvent : function(e){
58959         var t = Roo.lib.Event.getTarget(e);
58960         var cindex = this.view.findCellIndex(t);
58961         if(cindex !== false){
58962             return this.view.getHeaderCell(cindex);
58963         }
58964         return null;
58965     },
58966
58967     nextVisible : function(h){
58968         var v = this.view, cm = this.grid.colModel;
58969         h = h.nextSibling;
58970         while(h){
58971             if(!cm.isHidden(v.getCellIndex(h))){
58972                 return h;
58973             }
58974             h = h.nextSibling;
58975         }
58976         return null;
58977     },
58978
58979     prevVisible : function(h){
58980         var v = this.view, cm = this.grid.colModel;
58981         h = h.prevSibling;
58982         while(h){
58983             if(!cm.isHidden(v.getCellIndex(h))){
58984                 return h;
58985             }
58986             h = h.prevSibling;
58987         }
58988         return null;
58989     },
58990
58991     positionIndicator : function(h, n, e){
58992         var x = Roo.lib.Event.getPageX(e);
58993         var r = Roo.lib.Dom.getRegion(n.firstChild);
58994         var px, pt, py = r.top + this.proxyOffsets[1];
58995         if((r.right - x) <= (r.right-r.left)/2){
58996             px = r.right+this.view.borderWidth;
58997             pt = "after";
58998         }else{
58999             px = r.left;
59000             pt = "before";
59001         }
59002         var oldIndex = this.view.getCellIndex(h);
59003         var newIndex = this.view.getCellIndex(n);
59004
59005         if(this.grid.colModel.isFixed(newIndex)){
59006             return false;
59007         }
59008
59009         var locked = this.grid.colModel.isLocked(newIndex);
59010
59011         if(pt == "after"){
59012             newIndex++;
59013         }
59014         if(oldIndex < newIndex){
59015             newIndex--;
59016         }
59017         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59018             return false;
59019         }
59020         px +=  this.proxyOffsets[0];
59021         this.proxyTop.setLeftTop(px, py);
59022         this.proxyTop.show();
59023         if(!this.bottomOffset){
59024             this.bottomOffset = this.view.mainHd.getHeight();
59025         }
59026         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59027         this.proxyBottom.show();
59028         return pt;
59029     },
59030
59031     onNodeEnter : function(n, dd, e, data){
59032         if(data.header != n){
59033             this.positionIndicator(data.header, n, e);
59034         }
59035     },
59036
59037     onNodeOver : function(n, dd, e, data){
59038         var result = false;
59039         if(data.header != n){
59040             result = this.positionIndicator(data.header, n, e);
59041         }
59042         if(!result){
59043             this.proxyTop.hide();
59044             this.proxyBottom.hide();
59045         }
59046         return result ? this.dropAllowed : this.dropNotAllowed;
59047     },
59048
59049     onNodeOut : function(n, dd, e, data){
59050         this.proxyTop.hide();
59051         this.proxyBottom.hide();
59052     },
59053
59054     onNodeDrop : function(n, dd, e, data){
59055         var h = data.header;
59056         if(h != n){
59057             var cm = this.grid.colModel;
59058             var x = Roo.lib.Event.getPageX(e);
59059             var r = Roo.lib.Dom.getRegion(n.firstChild);
59060             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59061             var oldIndex = this.view.getCellIndex(h);
59062             var newIndex = this.view.getCellIndex(n);
59063             var locked = cm.isLocked(newIndex);
59064             if(pt == "after"){
59065                 newIndex++;
59066             }
59067             if(oldIndex < newIndex){
59068                 newIndex--;
59069             }
59070             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59071                 return false;
59072             }
59073             cm.setLocked(oldIndex, locked, true);
59074             cm.moveColumn(oldIndex, newIndex);
59075             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59076             return true;
59077         }
59078         return false;
59079     }
59080 });
59081 /*
59082  * Based on:
59083  * Ext JS Library 1.1.1
59084  * Copyright(c) 2006-2007, Ext JS, LLC.
59085  *
59086  * Originally Released Under LGPL - original licence link has changed is not relivant.
59087  *
59088  * Fork - LGPL
59089  * <script type="text/javascript">
59090  */
59091   
59092 /**
59093  * @class Roo.grid.GridView
59094  * @extends Roo.util.Observable
59095  *
59096  * @constructor
59097  * @param {Object} config
59098  */
59099 Roo.grid.GridView = function(config){
59100     Roo.grid.GridView.superclass.constructor.call(this);
59101     this.el = null;
59102
59103     Roo.apply(this, config);
59104 };
59105
59106 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59107
59108     unselectable :  'unselectable="on"',
59109     unselectableCls :  'x-unselectable',
59110     
59111     
59112     rowClass : "x-grid-row",
59113
59114     cellClass : "x-grid-col",
59115
59116     tdClass : "x-grid-td",
59117
59118     hdClass : "x-grid-hd",
59119
59120     splitClass : "x-grid-split",
59121
59122     sortClasses : ["sort-asc", "sort-desc"],
59123
59124     enableMoveAnim : false,
59125
59126     hlColor: "C3DAF9",
59127
59128     dh : Roo.DomHelper,
59129
59130     fly : Roo.Element.fly,
59131
59132     css : Roo.util.CSS,
59133
59134     borderWidth: 1,
59135
59136     splitOffset: 3,
59137
59138     scrollIncrement : 22,
59139
59140     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59141
59142     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59143
59144     bind : function(ds, cm){
59145         if(this.ds){
59146             this.ds.un("load", this.onLoad, this);
59147             this.ds.un("datachanged", this.onDataChange, this);
59148             this.ds.un("add", this.onAdd, this);
59149             this.ds.un("remove", this.onRemove, this);
59150             this.ds.un("update", this.onUpdate, this);
59151             this.ds.un("clear", this.onClear, this);
59152         }
59153         if(ds){
59154             ds.on("load", this.onLoad, this);
59155             ds.on("datachanged", this.onDataChange, this);
59156             ds.on("add", this.onAdd, this);
59157             ds.on("remove", this.onRemove, this);
59158             ds.on("update", this.onUpdate, this);
59159             ds.on("clear", this.onClear, this);
59160         }
59161         this.ds = ds;
59162
59163         if(this.cm){
59164             this.cm.un("widthchange", this.onColWidthChange, this);
59165             this.cm.un("headerchange", this.onHeaderChange, this);
59166             this.cm.un("hiddenchange", this.onHiddenChange, this);
59167             this.cm.un("columnmoved", this.onColumnMove, this);
59168             this.cm.un("columnlockchange", this.onColumnLock, this);
59169         }
59170         if(cm){
59171             this.generateRules(cm);
59172             cm.on("widthchange", this.onColWidthChange, this);
59173             cm.on("headerchange", this.onHeaderChange, this);
59174             cm.on("hiddenchange", this.onHiddenChange, this);
59175             cm.on("columnmoved", this.onColumnMove, this);
59176             cm.on("columnlockchange", this.onColumnLock, this);
59177         }
59178         this.cm = cm;
59179     },
59180
59181     init: function(grid){
59182         Roo.grid.GridView.superclass.init.call(this, grid);
59183
59184         this.bind(grid.dataSource, grid.colModel);
59185
59186         grid.on("headerclick", this.handleHeaderClick, this);
59187
59188         if(grid.trackMouseOver){
59189             grid.on("mouseover", this.onRowOver, this);
59190             grid.on("mouseout", this.onRowOut, this);
59191         }
59192         grid.cancelTextSelection = function(){};
59193         this.gridId = grid.id;
59194
59195         var tpls = this.templates || {};
59196
59197         if(!tpls.master){
59198             tpls.master = new Roo.Template(
59199                '<div class="x-grid" hidefocus="true">',
59200                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59201                   '<div class="x-grid-topbar"></div>',
59202                   '<div class="x-grid-scroller"><div></div></div>',
59203                   '<div class="x-grid-locked">',
59204                       '<div class="x-grid-header">{lockedHeader}</div>',
59205                       '<div class="x-grid-body">{lockedBody}</div>',
59206                   "</div>",
59207                   '<div class="x-grid-viewport">',
59208                       '<div class="x-grid-header">{header}</div>',
59209                       '<div class="x-grid-body">{body}</div>',
59210                   "</div>",
59211                   '<div class="x-grid-bottombar"></div>',
59212                  
59213                   '<div class="x-grid-resize-proxy">&#160;</div>',
59214                "</div>"
59215             );
59216             tpls.master.disableformats = true;
59217         }
59218
59219         if(!tpls.header){
59220             tpls.header = new Roo.Template(
59221                '<table border="0" cellspacing="0" cellpadding="0">',
59222                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59223                "</table>{splits}"
59224             );
59225             tpls.header.disableformats = true;
59226         }
59227         tpls.header.compile();
59228
59229         if(!tpls.hcell){
59230             tpls.hcell = new Roo.Template(
59231                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59232                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59233                 "</div></td>"
59234              );
59235              tpls.hcell.disableFormats = true;
59236         }
59237         tpls.hcell.compile();
59238
59239         if(!tpls.hsplit){
59240             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59241                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59242             tpls.hsplit.disableFormats = true;
59243         }
59244         tpls.hsplit.compile();
59245
59246         if(!tpls.body){
59247             tpls.body = new Roo.Template(
59248                '<table border="0" cellspacing="0" cellpadding="0">',
59249                "<tbody>{rows}</tbody>",
59250                "</table>"
59251             );
59252             tpls.body.disableFormats = true;
59253         }
59254         tpls.body.compile();
59255
59256         if(!tpls.row){
59257             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59258             tpls.row.disableFormats = true;
59259         }
59260         tpls.row.compile();
59261
59262         if(!tpls.cell){
59263             tpls.cell = new Roo.Template(
59264                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59265                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59266                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59267                 "</td>"
59268             );
59269             tpls.cell.disableFormats = true;
59270         }
59271         tpls.cell.compile();
59272
59273         this.templates = tpls;
59274     },
59275
59276     // remap these for backwards compat
59277     onColWidthChange : function(){
59278         this.updateColumns.apply(this, arguments);
59279     },
59280     onHeaderChange : function(){
59281         this.updateHeaders.apply(this, arguments);
59282     }, 
59283     onHiddenChange : function(){
59284         this.handleHiddenChange.apply(this, arguments);
59285     },
59286     onColumnMove : function(){
59287         this.handleColumnMove.apply(this, arguments);
59288     },
59289     onColumnLock : function(){
59290         this.handleLockChange.apply(this, arguments);
59291     },
59292
59293     onDataChange : function(){
59294         this.refresh();
59295         this.updateHeaderSortState();
59296     },
59297
59298     onClear : function(){
59299         this.refresh();
59300     },
59301
59302     onUpdate : function(ds, record){
59303         this.refreshRow(record);
59304     },
59305
59306     refreshRow : function(record){
59307         var ds = this.ds, index;
59308         if(typeof record == 'number'){
59309             index = record;
59310             record = ds.getAt(index);
59311         }else{
59312             index = ds.indexOf(record);
59313         }
59314         this.insertRows(ds, index, index, true);
59315         this.onRemove(ds, record, index+1, true);
59316         this.syncRowHeights(index, index);
59317         this.layout();
59318         this.fireEvent("rowupdated", this, index, record);
59319     },
59320
59321     onAdd : function(ds, records, index){
59322         this.insertRows(ds, index, index + (records.length-1));
59323     },
59324
59325     onRemove : function(ds, record, index, isUpdate){
59326         if(isUpdate !== true){
59327             this.fireEvent("beforerowremoved", this, index, record);
59328         }
59329         var bt = this.getBodyTable(), lt = this.getLockedTable();
59330         if(bt.rows[index]){
59331             bt.firstChild.removeChild(bt.rows[index]);
59332         }
59333         if(lt.rows[index]){
59334             lt.firstChild.removeChild(lt.rows[index]);
59335         }
59336         if(isUpdate !== true){
59337             this.stripeRows(index);
59338             this.syncRowHeights(index, index);
59339             this.layout();
59340             this.fireEvent("rowremoved", this, index, record);
59341         }
59342     },
59343
59344     onLoad : function(){
59345         this.scrollToTop();
59346     },
59347
59348     /**
59349      * Scrolls the grid to the top
59350      */
59351     scrollToTop : function(){
59352         if(this.scroller){
59353             this.scroller.dom.scrollTop = 0;
59354             this.syncScroll();
59355         }
59356     },
59357
59358     /**
59359      * Gets a panel in the header of the grid that can be used for toolbars etc.
59360      * After modifying the contents of this panel a call to grid.autoSize() may be
59361      * required to register any changes in size.
59362      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59363      * @return Roo.Element
59364      */
59365     getHeaderPanel : function(doShow){
59366         if(doShow){
59367             this.headerPanel.show();
59368         }
59369         return this.headerPanel;
59370     },
59371
59372     /**
59373      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59374      * After modifying the contents of this panel a call to grid.autoSize() may be
59375      * required to register any changes in size.
59376      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59377      * @return Roo.Element
59378      */
59379     getFooterPanel : function(doShow){
59380         if(doShow){
59381             this.footerPanel.show();
59382         }
59383         return this.footerPanel;
59384     },
59385
59386     initElements : function(){
59387         var E = Roo.Element;
59388         var el = this.grid.getGridEl().dom.firstChild;
59389         var cs = el.childNodes;
59390
59391         this.el = new E(el);
59392         
59393          this.focusEl = new E(el.firstChild);
59394         this.focusEl.swallowEvent("click", true);
59395         
59396         this.headerPanel = new E(cs[1]);
59397         this.headerPanel.enableDisplayMode("block");
59398
59399         this.scroller = new E(cs[2]);
59400         this.scrollSizer = new E(this.scroller.dom.firstChild);
59401
59402         this.lockedWrap = new E(cs[3]);
59403         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59404         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59405
59406         this.mainWrap = new E(cs[4]);
59407         this.mainHd = new E(this.mainWrap.dom.firstChild);
59408         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59409
59410         this.footerPanel = new E(cs[5]);
59411         this.footerPanel.enableDisplayMode("block");
59412
59413         this.resizeProxy = new E(cs[6]);
59414
59415         this.headerSelector = String.format(
59416            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59417            this.lockedHd.id, this.mainHd.id
59418         );
59419
59420         this.splitterSelector = String.format(
59421            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59422            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59423         );
59424     },
59425     idToCssName : function(s)
59426     {
59427         return s.replace(/[^a-z0-9]+/ig, '-');
59428     },
59429
59430     getHeaderCell : function(index){
59431         return Roo.DomQuery.select(this.headerSelector)[index];
59432     },
59433
59434     getHeaderCellMeasure : function(index){
59435         return this.getHeaderCell(index).firstChild;
59436     },
59437
59438     getHeaderCellText : function(index){
59439         return this.getHeaderCell(index).firstChild.firstChild;
59440     },
59441
59442     getLockedTable : function(){
59443         return this.lockedBody.dom.firstChild;
59444     },
59445
59446     getBodyTable : function(){
59447         return this.mainBody.dom.firstChild;
59448     },
59449
59450     getLockedRow : function(index){
59451         return this.getLockedTable().rows[index];
59452     },
59453
59454     getRow : function(index){
59455         return this.getBodyTable().rows[index];
59456     },
59457
59458     getRowComposite : function(index){
59459         if(!this.rowEl){
59460             this.rowEl = new Roo.CompositeElementLite();
59461         }
59462         var els = [], lrow, mrow;
59463         if(lrow = this.getLockedRow(index)){
59464             els.push(lrow);
59465         }
59466         if(mrow = this.getRow(index)){
59467             els.push(mrow);
59468         }
59469         this.rowEl.elements = els;
59470         return this.rowEl;
59471     },
59472     /**
59473      * Gets the 'td' of the cell
59474      * 
59475      * @param {Integer} rowIndex row to select
59476      * @param {Integer} colIndex column to select
59477      * 
59478      * @return {Object} 
59479      */
59480     getCell : function(rowIndex, colIndex){
59481         var locked = this.cm.getLockedCount();
59482         var source;
59483         if(colIndex < locked){
59484             source = this.lockedBody.dom.firstChild;
59485         }else{
59486             source = this.mainBody.dom.firstChild;
59487             colIndex -= locked;
59488         }
59489         return source.rows[rowIndex].childNodes[colIndex];
59490     },
59491
59492     getCellText : function(rowIndex, colIndex){
59493         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59494     },
59495
59496     getCellBox : function(cell){
59497         var b = this.fly(cell).getBox();
59498         if(Roo.isOpera){ // opera fails to report the Y
59499             b.y = cell.offsetTop + this.mainBody.getY();
59500         }
59501         return b;
59502     },
59503
59504     getCellIndex : function(cell){
59505         var id = String(cell.className).match(this.cellRE);
59506         if(id){
59507             return parseInt(id[1], 10);
59508         }
59509         return 0;
59510     },
59511
59512     findHeaderIndex : function(n){
59513         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59514         return r ? this.getCellIndex(r) : false;
59515     },
59516
59517     findHeaderCell : function(n){
59518         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59519         return r ? r : false;
59520     },
59521
59522     findRowIndex : function(n){
59523         if(!n){
59524             return false;
59525         }
59526         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59527         return r ? r.rowIndex : false;
59528     },
59529
59530     findCellIndex : function(node){
59531         var stop = this.el.dom;
59532         while(node && node != stop){
59533             if(this.findRE.test(node.className)){
59534                 return this.getCellIndex(node);
59535             }
59536             node = node.parentNode;
59537         }
59538         return false;
59539     },
59540
59541     getColumnId : function(index){
59542         return this.cm.getColumnId(index);
59543     },
59544
59545     getSplitters : function()
59546     {
59547         if(this.splitterSelector){
59548            return Roo.DomQuery.select(this.splitterSelector);
59549         }else{
59550             return null;
59551       }
59552     },
59553
59554     getSplitter : function(index){
59555         return this.getSplitters()[index];
59556     },
59557
59558     onRowOver : function(e, t){
59559         var row;
59560         if((row = this.findRowIndex(t)) !== false){
59561             this.getRowComposite(row).addClass("x-grid-row-over");
59562         }
59563     },
59564
59565     onRowOut : function(e, t){
59566         var row;
59567         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59568             this.getRowComposite(row).removeClass("x-grid-row-over");
59569         }
59570     },
59571
59572     renderHeaders : function(){
59573         var cm = this.cm;
59574         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59575         var cb = [], lb = [], sb = [], lsb = [], p = {};
59576         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59577             p.cellId = "x-grid-hd-0-" + i;
59578             p.splitId = "x-grid-csplit-0-" + i;
59579             p.id = cm.getColumnId(i);
59580             p.value = cm.getColumnHeader(i) || "";
59581             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59582             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59583             if(!cm.isLocked(i)){
59584                 cb[cb.length] = ct.apply(p);
59585                 sb[sb.length] = st.apply(p);
59586             }else{
59587                 lb[lb.length] = ct.apply(p);
59588                 lsb[lsb.length] = st.apply(p);
59589             }
59590         }
59591         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59592                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59593     },
59594
59595     updateHeaders : function(){
59596         var html = this.renderHeaders();
59597         this.lockedHd.update(html[0]);
59598         this.mainHd.update(html[1]);
59599     },
59600
59601     /**
59602      * Focuses the specified row.
59603      * @param {Number} row The row index
59604      */
59605     focusRow : function(row)
59606     {
59607         //Roo.log('GridView.focusRow');
59608         var x = this.scroller.dom.scrollLeft;
59609         this.focusCell(row, 0, false);
59610         this.scroller.dom.scrollLeft = x;
59611     },
59612
59613     /**
59614      * Focuses the specified cell.
59615      * @param {Number} row The row index
59616      * @param {Number} col The column index
59617      * @param {Boolean} hscroll false to disable horizontal scrolling
59618      */
59619     focusCell : function(row, col, hscroll)
59620     {
59621         //Roo.log('GridView.focusCell');
59622         var el = this.ensureVisible(row, col, hscroll);
59623         this.focusEl.alignTo(el, "tl-tl");
59624         if(Roo.isGecko){
59625             this.focusEl.focus();
59626         }else{
59627             this.focusEl.focus.defer(1, this.focusEl);
59628         }
59629     },
59630
59631     /**
59632      * Scrolls the specified cell into view
59633      * @param {Number} row The row index
59634      * @param {Number} col The column index
59635      * @param {Boolean} hscroll false to disable horizontal scrolling
59636      */
59637     ensureVisible : function(row, col, hscroll)
59638     {
59639         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59640         //return null; //disable for testing.
59641         if(typeof row != "number"){
59642             row = row.rowIndex;
59643         }
59644         if(row < 0 && row >= this.ds.getCount()){
59645             return  null;
59646         }
59647         col = (col !== undefined ? col : 0);
59648         var cm = this.grid.colModel;
59649         while(cm.isHidden(col)){
59650             col++;
59651         }
59652
59653         var el = this.getCell(row, col);
59654         if(!el){
59655             return null;
59656         }
59657         var c = this.scroller.dom;
59658
59659         var ctop = parseInt(el.offsetTop, 10);
59660         var cleft = parseInt(el.offsetLeft, 10);
59661         var cbot = ctop + el.offsetHeight;
59662         var cright = cleft + el.offsetWidth;
59663         
59664         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59665         var stop = parseInt(c.scrollTop, 10);
59666         var sleft = parseInt(c.scrollLeft, 10);
59667         var sbot = stop + ch;
59668         var sright = sleft + c.clientWidth;
59669         /*
59670         Roo.log('GridView.ensureVisible:' +
59671                 ' ctop:' + ctop +
59672                 ' c.clientHeight:' + c.clientHeight +
59673                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59674                 ' stop:' + stop +
59675                 ' cbot:' + cbot +
59676                 ' sbot:' + sbot +
59677                 ' ch:' + ch  
59678                 );
59679         */
59680         if(ctop < stop){
59681             c.scrollTop = ctop;
59682             //Roo.log("set scrolltop to ctop DISABLE?");
59683         }else if(cbot > sbot){
59684             //Roo.log("set scrolltop to cbot-ch");
59685             c.scrollTop = cbot-ch;
59686         }
59687         
59688         if(hscroll !== false){
59689             if(cleft < sleft){
59690                 c.scrollLeft = cleft;
59691             }else if(cright > sright){
59692                 c.scrollLeft = cright-c.clientWidth;
59693             }
59694         }
59695          
59696         return el;
59697     },
59698
59699     updateColumns : function(){
59700         this.grid.stopEditing();
59701         var cm = this.grid.colModel, colIds = this.getColumnIds();
59702         //var totalWidth = cm.getTotalWidth();
59703         var pos = 0;
59704         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59705             //if(cm.isHidden(i)) continue;
59706             var w = cm.getColumnWidth(i);
59707             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59708             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59709         }
59710         this.updateSplitters();
59711     },
59712
59713     generateRules : function(cm){
59714         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59715         Roo.util.CSS.removeStyleSheet(rulesId);
59716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59717             var cid = cm.getColumnId(i);
59718             var align = '';
59719             if(cm.config[i].align){
59720                 align = 'text-align:'+cm.config[i].align+';';
59721             }
59722             var hidden = '';
59723             if(cm.isHidden(i)){
59724                 hidden = 'display:none;';
59725             }
59726             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59727             ruleBuf.push(
59728                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59729                     this.hdSelector, cid, " {\n", align, width, "}\n",
59730                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59731                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59732         }
59733         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59734     },
59735
59736     updateSplitters : function(){
59737         var cm = this.cm, s = this.getSplitters();
59738         if(s){ // splitters not created yet
59739             var pos = 0, locked = true;
59740             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59741                 if(cm.isHidden(i)) {
59742                     continue;
59743                 }
59744                 var w = cm.getColumnWidth(i); // make sure it's a number
59745                 if(!cm.isLocked(i) && locked){
59746                     pos = 0;
59747                     locked = false;
59748                 }
59749                 pos += w;
59750                 s[i].style.left = (pos-this.splitOffset) + "px";
59751             }
59752         }
59753     },
59754
59755     handleHiddenChange : function(colModel, colIndex, hidden){
59756         if(hidden){
59757             this.hideColumn(colIndex);
59758         }else{
59759             this.unhideColumn(colIndex);
59760         }
59761     },
59762
59763     hideColumn : function(colIndex){
59764         var cid = this.getColumnId(colIndex);
59765         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59766         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59767         if(Roo.isSafari){
59768             this.updateHeaders();
59769         }
59770         this.updateSplitters();
59771         this.layout();
59772     },
59773
59774     unhideColumn : function(colIndex){
59775         var cid = this.getColumnId(colIndex);
59776         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59777         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59778
59779         if(Roo.isSafari){
59780             this.updateHeaders();
59781         }
59782         this.updateSplitters();
59783         this.layout();
59784     },
59785
59786     insertRows : function(dm, firstRow, lastRow, isUpdate){
59787         if(firstRow == 0 && lastRow == dm.getCount()-1){
59788             this.refresh();
59789         }else{
59790             if(!isUpdate){
59791                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59792             }
59793             var s = this.getScrollState();
59794             var markup = this.renderRows(firstRow, lastRow);
59795             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59796             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59797             this.restoreScroll(s);
59798             if(!isUpdate){
59799                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59800                 this.syncRowHeights(firstRow, lastRow);
59801                 this.stripeRows(firstRow);
59802                 this.layout();
59803             }
59804         }
59805     },
59806
59807     bufferRows : function(markup, target, index){
59808         var before = null, trows = target.rows, tbody = target.tBodies[0];
59809         if(index < trows.length){
59810             before = trows[index];
59811         }
59812         var b = document.createElement("div");
59813         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59814         var rows = b.firstChild.rows;
59815         for(var i = 0, len = rows.length; i < len; i++){
59816             if(before){
59817                 tbody.insertBefore(rows[0], before);
59818             }else{
59819                 tbody.appendChild(rows[0]);
59820             }
59821         }
59822         b.innerHTML = "";
59823         b = null;
59824     },
59825
59826     deleteRows : function(dm, firstRow, lastRow){
59827         if(dm.getRowCount()<1){
59828             this.fireEvent("beforerefresh", this);
59829             this.mainBody.update("");
59830             this.lockedBody.update("");
59831             this.fireEvent("refresh", this);
59832         }else{
59833             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59834             var bt = this.getBodyTable();
59835             var tbody = bt.firstChild;
59836             var rows = bt.rows;
59837             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59838                 tbody.removeChild(rows[firstRow]);
59839             }
59840             this.stripeRows(firstRow);
59841             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59842         }
59843     },
59844
59845     updateRows : function(dataSource, firstRow, lastRow){
59846         var s = this.getScrollState();
59847         this.refresh();
59848         this.restoreScroll(s);
59849     },
59850
59851     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59852         if(!noRefresh){
59853            this.refresh();
59854         }
59855         this.updateHeaderSortState();
59856     },
59857
59858     getScrollState : function(){
59859         
59860         var sb = this.scroller.dom;
59861         return {left: sb.scrollLeft, top: sb.scrollTop};
59862     },
59863
59864     stripeRows : function(startRow){
59865         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59866             return;
59867         }
59868         startRow = startRow || 0;
59869         var rows = this.getBodyTable().rows;
59870         var lrows = this.getLockedTable().rows;
59871         var cls = ' x-grid-row-alt ';
59872         for(var i = startRow, len = rows.length; i < len; i++){
59873             var row = rows[i], lrow = lrows[i];
59874             var isAlt = ((i+1) % 2 == 0);
59875             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59876             if(isAlt == hasAlt){
59877                 continue;
59878             }
59879             if(isAlt){
59880                 row.className += " x-grid-row-alt";
59881             }else{
59882                 row.className = row.className.replace("x-grid-row-alt", "");
59883             }
59884             if(lrow){
59885                 lrow.className = row.className;
59886             }
59887         }
59888     },
59889
59890     restoreScroll : function(state){
59891         //Roo.log('GridView.restoreScroll');
59892         var sb = this.scroller.dom;
59893         sb.scrollLeft = state.left;
59894         sb.scrollTop = state.top;
59895         this.syncScroll();
59896     },
59897
59898     syncScroll : function(){
59899         //Roo.log('GridView.syncScroll');
59900         var sb = this.scroller.dom;
59901         var sh = this.mainHd.dom;
59902         var bs = this.mainBody.dom;
59903         var lv = this.lockedBody.dom;
59904         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59905         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59906     },
59907
59908     handleScroll : function(e){
59909         this.syncScroll();
59910         var sb = this.scroller.dom;
59911         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59912         e.stopEvent();
59913     },
59914
59915     handleWheel : function(e){
59916         var d = e.getWheelDelta();
59917         this.scroller.dom.scrollTop -= d*22;
59918         // set this here to prevent jumpy scrolling on large tables
59919         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59920         e.stopEvent();
59921     },
59922
59923     renderRows : function(startRow, endRow){
59924         // pull in all the crap needed to render rows
59925         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59926         var colCount = cm.getColumnCount();
59927
59928         if(ds.getCount() < 1){
59929             return ["", ""];
59930         }
59931
59932         // build a map for all the columns
59933         var cs = [];
59934         for(var i = 0; i < colCount; i++){
59935             var name = cm.getDataIndex(i);
59936             cs[i] = {
59937                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59938                 renderer : cm.getRenderer(i),
59939                 id : cm.getColumnId(i),
59940                 locked : cm.isLocked(i),
59941                 has_editor : cm.isCellEditable(i)
59942             };
59943         }
59944
59945         startRow = startRow || 0;
59946         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59947
59948         // records to render
59949         var rs = ds.getRange(startRow, endRow);
59950
59951         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59952     },
59953
59954     // As much as I hate to duplicate code, this was branched because FireFox really hates
59955     // [].join("") on strings. The performance difference was substantial enough to
59956     // branch this function
59957     doRender : Roo.isGecko ?
59958             function(cs, rs, ds, startRow, colCount, stripe){
59959                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59960                 // buffers
59961                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59962                 
59963                 var hasListener = this.grid.hasListener('rowclass');
59964                 var rowcfg = {};
59965                 for(var j = 0, len = rs.length; j < len; j++){
59966                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59967                     for(var i = 0; i < colCount; i++){
59968                         c = cs[i];
59969                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59970                         p.id = c.id;
59971                         p.css = p.attr = "";
59972                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59973                         if(p.value == undefined || p.value === "") {
59974                             p.value = "&#160;";
59975                         }
59976                         if(c.has_editor){
59977                             p.css += ' x-grid-editable-cell';
59978                         }
59979                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59980                             p.css +=  ' x-grid-dirty-cell';
59981                         }
59982                         var markup = ct.apply(p);
59983                         if(!c.locked){
59984                             cb+= markup;
59985                         }else{
59986                             lcb+= markup;
59987                         }
59988                     }
59989                     var alt = [];
59990                     if(stripe && ((rowIndex+1) % 2 == 0)){
59991                         alt.push("x-grid-row-alt")
59992                     }
59993                     if(r.dirty){
59994                         alt.push(  " x-grid-dirty-row");
59995                     }
59996                     rp.cells = lcb;
59997                     if(this.getRowClass){
59998                         alt.push(this.getRowClass(r, rowIndex));
59999                     }
60000                     if (hasListener) {
60001                         rowcfg = {
60002                              
60003                             record: r,
60004                             rowIndex : rowIndex,
60005                             rowClass : ''
60006                         };
60007                         this.grid.fireEvent('rowclass', this, rowcfg);
60008                         alt.push(rowcfg.rowClass);
60009                     }
60010                     rp.alt = alt.join(" ");
60011                     lbuf+= rt.apply(rp);
60012                     rp.cells = cb;
60013                     buf+=  rt.apply(rp);
60014                 }
60015                 return [lbuf, buf];
60016             } :
60017             function(cs, rs, ds, startRow, colCount, stripe){
60018                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60019                 // buffers
60020                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60021                 var hasListener = this.grid.hasListener('rowclass');
60022  
60023                 var rowcfg = {};
60024                 for(var j = 0, len = rs.length; j < len; j++){
60025                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60026                     for(var i = 0; i < colCount; i++){
60027                         c = cs[i];
60028                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60029                         p.id = c.id;
60030                         p.css = p.attr = "";
60031                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60032                         if(p.value == undefined || p.value === "") {
60033                             p.value = "&#160;";
60034                         }
60035                         //Roo.log(c);
60036                          if(c.has_editor){
60037                             p.css += ' x-grid-editable-cell';
60038                         }
60039                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60040                             p.css += ' x-grid-dirty-cell' 
60041                         }
60042                         
60043                         var markup = ct.apply(p);
60044                         if(!c.locked){
60045                             cb[cb.length] = markup;
60046                         }else{
60047                             lcb[lcb.length] = markup;
60048                         }
60049                     }
60050                     var alt = [];
60051                     if(stripe && ((rowIndex+1) % 2 == 0)){
60052                         alt.push( "x-grid-row-alt");
60053                     }
60054                     if(r.dirty){
60055                         alt.push(" x-grid-dirty-row");
60056                     }
60057                     rp.cells = lcb;
60058                     if(this.getRowClass){
60059                         alt.push( this.getRowClass(r, rowIndex));
60060                     }
60061                     if (hasListener) {
60062                         rowcfg = {
60063                              
60064                             record: r,
60065                             rowIndex : rowIndex,
60066                             rowClass : ''
60067                         };
60068                         this.grid.fireEvent('rowclass', this, rowcfg);
60069                         alt.push(rowcfg.rowClass);
60070                     }
60071                     
60072                     rp.alt = alt.join(" ");
60073                     rp.cells = lcb.join("");
60074                     lbuf[lbuf.length] = rt.apply(rp);
60075                     rp.cells = cb.join("");
60076                     buf[buf.length] =  rt.apply(rp);
60077                 }
60078                 return [lbuf.join(""), buf.join("")];
60079             },
60080
60081     renderBody : function(){
60082         var markup = this.renderRows();
60083         var bt = this.templates.body;
60084         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60085     },
60086
60087     /**
60088      * Refreshes the grid
60089      * @param {Boolean} headersToo
60090      */
60091     refresh : function(headersToo){
60092         this.fireEvent("beforerefresh", this);
60093         this.grid.stopEditing();
60094         var result = this.renderBody();
60095         this.lockedBody.update(result[0]);
60096         this.mainBody.update(result[1]);
60097         if(headersToo === true){
60098             this.updateHeaders();
60099             this.updateColumns();
60100             this.updateSplitters();
60101             this.updateHeaderSortState();
60102         }
60103         this.syncRowHeights();
60104         this.layout();
60105         this.fireEvent("refresh", this);
60106     },
60107
60108     handleColumnMove : function(cm, oldIndex, newIndex){
60109         this.indexMap = null;
60110         var s = this.getScrollState();
60111         this.refresh(true);
60112         this.restoreScroll(s);
60113         this.afterMove(newIndex);
60114     },
60115
60116     afterMove : function(colIndex){
60117         if(this.enableMoveAnim && Roo.enableFx){
60118             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60119         }
60120         // if multisort - fix sortOrder, and reload..
60121         if (this.grid.dataSource.multiSort) {
60122             // the we can call sort again..
60123             var dm = this.grid.dataSource;
60124             var cm = this.grid.colModel;
60125             var so = [];
60126             for(var i = 0; i < cm.config.length; i++ ) {
60127                 
60128                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60129                     continue; // dont' bother, it's not in sort list or being set.
60130                 }
60131                 
60132                 so.push(cm.config[i].dataIndex);
60133             };
60134             dm.sortOrder = so;
60135             dm.load(dm.lastOptions);
60136             
60137             
60138         }
60139         
60140     },
60141
60142     updateCell : function(dm, rowIndex, dataIndex){
60143         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60144         if(typeof colIndex == "undefined"){ // not present in grid
60145             return;
60146         }
60147         var cm = this.grid.colModel;
60148         var cell = this.getCell(rowIndex, colIndex);
60149         var cellText = this.getCellText(rowIndex, colIndex);
60150
60151         var p = {
60152             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60153             id : cm.getColumnId(colIndex),
60154             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60155         };
60156         var renderer = cm.getRenderer(colIndex);
60157         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60158         if(typeof val == "undefined" || val === "") {
60159             val = "&#160;";
60160         }
60161         cellText.innerHTML = val;
60162         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60163         this.syncRowHeights(rowIndex, rowIndex);
60164     },
60165
60166     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60167         var maxWidth = 0;
60168         if(this.grid.autoSizeHeaders){
60169             var h = this.getHeaderCellMeasure(colIndex);
60170             maxWidth = Math.max(maxWidth, h.scrollWidth);
60171         }
60172         var tb, index;
60173         if(this.cm.isLocked(colIndex)){
60174             tb = this.getLockedTable();
60175             index = colIndex;
60176         }else{
60177             tb = this.getBodyTable();
60178             index = colIndex - this.cm.getLockedCount();
60179         }
60180         if(tb && tb.rows){
60181             var rows = tb.rows;
60182             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60183             for(var i = 0; i < stopIndex; i++){
60184                 var cell = rows[i].childNodes[index].firstChild;
60185                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60186             }
60187         }
60188         return maxWidth + /*margin for error in IE*/ 5;
60189     },
60190     /**
60191      * Autofit a column to its content.
60192      * @param {Number} colIndex
60193      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60194      */
60195      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60196          if(this.cm.isHidden(colIndex)){
60197              return; // can't calc a hidden column
60198          }
60199         if(forceMinSize){
60200             var cid = this.cm.getColumnId(colIndex);
60201             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60202            if(this.grid.autoSizeHeaders){
60203                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60204            }
60205         }
60206         var newWidth = this.calcColumnWidth(colIndex);
60207         this.cm.setColumnWidth(colIndex,
60208             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60209         if(!suppressEvent){
60210             this.grid.fireEvent("columnresize", colIndex, newWidth);
60211         }
60212     },
60213
60214     /**
60215      * Autofits all columns to their content and then expands to fit any extra space in the grid
60216      */
60217      autoSizeColumns : function(){
60218         var cm = this.grid.colModel;
60219         var colCount = cm.getColumnCount();
60220         for(var i = 0; i < colCount; i++){
60221             this.autoSizeColumn(i, true, true);
60222         }
60223         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60224             this.fitColumns();
60225         }else{
60226             this.updateColumns();
60227             this.layout();
60228         }
60229     },
60230
60231     /**
60232      * Autofits all columns to the grid's width proportionate with their current size
60233      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60234      */
60235     fitColumns : function(reserveScrollSpace){
60236         var cm = this.grid.colModel;
60237         var colCount = cm.getColumnCount();
60238         var cols = [];
60239         var width = 0;
60240         var i, w;
60241         for (i = 0; i < colCount; i++){
60242             if(!cm.isHidden(i) && !cm.isFixed(i)){
60243                 w = cm.getColumnWidth(i);
60244                 cols.push(i);
60245                 cols.push(w);
60246                 width += w;
60247             }
60248         }
60249         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60250         if(reserveScrollSpace){
60251             avail -= 17;
60252         }
60253         var frac = (avail - cm.getTotalWidth())/width;
60254         while (cols.length){
60255             w = cols.pop();
60256             i = cols.pop();
60257             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60258         }
60259         this.updateColumns();
60260         this.layout();
60261     },
60262
60263     onRowSelect : function(rowIndex){
60264         var row = this.getRowComposite(rowIndex);
60265         row.addClass("x-grid-row-selected");
60266     },
60267
60268     onRowDeselect : function(rowIndex){
60269         var row = this.getRowComposite(rowIndex);
60270         row.removeClass("x-grid-row-selected");
60271     },
60272
60273     onCellSelect : function(row, col){
60274         var cell = this.getCell(row, col);
60275         if(cell){
60276             Roo.fly(cell).addClass("x-grid-cell-selected");
60277         }
60278     },
60279
60280     onCellDeselect : function(row, col){
60281         var cell = this.getCell(row, col);
60282         if(cell){
60283             Roo.fly(cell).removeClass("x-grid-cell-selected");
60284         }
60285     },
60286
60287     updateHeaderSortState : function(){
60288         
60289         // sort state can be single { field: xxx, direction : yyy}
60290         // or   { xxx=>ASC , yyy : DESC ..... }
60291         
60292         var mstate = {};
60293         if (!this.ds.multiSort) { 
60294             var state = this.ds.getSortState();
60295             if(!state){
60296                 return;
60297             }
60298             mstate[state.field] = state.direction;
60299             // FIXME... - this is not used here.. but might be elsewhere..
60300             this.sortState = state;
60301             
60302         } else {
60303             mstate = this.ds.sortToggle;
60304         }
60305         //remove existing sort classes..
60306         
60307         var sc = this.sortClasses;
60308         var hds = this.el.select(this.headerSelector).removeClass(sc);
60309         
60310         for(var f in mstate) {
60311         
60312             var sortColumn = this.cm.findColumnIndex(f);
60313             
60314             if(sortColumn != -1){
60315                 var sortDir = mstate[f];        
60316                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60317             }
60318         }
60319         
60320          
60321         
60322     },
60323
60324
60325     handleHeaderClick : function(g, index,e){
60326         
60327         Roo.log("header click");
60328         
60329         if (Roo.isTouch) {
60330             // touch events on header are handled by context
60331             this.handleHdCtx(g,index,e);
60332             return;
60333         }
60334         
60335         
60336         if(this.headersDisabled){
60337             return;
60338         }
60339         var dm = g.dataSource, cm = g.colModel;
60340         if(!cm.isSortable(index)){
60341             return;
60342         }
60343         g.stopEditing();
60344         
60345         if (dm.multiSort) {
60346             // update the sortOrder
60347             var so = [];
60348             for(var i = 0; i < cm.config.length; i++ ) {
60349                 
60350                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60351                     continue; // dont' bother, it's not in sort list or being set.
60352                 }
60353                 
60354                 so.push(cm.config[i].dataIndex);
60355             };
60356             dm.sortOrder = so;
60357         }
60358         
60359         
60360         dm.sort(cm.getDataIndex(index));
60361     },
60362
60363
60364     destroy : function(){
60365         if(this.colMenu){
60366             this.colMenu.removeAll();
60367             Roo.menu.MenuMgr.unregister(this.colMenu);
60368             this.colMenu.getEl().remove();
60369             delete this.colMenu;
60370         }
60371         if(this.hmenu){
60372             this.hmenu.removeAll();
60373             Roo.menu.MenuMgr.unregister(this.hmenu);
60374             this.hmenu.getEl().remove();
60375             delete this.hmenu;
60376         }
60377         if(this.grid.enableColumnMove){
60378             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60379             if(dds){
60380                 for(var dd in dds){
60381                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60382                         var elid = dds[dd].dragElId;
60383                         dds[dd].unreg();
60384                         Roo.get(elid).remove();
60385                     } else if(dds[dd].config.isTarget){
60386                         dds[dd].proxyTop.remove();
60387                         dds[dd].proxyBottom.remove();
60388                         dds[dd].unreg();
60389                     }
60390                     if(Roo.dd.DDM.locationCache[dd]){
60391                         delete Roo.dd.DDM.locationCache[dd];
60392                     }
60393                 }
60394                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60395             }
60396         }
60397         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60398         this.bind(null, null);
60399         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60400     },
60401
60402     handleLockChange : function(){
60403         this.refresh(true);
60404     },
60405
60406     onDenyColumnLock : function(){
60407
60408     },
60409
60410     onDenyColumnHide : function(){
60411
60412     },
60413
60414     handleHdMenuClick : function(item){
60415         var index = this.hdCtxIndex;
60416         var cm = this.cm, ds = this.ds;
60417         switch(item.id){
60418             case "asc":
60419                 ds.sort(cm.getDataIndex(index), "ASC");
60420                 break;
60421             case "desc":
60422                 ds.sort(cm.getDataIndex(index), "DESC");
60423                 break;
60424             case "lock":
60425                 var lc = cm.getLockedCount();
60426                 if(cm.getColumnCount(true) <= lc+1){
60427                     this.onDenyColumnLock();
60428                     return;
60429                 }
60430                 if(lc != index){
60431                     cm.setLocked(index, true, true);
60432                     cm.moveColumn(index, lc);
60433                     this.grid.fireEvent("columnmove", index, lc);
60434                 }else{
60435                     cm.setLocked(index, true);
60436                 }
60437             break;
60438             case "unlock":
60439                 var lc = cm.getLockedCount();
60440                 if((lc-1) != index){
60441                     cm.setLocked(index, false, true);
60442                     cm.moveColumn(index, lc-1);
60443                     this.grid.fireEvent("columnmove", index, lc-1);
60444                 }else{
60445                     cm.setLocked(index, false);
60446                 }
60447             break;
60448             case 'wider': // used to expand cols on touch..
60449             case 'narrow':
60450                 var cw = cm.getColumnWidth(index);
60451                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60452                 cw = Math.max(0, cw);
60453                 cw = Math.min(cw,4000);
60454                 cm.setColumnWidth(index, cw);
60455                 break;
60456                 
60457             default:
60458                 index = cm.getIndexById(item.id.substr(4));
60459                 if(index != -1){
60460                     if(item.checked && cm.getColumnCount(true) <= 1){
60461                         this.onDenyColumnHide();
60462                         return false;
60463                     }
60464                     cm.setHidden(index, item.checked);
60465                 }
60466         }
60467         return true;
60468     },
60469
60470     beforeColMenuShow : function(){
60471         var cm = this.cm,  colCount = cm.getColumnCount();
60472         this.colMenu.removeAll();
60473         
60474         var items = [];
60475         for(var i = 0; i < colCount; i++){
60476             items.push({
60477                 id: "col-"+cm.getColumnId(i),
60478                 text: cm.getColumnHeader(i),
60479                 checked: !cm.isHidden(i),
60480                 hideOnClick:false
60481             });
60482         }
60483         
60484         if (this.grid.sortColMenu) {
60485             items.sort(function(a,b) {
60486                 if (a.text == b.text) {
60487                     return 0;
60488                 }
60489                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60490             });
60491         }
60492         
60493         for(var i = 0; i < colCount; i++){
60494             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60495         }
60496     },
60497
60498     handleHdCtx : function(g, index, e){
60499         e.stopEvent();
60500         var hd = this.getHeaderCell(index);
60501         this.hdCtxIndex = index;
60502         var ms = this.hmenu.items, cm = this.cm;
60503         ms.get("asc").setDisabled(!cm.isSortable(index));
60504         ms.get("desc").setDisabled(!cm.isSortable(index));
60505         if(this.grid.enableColLock !== false){
60506             ms.get("lock").setDisabled(cm.isLocked(index));
60507             ms.get("unlock").setDisabled(!cm.isLocked(index));
60508         }
60509         this.hmenu.show(hd, "tl-bl");
60510     },
60511
60512     handleHdOver : function(e){
60513         var hd = this.findHeaderCell(e.getTarget());
60514         if(hd && !this.headersDisabled){
60515             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60516                this.fly(hd).addClass("x-grid-hd-over");
60517             }
60518         }
60519     },
60520
60521     handleHdOut : function(e){
60522         var hd = this.findHeaderCell(e.getTarget());
60523         if(hd){
60524             this.fly(hd).removeClass("x-grid-hd-over");
60525         }
60526     },
60527
60528     handleSplitDblClick : function(e, t){
60529         var i = this.getCellIndex(t);
60530         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60531             this.autoSizeColumn(i, true);
60532             this.layout();
60533         }
60534     },
60535
60536     render : function(){
60537
60538         var cm = this.cm;
60539         var colCount = cm.getColumnCount();
60540
60541         if(this.grid.monitorWindowResize === true){
60542             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60543         }
60544         var header = this.renderHeaders();
60545         var body = this.templates.body.apply({rows:""});
60546         var html = this.templates.master.apply({
60547             lockedBody: body,
60548             body: body,
60549             lockedHeader: header[0],
60550             header: header[1]
60551         });
60552
60553         //this.updateColumns();
60554
60555         this.grid.getGridEl().dom.innerHTML = html;
60556
60557         this.initElements();
60558         
60559         // a kludge to fix the random scolling effect in webkit
60560         this.el.on("scroll", function() {
60561             this.el.dom.scrollTop=0; // hopefully not recursive..
60562         },this);
60563
60564         this.scroller.on("scroll", this.handleScroll, this);
60565         this.lockedBody.on("mousewheel", this.handleWheel, this);
60566         this.mainBody.on("mousewheel", this.handleWheel, this);
60567
60568         this.mainHd.on("mouseover", this.handleHdOver, this);
60569         this.mainHd.on("mouseout", this.handleHdOut, this);
60570         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60571                 {delegate: "."+this.splitClass});
60572
60573         this.lockedHd.on("mouseover", this.handleHdOver, this);
60574         this.lockedHd.on("mouseout", this.handleHdOut, this);
60575         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60576                 {delegate: "."+this.splitClass});
60577
60578         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60579             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60580         }
60581
60582         this.updateSplitters();
60583
60584         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60585             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60586             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60587         }
60588
60589         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60590             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60591             this.hmenu.add(
60592                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60593                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60594             );
60595             if(this.grid.enableColLock !== false){
60596                 this.hmenu.add('-',
60597                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60598                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60599                 );
60600             }
60601             if (Roo.isTouch) {
60602                  this.hmenu.add('-',
60603                     {id:"wider", text: this.columnsWiderText},
60604                     {id:"narrow", text: this.columnsNarrowText }
60605                 );
60606                 
60607                  
60608             }
60609             
60610             if(this.grid.enableColumnHide !== false){
60611
60612                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60613                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60614                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60615
60616                 this.hmenu.add('-',
60617                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60618                 );
60619             }
60620             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60621
60622             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60623         }
60624
60625         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60626             this.dd = new Roo.grid.GridDragZone(this.grid, {
60627                 ddGroup : this.grid.ddGroup || 'GridDD'
60628             });
60629             
60630         }
60631
60632         /*
60633         for(var i = 0; i < colCount; i++){
60634             if(cm.isHidden(i)){
60635                 this.hideColumn(i);
60636             }
60637             if(cm.config[i].align){
60638                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60639                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60640             }
60641         }*/
60642         
60643         this.updateHeaderSortState();
60644
60645         this.beforeInitialResize();
60646         this.layout(true);
60647
60648         // two part rendering gives faster view to the user
60649         this.renderPhase2.defer(1, this);
60650     },
60651
60652     renderPhase2 : function(){
60653         // render the rows now
60654         this.refresh();
60655         if(this.grid.autoSizeColumns){
60656             this.autoSizeColumns();
60657         }
60658     },
60659
60660     beforeInitialResize : function(){
60661
60662     },
60663
60664     onColumnSplitterMoved : function(i, w){
60665         this.userResized = true;
60666         var cm = this.grid.colModel;
60667         cm.setColumnWidth(i, w, true);
60668         var cid = cm.getColumnId(i);
60669         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60670         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60671         this.updateSplitters();
60672         this.layout();
60673         this.grid.fireEvent("columnresize", i, w);
60674     },
60675
60676     syncRowHeights : function(startIndex, endIndex){
60677         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60678             startIndex = startIndex || 0;
60679             var mrows = this.getBodyTable().rows;
60680             var lrows = this.getLockedTable().rows;
60681             var len = mrows.length-1;
60682             endIndex = Math.min(endIndex || len, len);
60683             for(var i = startIndex; i <= endIndex; i++){
60684                 var m = mrows[i], l = lrows[i];
60685                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60686                 m.style.height = l.style.height = h + "px";
60687             }
60688         }
60689     },
60690
60691     layout : function(initialRender, is2ndPass)
60692     {
60693         var g = this.grid;
60694         var auto = g.autoHeight;
60695         var scrollOffset = 16;
60696         var c = g.getGridEl(), cm = this.cm,
60697                 expandCol = g.autoExpandColumn,
60698                 gv = this;
60699         //c.beginMeasure();
60700
60701         if(!c.dom.offsetWidth){ // display:none?
60702             if(initialRender){
60703                 this.lockedWrap.show();
60704                 this.mainWrap.show();
60705             }
60706             return;
60707         }
60708
60709         var hasLock = this.cm.isLocked(0);
60710
60711         var tbh = this.headerPanel.getHeight();
60712         var bbh = this.footerPanel.getHeight();
60713
60714         if(auto){
60715             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60716             var newHeight = ch + c.getBorderWidth("tb");
60717             if(g.maxHeight){
60718                 newHeight = Math.min(g.maxHeight, newHeight);
60719             }
60720             c.setHeight(newHeight);
60721         }
60722
60723         if(g.autoWidth){
60724             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60725         }
60726
60727         var s = this.scroller;
60728
60729         var csize = c.getSize(true);
60730
60731         this.el.setSize(csize.width, csize.height);
60732
60733         this.headerPanel.setWidth(csize.width);
60734         this.footerPanel.setWidth(csize.width);
60735
60736         var hdHeight = this.mainHd.getHeight();
60737         var vw = csize.width;
60738         var vh = csize.height - (tbh + bbh);
60739
60740         s.setSize(vw, vh);
60741
60742         var bt = this.getBodyTable();
60743         
60744         if(cm.getLockedCount() == cm.config.length){
60745             bt = this.getLockedTable();
60746         }
60747         
60748         var ltWidth = hasLock ?
60749                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60750
60751         var scrollHeight = bt.offsetHeight;
60752         var scrollWidth = ltWidth + bt.offsetWidth;
60753         var vscroll = false, hscroll = false;
60754
60755         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60756
60757         var lw = this.lockedWrap, mw = this.mainWrap;
60758         var lb = this.lockedBody, mb = this.mainBody;
60759
60760         setTimeout(function(){
60761             var t = s.dom.offsetTop;
60762             var w = s.dom.clientWidth,
60763                 h = s.dom.clientHeight;
60764
60765             lw.setTop(t);
60766             lw.setSize(ltWidth, h);
60767
60768             mw.setLeftTop(ltWidth, t);
60769             mw.setSize(w-ltWidth, h);
60770
60771             lb.setHeight(h-hdHeight);
60772             mb.setHeight(h-hdHeight);
60773
60774             if(is2ndPass !== true && !gv.userResized && expandCol){
60775                 // high speed resize without full column calculation
60776                 
60777                 var ci = cm.getIndexById(expandCol);
60778                 if (ci < 0) {
60779                     ci = cm.findColumnIndex(expandCol);
60780                 }
60781                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60782                 var expandId = cm.getColumnId(ci);
60783                 var  tw = cm.getTotalWidth(false);
60784                 var currentWidth = cm.getColumnWidth(ci);
60785                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60786                 if(currentWidth != cw){
60787                     cm.setColumnWidth(ci, cw, true);
60788                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60789                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60790                     gv.updateSplitters();
60791                     gv.layout(false, true);
60792                 }
60793             }
60794
60795             if(initialRender){
60796                 lw.show();
60797                 mw.show();
60798             }
60799             //c.endMeasure();
60800         }, 10);
60801     },
60802
60803     onWindowResize : function(){
60804         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60805             return;
60806         }
60807         this.layout();
60808     },
60809
60810     appendFooter : function(parentEl){
60811         return null;
60812     },
60813
60814     sortAscText : "Sort Ascending",
60815     sortDescText : "Sort Descending",
60816     lockText : "Lock Column",
60817     unlockText : "Unlock Column",
60818     columnsText : "Columns",
60819  
60820     columnsWiderText : "Wider",
60821     columnsNarrowText : "Thinner"
60822 });
60823
60824
60825 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60826     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60827     this.proxy.el.addClass('x-grid3-col-dd');
60828 };
60829
60830 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60831     handleMouseDown : function(e){
60832
60833     },
60834
60835     callHandleMouseDown : function(e){
60836         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60837     }
60838 });
60839 /*
60840  * Based on:
60841  * Ext JS Library 1.1.1
60842  * Copyright(c) 2006-2007, Ext JS, LLC.
60843  *
60844  * Originally Released Under LGPL - original licence link has changed is not relivant.
60845  *
60846  * Fork - LGPL
60847  * <script type="text/javascript">
60848  */
60849  /**
60850  * @extends Roo.dd.DDProxy
60851  * @class Roo.grid.SplitDragZone
60852  * Support for Column Header resizing
60853  * @constructor
60854  * @param {Object} config
60855  */
60856 // private
60857 // This is a support class used internally by the Grid components
60858 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60859     this.grid = grid;
60860     this.view = grid.getView();
60861     this.proxy = this.view.resizeProxy;
60862     Roo.grid.SplitDragZone.superclass.constructor.call(
60863         this,
60864         hd, // ID
60865         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60866         {  // CONFIG
60867             dragElId : Roo.id(this.proxy.dom),
60868             resizeFrame:false
60869         }
60870     );
60871     
60872     this.setHandleElId(Roo.id(hd));
60873     if (hd2 !== false) {
60874         this.setOuterHandleElId(Roo.id(hd2));
60875     }
60876     
60877     this.scroll = false;
60878 };
60879 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60880     fly: Roo.Element.fly,
60881
60882     b4StartDrag : function(x, y){
60883         this.view.headersDisabled = true;
60884         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60885                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60886         );
60887         this.proxy.setHeight(h);
60888         
60889         // for old system colWidth really stored the actual width?
60890         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60891         // which in reality did not work.. - it worked only for fixed sizes
60892         // for resizable we need to use actual sizes.
60893         var w = this.cm.getColumnWidth(this.cellIndex);
60894         if (!this.view.mainWrap) {
60895             // bootstrap.
60896             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60897         }
60898         
60899         
60900         
60901         // this was w-this.grid.minColumnWidth;
60902         // doesnt really make sense? - w = thie curren width or the rendered one?
60903         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60904         this.resetConstraints();
60905         this.setXConstraint(minw, 1000);
60906         this.setYConstraint(0, 0);
60907         this.minX = x - minw;
60908         this.maxX = x + 1000;
60909         this.startPos = x;
60910         if (!this.view.mainWrap) { // this is Bootstrap code..
60911             this.getDragEl().style.display='block';
60912         }
60913         
60914         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60915     },
60916
60917
60918     handleMouseDown : function(e){
60919         ev = Roo.EventObject.setEvent(e);
60920         var t = this.fly(ev.getTarget());
60921         if(t.hasClass("x-grid-split")){
60922             this.cellIndex = this.view.getCellIndex(t.dom);
60923             this.split = t.dom;
60924             this.cm = this.grid.colModel;
60925             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60926                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60927             }
60928         }
60929     },
60930
60931     endDrag : function(e){
60932         this.view.headersDisabled = false;
60933         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60934         var diff = endX - this.startPos;
60935         // 
60936         var w = this.cm.getColumnWidth(this.cellIndex);
60937         if (!this.view.mainWrap) {
60938             w = 0;
60939         }
60940         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60941     },
60942
60943     autoOffset : function(){
60944         this.setDelta(0,0);
60945     }
60946 });/*
60947  * Based on:
60948  * Ext JS Library 1.1.1
60949  * Copyright(c) 2006-2007, Ext JS, LLC.
60950  *
60951  * Originally Released Under LGPL - original licence link has changed is not relivant.
60952  *
60953  * Fork - LGPL
60954  * <script type="text/javascript">
60955  */
60956  
60957 // private
60958 // This is a support class used internally by the Grid components
60959 Roo.grid.GridDragZone = function(grid, config){
60960     this.view = grid.getView();
60961     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60962     if(this.view.lockedBody){
60963         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60964         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60965     }
60966     this.scroll = false;
60967     this.grid = grid;
60968     this.ddel = document.createElement('div');
60969     this.ddel.className = 'x-grid-dd-wrap';
60970 };
60971
60972 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60973     ddGroup : "GridDD",
60974
60975     getDragData : function(e){
60976         var t = Roo.lib.Event.getTarget(e);
60977         var rowIndex = this.view.findRowIndex(t);
60978         var sm = this.grid.selModel;
60979             
60980         //Roo.log(rowIndex);
60981         
60982         if (sm.getSelectedCell) {
60983             // cell selection..
60984             if (!sm.getSelectedCell()) {
60985                 return false;
60986             }
60987             if (rowIndex != sm.getSelectedCell()[0]) {
60988                 return false;
60989             }
60990         
60991         }
60992         if (sm.getSelections && sm.getSelections().length < 1) {
60993             return false;
60994         }
60995         
60996         
60997         // before it used to all dragging of unseleted... - now we dont do that.
60998         if(rowIndex !== false){
60999             
61000             // if editorgrid.. 
61001             
61002             
61003             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61004                
61005             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61006               //  
61007             //}
61008             if (e.hasModifier()){
61009                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61010             }
61011             
61012             Roo.log("getDragData");
61013             
61014             return {
61015                 grid: this.grid,
61016                 ddel: this.ddel,
61017                 rowIndex: rowIndex,
61018                 selections: sm.getSelections ? sm.getSelections() : (
61019                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61020             };
61021         }
61022         return false;
61023     },
61024     
61025     
61026     onInitDrag : function(e){
61027         var data = this.dragData;
61028         this.ddel.innerHTML = this.grid.getDragDropText();
61029         this.proxy.update(this.ddel);
61030         // fire start drag?
61031     },
61032
61033     afterRepair : function(){
61034         this.dragging = false;
61035     },
61036
61037     getRepairXY : function(e, data){
61038         return false;
61039     },
61040
61041     onEndDrag : function(data, e){
61042         // fire end drag?
61043     },
61044
61045     onValidDrop : function(dd, e, id){
61046         // fire drag drop?
61047         this.hideProxy();
61048     },
61049
61050     beforeInvalidDrop : function(e, id){
61051
61052     }
61053 });/*
61054  * Based on:
61055  * Ext JS Library 1.1.1
61056  * Copyright(c) 2006-2007, Ext JS, LLC.
61057  *
61058  * Originally Released Under LGPL - original licence link has changed is not relivant.
61059  *
61060  * Fork - LGPL
61061  * <script type="text/javascript">
61062  */
61063  
61064
61065 /**
61066  * @class Roo.grid.ColumnModel
61067  * @extends Roo.util.Observable
61068  * This is the default implementation of a ColumnModel used by the Grid. It defines
61069  * the columns in the grid.
61070  * <br>Usage:<br>
61071  <pre><code>
61072  var colModel = new Roo.grid.ColumnModel([
61073         {header: "Ticker", width: 60, sortable: true, locked: true},
61074         {header: "Company Name", width: 150, sortable: true},
61075         {header: "Market Cap.", width: 100, sortable: true},
61076         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61077         {header: "Employees", width: 100, sortable: true, resizable: false}
61078  ]);
61079  </code></pre>
61080  * <p>
61081  
61082  * The config options listed for this class are options which may appear in each
61083  * individual column definition.
61084  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61085  * @constructor
61086  * @param {Object} config An Array of column config objects. See this class's
61087  * config objects for details.
61088 */
61089 Roo.grid.ColumnModel = function(config){
61090         /**
61091      * The config passed into the constructor
61092      */
61093     this.config = []; //config;
61094     this.lookup = {};
61095
61096     // if no id, create one
61097     // if the column does not have a dataIndex mapping,
61098     // map it to the order it is in the config
61099     for(var i = 0, len = config.length; i < len; i++){
61100         this.addColumn(config[i]);
61101         
61102     }
61103
61104     /**
61105      * The width of columns which have no width specified (defaults to 100)
61106      * @type Number
61107      */
61108     this.defaultWidth = 100;
61109
61110     /**
61111      * Default sortable of columns which have no sortable specified (defaults to false)
61112      * @type Boolean
61113      */
61114     this.defaultSortable = false;
61115
61116     this.addEvents({
61117         /**
61118              * @event widthchange
61119              * Fires when the width of a column changes.
61120              * @param {ColumnModel} this
61121              * @param {Number} columnIndex The column index
61122              * @param {Number} newWidth The new width
61123              */
61124             "widthchange": true,
61125         /**
61126              * @event headerchange
61127              * Fires when the text of a header changes.
61128              * @param {ColumnModel} this
61129              * @param {Number} columnIndex The column index
61130              * @param {Number} newText The new header text
61131              */
61132             "headerchange": true,
61133         /**
61134              * @event hiddenchange
61135              * Fires when a column is hidden or "unhidden".
61136              * @param {ColumnModel} this
61137              * @param {Number} columnIndex The column index
61138              * @param {Boolean} hidden true if hidden, false otherwise
61139              */
61140             "hiddenchange": true,
61141             /**
61142          * @event columnmoved
61143          * Fires when a column is moved.
61144          * @param {ColumnModel} this
61145          * @param {Number} oldIndex
61146          * @param {Number} newIndex
61147          */
61148         "columnmoved" : true,
61149         /**
61150          * @event columlockchange
61151          * Fires when a column's locked state is changed
61152          * @param {ColumnModel} this
61153          * @param {Number} colIndex
61154          * @param {Boolean} locked true if locked
61155          */
61156         "columnlockchange" : true
61157     });
61158     Roo.grid.ColumnModel.superclass.constructor.call(this);
61159 };
61160 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61161     /**
61162      * @cfg {String} header The header text to display in the Grid view.
61163      */
61164         /**
61165      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61166      */
61167         /**
61168      * @cfg {String} smHeader Header at Bootsrap Small width
61169      */
61170         /**
61171      * @cfg {String} mdHeader Header at Bootsrap Medium width
61172      */
61173         /**
61174      * @cfg {String} lgHeader Header at Bootsrap Large width
61175      */
61176         /**
61177      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61178      */
61179     /**
61180      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61181      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61182      * specified, the column's index is used as an index into the Record's data Array.
61183      */
61184     /**
61185      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61186      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61187      */
61188     /**
61189      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61190      * Defaults to the value of the {@link #defaultSortable} property.
61191      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61192      */
61193     /**
61194      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61195      */
61196     /**
61197      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61198      */
61199     /**
61200      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61201      */
61202     /**
61203      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61204      */
61205     /**
61206      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61207      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61208      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61209      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61210      */
61211        /**
61212      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61213      */
61214     /**
61215      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61216      */
61217     /**
61218      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61219      */
61220     /**
61221      * @cfg {String} cursor (Optional)
61222      */
61223     /**
61224      * @cfg {String} tooltip (Optional)
61225      */
61226     /**
61227      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61228      */
61229     /**
61230      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61231      */
61232     /**
61233      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61234      */
61235     /**
61236      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61237      */
61238         /**
61239      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61240      */
61241     /**
61242      * Returns the id of the column at the specified index.
61243      * @param {Number} index The column index
61244      * @return {String} the id
61245      */
61246     getColumnId : function(index){
61247         return this.config[index].id;
61248     },
61249
61250     /**
61251      * Returns the column for a specified id.
61252      * @param {String} id The column id
61253      * @return {Object} the column
61254      */
61255     getColumnById : function(id){
61256         return this.lookup[id];
61257     },
61258
61259     
61260     /**
61261      * Returns the column Object for a specified dataIndex.
61262      * @param {String} dataIndex The column dataIndex
61263      * @return {Object|Boolean} the column or false if not found
61264      */
61265     getColumnByDataIndex: function(dataIndex){
61266         var index = this.findColumnIndex(dataIndex);
61267         return index > -1 ? this.config[index] : false;
61268     },
61269     
61270     /**
61271      * Returns the index for a specified column id.
61272      * @param {String} id The column id
61273      * @return {Number} the index, or -1 if not found
61274      */
61275     getIndexById : function(id){
61276         for(var i = 0, len = this.config.length; i < len; i++){
61277             if(this.config[i].id == id){
61278                 return i;
61279             }
61280         }
61281         return -1;
61282     },
61283     
61284     /**
61285      * Returns the index for a specified column dataIndex.
61286      * @param {String} dataIndex The column dataIndex
61287      * @return {Number} the index, or -1 if not found
61288      */
61289     
61290     findColumnIndex : function(dataIndex){
61291         for(var i = 0, len = this.config.length; i < len; i++){
61292             if(this.config[i].dataIndex == dataIndex){
61293                 return i;
61294             }
61295         }
61296         return -1;
61297     },
61298     
61299     
61300     moveColumn : function(oldIndex, newIndex){
61301         var c = this.config[oldIndex];
61302         this.config.splice(oldIndex, 1);
61303         this.config.splice(newIndex, 0, c);
61304         this.dataMap = null;
61305         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61306     },
61307
61308     isLocked : function(colIndex){
61309         return this.config[colIndex].locked === true;
61310     },
61311
61312     setLocked : function(colIndex, value, suppressEvent){
61313         if(this.isLocked(colIndex) == value){
61314             return;
61315         }
61316         this.config[colIndex].locked = value;
61317         if(!suppressEvent){
61318             this.fireEvent("columnlockchange", this, colIndex, value);
61319         }
61320     },
61321
61322     getTotalLockedWidth : function(){
61323         var totalWidth = 0;
61324         for(var i = 0; i < this.config.length; i++){
61325             if(this.isLocked(i) && !this.isHidden(i)){
61326                 this.totalWidth += this.getColumnWidth(i);
61327             }
61328         }
61329         return totalWidth;
61330     },
61331
61332     getLockedCount : function(){
61333         for(var i = 0, len = this.config.length; i < len; i++){
61334             if(!this.isLocked(i)){
61335                 return i;
61336             }
61337         }
61338         
61339         return this.config.length;
61340     },
61341
61342     /**
61343      * Returns the number of columns.
61344      * @return {Number}
61345      */
61346     getColumnCount : function(visibleOnly){
61347         if(visibleOnly === true){
61348             var c = 0;
61349             for(var i = 0, len = this.config.length; i < len; i++){
61350                 if(!this.isHidden(i)){
61351                     c++;
61352                 }
61353             }
61354             return c;
61355         }
61356         return this.config.length;
61357     },
61358
61359     /**
61360      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61361      * @param {Function} fn
61362      * @param {Object} scope (optional)
61363      * @return {Array} result
61364      */
61365     getColumnsBy : function(fn, scope){
61366         var r = [];
61367         for(var i = 0, len = this.config.length; i < len; i++){
61368             var c = this.config[i];
61369             if(fn.call(scope||this, c, i) === true){
61370                 r[r.length] = c;
61371             }
61372         }
61373         return r;
61374     },
61375
61376     /**
61377      * Returns true if the specified column is sortable.
61378      * @param {Number} col The column index
61379      * @return {Boolean}
61380      */
61381     isSortable : function(col){
61382         if(typeof this.config[col].sortable == "undefined"){
61383             return this.defaultSortable;
61384         }
61385         return this.config[col].sortable;
61386     },
61387
61388     /**
61389      * Returns the rendering (formatting) function defined for the column.
61390      * @param {Number} col The column index.
61391      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61392      */
61393     getRenderer : function(col){
61394         if(!this.config[col].renderer){
61395             return Roo.grid.ColumnModel.defaultRenderer;
61396         }
61397         return this.config[col].renderer;
61398     },
61399
61400     /**
61401      * Sets the rendering (formatting) function for a column.
61402      * @param {Number} col The column index
61403      * @param {Function} fn The function to use to process the cell's raw data
61404      * to return HTML markup for the grid view. The render function is called with
61405      * the following parameters:<ul>
61406      * <li>Data value.</li>
61407      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61408      * <li>css A CSS style string to apply to the table cell.</li>
61409      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61410      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61411      * <li>Row index</li>
61412      * <li>Column index</li>
61413      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61414      */
61415     setRenderer : function(col, fn){
61416         this.config[col].renderer = fn;
61417     },
61418
61419     /**
61420      * Returns the width for the specified column.
61421      * @param {Number} col The column index
61422      * @param (optional) {String} gridSize bootstrap width size.
61423      * @return {Number}
61424      */
61425     getColumnWidth : function(col, gridSize)
61426         {
61427                 var cfg = this.config[col];
61428                 
61429                 if (typeof(gridSize) == 'undefined') {
61430                         return cfg.width * 1 || this.defaultWidth;
61431                 }
61432                 if (gridSize === false) { // if we set it..
61433                         return cfg.width || false;
61434                 }
61435                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61436                 
61437                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61438                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61439                                 continue;
61440                         }
61441                         return cfg[ sizes[i] ];
61442                 }
61443                 return 1;
61444                 
61445     },
61446
61447     /**
61448      * Sets the width for a column.
61449      * @param {Number} col The column index
61450      * @param {Number} width The new width
61451      */
61452     setColumnWidth : function(col, width, suppressEvent){
61453         this.config[col].width = width;
61454         this.totalWidth = null;
61455         if(!suppressEvent){
61456              this.fireEvent("widthchange", this, col, width);
61457         }
61458     },
61459
61460     /**
61461      * Returns the total width of all columns.
61462      * @param {Boolean} includeHidden True to include hidden column widths
61463      * @return {Number}
61464      */
61465     getTotalWidth : function(includeHidden){
61466         if(!this.totalWidth){
61467             this.totalWidth = 0;
61468             for(var i = 0, len = this.config.length; i < len; i++){
61469                 if(includeHidden || !this.isHidden(i)){
61470                     this.totalWidth += this.getColumnWidth(i);
61471                 }
61472             }
61473         }
61474         return this.totalWidth;
61475     },
61476
61477     /**
61478      * Returns the header for the specified column.
61479      * @param {Number} col The column index
61480      * @return {String}
61481      */
61482     getColumnHeader : function(col){
61483         return this.config[col].header;
61484     },
61485
61486     /**
61487      * Sets the header for a column.
61488      * @param {Number} col The column index
61489      * @param {String} header The new header
61490      */
61491     setColumnHeader : function(col, header){
61492         this.config[col].header = header;
61493         this.fireEvent("headerchange", this, col, header);
61494     },
61495
61496     /**
61497      * Returns the tooltip for the specified column.
61498      * @param {Number} col The column index
61499      * @return {String}
61500      */
61501     getColumnTooltip : function(col){
61502             return this.config[col].tooltip;
61503     },
61504     /**
61505      * Sets the tooltip for a column.
61506      * @param {Number} col The column index
61507      * @param {String} tooltip The new tooltip
61508      */
61509     setColumnTooltip : function(col, tooltip){
61510             this.config[col].tooltip = tooltip;
61511     },
61512
61513     /**
61514      * Returns the dataIndex for the specified column.
61515      * @param {Number} col The column index
61516      * @return {Number}
61517      */
61518     getDataIndex : function(col){
61519         return this.config[col].dataIndex;
61520     },
61521
61522     /**
61523      * Sets the dataIndex for a column.
61524      * @param {Number} col The column index
61525      * @param {Number} dataIndex The new dataIndex
61526      */
61527     setDataIndex : function(col, dataIndex){
61528         this.config[col].dataIndex = dataIndex;
61529     },
61530
61531     
61532     
61533     /**
61534      * Returns true if the cell is editable.
61535      * @param {Number} colIndex The column index
61536      * @param {Number} rowIndex The row index - this is nto actually used..?
61537      * @return {Boolean}
61538      */
61539     isCellEditable : function(colIndex, rowIndex){
61540         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61541     },
61542
61543     /**
61544      * Returns the editor defined for the cell/column.
61545      * return false or null to disable editing.
61546      * @param {Number} colIndex The column index
61547      * @param {Number} rowIndex The row index
61548      * @return {Object}
61549      */
61550     getCellEditor : function(colIndex, rowIndex){
61551         return this.config[colIndex].editor;
61552     },
61553
61554     /**
61555      * Sets if a column is editable.
61556      * @param {Number} col The column index
61557      * @param {Boolean} editable True if the column is editable
61558      */
61559     setEditable : function(col, editable){
61560         this.config[col].editable = editable;
61561     },
61562
61563
61564     /**
61565      * Returns true if the column is hidden.
61566      * @param {Number} colIndex The column index
61567      * @return {Boolean}
61568      */
61569     isHidden : function(colIndex){
61570         return this.config[colIndex].hidden;
61571     },
61572
61573
61574     /**
61575      * Returns true if the column width cannot be changed
61576      */
61577     isFixed : function(colIndex){
61578         return this.config[colIndex].fixed;
61579     },
61580
61581     /**
61582      * Returns true if the column can be resized
61583      * @return {Boolean}
61584      */
61585     isResizable : function(colIndex){
61586         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61587     },
61588     /**
61589      * Sets if a column is hidden.
61590      * @param {Number} colIndex The column index
61591      * @param {Boolean} hidden True if the column is hidden
61592      */
61593     setHidden : function(colIndex, hidden){
61594         this.config[colIndex].hidden = hidden;
61595         this.totalWidth = null;
61596         this.fireEvent("hiddenchange", this, colIndex, hidden);
61597     },
61598
61599     /**
61600      * Sets the editor for a column.
61601      * @param {Number} col The column index
61602      * @param {Object} editor The editor object
61603      */
61604     setEditor : function(col, editor){
61605         this.config[col].editor = editor;
61606     },
61607     /**
61608      * Add a column (experimental...) - defaults to adding to the end..
61609      * @param {Object} config 
61610     */
61611     addColumn : function(c)
61612     {
61613     
61614         var i = this.config.length;
61615         this.config[i] = c;
61616         
61617         if(typeof c.dataIndex == "undefined"){
61618             c.dataIndex = i;
61619         }
61620         if(typeof c.renderer == "string"){
61621             c.renderer = Roo.util.Format[c.renderer];
61622         }
61623         if(typeof c.id == "undefined"){
61624             c.id = Roo.id();
61625         }
61626         if(c.editor && c.editor.xtype){
61627             c.editor  = Roo.factory(c.editor, Roo.grid);
61628         }
61629         if(c.editor && c.editor.isFormField){
61630             c.editor = new Roo.grid.GridEditor(c.editor);
61631         }
61632         this.lookup[c.id] = c;
61633     }
61634     
61635 });
61636
61637 Roo.grid.ColumnModel.defaultRenderer = function(value)
61638 {
61639     if(typeof value == "object") {
61640         return value;
61641     }
61642         if(typeof value == "string" && value.length < 1){
61643             return "&#160;";
61644         }
61645     
61646         return String.format("{0}", value);
61647 };
61648
61649 // Alias for backwards compatibility
61650 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61651 /*
61652  * Based on:
61653  * Ext JS Library 1.1.1
61654  * Copyright(c) 2006-2007, Ext JS, LLC.
61655  *
61656  * Originally Released Under LGPL - original licence link has changed is not relivant.
61657  *
61658  * Fork - LGPL
61659  * <script type="text/javascript">
61660  */
61661
61662 /**
61663  * @class Roo.grid.AbstractSelectionModel
61664  * @extends Roo.util.Observable
61665  * @abstract
61666  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61667  * implemented by descendant classes.  This class should not be directly instantiated.
61668  * @constructor
61669  */
61670 Roo.grid.AbstractSelectionModel = function(){
61671     this.locked = false;
61672     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61673 };
61674
61675 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61676     /** @ignore Called by the grid automatically. Do not call directly. */
61677     init : function(grid){
61678         this.grid = grid;
61679         this.initEvents();
61680     },
61681
61682     /**
61683      * Locks the selections.
61684      */
61685     lock : function(){
61686         this.locked = true;
61687     },
61688
61689     /**
61690      * Unlocks the selections.
61691      */
61692     unlock : function(){
61693         this.locked = false;
61694     },
61695
61696     /**
61697      * Returns true if the selections are locked.
61698      * @return {Boolean}
61699      */
61700     isLocked : function(){
61701         return this.locked;
61702     }
61703 });/*
61704  * Based on:
61705  * Ext JS Library 1.1.1
61706  * Copyright(c) 2006-2007, Ext JS, LLC.
61707  *
61708  * Originally Released Under LGPL - original licence link has changed is not relivant.
61709  *
61710  * Fork - LGPL
61711  * <script type="text/javascript">
61712  */
61713 /**
61714  * @extends Roo.grid.AbstractSelectionModel
61715  * @class Roo.grid.RowSelectionModel
61716  * The default SelectionModel used by {@link Roo.grid.Grid}.
61717  * It supports multiple selections and keyboard selection/navigation. 
61718  * @constructor
61719  * @param {Object} config
61720  */
61721 Roo.grid.RowSelectionModel = function(config){
61722     Roo.apply(this, config);
61723     this.selections = new Roo.util.MixedCollection(false, function(o){
61724         return o.id;
61725     });
61726
61727     this.last = false;
61728     this.lastActive = false;
61729
61730     this.addEvents({
61731         /**
61732         * @event selectionchange
61733         * Fires when the selection changes
61734         * @param {SelectionModel} this
61735         */
61736        "selectionchange" : true,
61737        /**
61738         * @event afterselectionchange
61739         * Fires after the selection changes (eg. by key press or clicking)
61740         * @param {SelectionModel} this
61741         */
61742        "afterselectionchange" : true,
61743        /**
61744         * @event beforerowselect
61745         * Fires when a row is selected being selected, return false to cancel.
61746         * @param {SelectionModel} this
61747         * @param {Number} rowIndex The selected index
61748         * @param {Boolean} keepExisting False if other selections will be cleared
61749         */
61750        "beforerowselect" : true,
61751        /**
61752         * @event rowselect
61753         * Fires when a row is selected.
61754         * @param {SelectionModel} this
61755         * @param {Number} rowIndex The selected index
61756         * @param {Roo.data.Record} r The record
61757         */
61758        "rowselect" : true,
61759        /**
61760         * @event rowdeselect
61761         * Fires when a row is deselected.
61762         * @param {SelectionModel} this
61763         * @param {Number} rowIndex The selected index
61764         */
61765         "rowdeselect" : true
61766     });
61767     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61768     this.locked = false;
61769 };
61770
61771 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61772     /**
61773      * @cfg {Boolean} singleSelect
61774      * True to allow selection of only one row at a time (defaults to false)
61775      */
61776     singleSelect : false,
61777
61778     // private
61779     initEvents : function(){
61780
61781         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61782             this.grid.on("mousedown", this.handleMouseDown, this);
61783         }else{ // allow click to work like normal
61784             this.grid.on("rowclick", this.handleDragableRowClick, this);
61785         }
61786         // bootstrap does not have a view..
61787         var view = this.grid.view ? this.grid.view : this.grid;
61788         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61789             "up" : function(e){
61790                 if(!e.shiftKey){
61791                     this.selectPrevious(e.shiftKey);
61792                 }else if(this.last !== false && this.lastActive !== false){
61793                     var last = this.last;
61794                     this.selectRange(this.last,  this.lastActive-1);
61795                     view.focusRow(this.lastActive);
61796                     if(last !== false){
61797                         this.last = last;
61798                     }
61799                 }else{
61800                     this.selectFirstRow();
61801                 }
61802                 this.fireEvent("afterselectionchange", this);
61803             },
61804             "down" : function(e){
61805                 if(!e.shiftKey){
61806                     this.selectNext(e.shiftKey);
61807                 }else if(this.last !== false && this.lastActive !== false){
61808                     var last = this.last;
61809                     this.selectRange(this.last,  this.lastActive+1);
61810                     view.focusRow(this.lastActive);
61811                     if(last !== false){
61812                         this.last = last;
61813                     }
61814                 }else{
61815                     this.selectFirstRow();
61816                 }
61817                 this.fireEvent("afterselectionchange", this);
61818             },
61819             scope: this
61820         });
61821
61822          
61823         view.on("refresh", this.onRefresh, this);
61824         view.on("rowupdated", this.onRowUpdated, this);
61825         view.on("rowremoved", this.onRemove, this);
61826     },
61827
61828     // private
61829     onRefresh : function(){
61830         var ds = this.grid.ds, i, v = this.grid.view;
61831         var s = this.selections;
61832         s.each(function(r){
61833             if((i = ds.indexOfId(r.id)) != -1){
61834                 v.onRowSelect(i);
61835                 s.add(ds.getAt(i)); // updating the selection relate data
61836             }else{
61837                 s.remove(r);
61838             }
61839         });
61840     },
61841
61842     // private
61843     onRemove : function(v, index, r){
61844         this.selections.remove(r);
61845     },
61846
61847     // private
61848     onRowUpdated : function(v, index, r){
61849         if(this.isSelected(r)){
61850             v.onRowSelect(index);
61851         }
61852     },
61853
61854     /**
61855      * Select records.
61856      * @param {Array} records The records to select
61857      * @param {Boolean} keepExisting (optional) True to keep existing selections
61858      */
61859     selectRecords : function(records, keepExisting){
61860         if(!keepExisting){
61861             this.clearSelections();
61862         }
61863         var ds = this.grid.ds;
61864         for(var i = 0, len = records.length; i < len; i++){
61865             this.selectRow(ds.indexOf(records[i]), true);
61866         }
61867     },
61868
61869     /**
61870      * Gets the number of selected rows.
61871      * @return {Number}
61872      */
61873     getCount : function(){
61874         return this.selections.length;
61875     },
61876
61877     /**
61878      * Selects the first row in the grid.
61879      */
61880     selectFirstRow : function(){
61881         this.selectRow(0);
61882     },
61883
61884     /**
61885      * Select the last row.
61886      * @param {Boolean} keepExisting (optional) True to keep existing selections
61887      */
61888     selectLastRow : function(keepExisting){
61889         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61890     },
61891
61892     /**
61893      * Selects the row immediately following the last selected row.
61894      * @param {Boolean} keepExisting (optional) True to keep existing selections
61895      */
61896     selectNext : function(keepExisting){
61897         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61898             this.selectRow(this.last+1, keepExisting);
61899             var view = this.grid.view ? this.grid.view : this.grid;
61900             view.focusRow(this.last);
61901         }
61902     },
61903
61904     /**
61905      * Selects the row that precedes the last selected row.
61906      * @param {Boolean} keepExisting (optional) True to keep existing selections
61907      */
61908     selectPrevious : function(keepExisting){
61909         if(this.last){
61910             this.selectRow(this.last-1, keepExisting);
61911             var view = this.grid.view ? this.grid.view : this.grid;
61912             view.focusRow(this.last);
61913         }
61914     },
61915
61916     /**
61917      * Returns the selected records
61918      * @return {Array} Array of selected records
61919      */
61920     getSelections : function(){
61921         return [].concat(this.selections.items);
61922     },
61923
61924     /**
61925      * Returns the first selected record.
61926      * @return {Record}
61927      */
61928     getSelected : function(){
61929         return this.selections.itemAt(0);
61930     },
61931
61932
61933     /**
61934      * Clears all selections.
61935      */
61936     clearSelections : function(fast){
61937         if(this.locked) {
61938             return;
61939         }
61940         if(fast !== true){
61941             var ds = this.grid.ds;
61942             var s = this.selections;
61943             s.each(function(r){
61944                 this.deselectRow(ds.indexOfId(r.id));
61945             }, this);
61946             s.clear();
61947         }else{
61948             this.selections.clear();
61949         }
61950         this.last = false;
61951     },
61952
61953
61954     /**
61955      * Selects all rows.
61956      */
61957     selectAll : function(){
61958         if(this.locked) {
61959             return;
61960         }
61961         this.selections.clear();
61962         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61963             this.selectRow(i, true);
61964         }
61965     },
61966
61967     /**
61968      * Returns True if there is a selection.
61969      * @return {Boolean}
61970      */
61971     hasSelection : function(){
61972         return this.selections.length > 0;
61973     },
61974
61975     /**
61976      * Returns True if the specified row is selected.
61977      * @param {Number/Record} record The record or index of the record to check
61978      * @return {Boolean}
61979      */
61980     isSelected : function(index){
61981         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61982         return (r && this.selections.key(r.id) ? true : false);
61983     },
61984
61985     /**
61986      * Returns True if the specified record id is selected.
61987      * @param {String} id The id of record to check
61988      * @return {Boolean}
61989      */
61990     isIdSelected : function(id){
61991         return (this.selections.key(id) ? true : false);
61992     },
61993
61994     // private
61995     handleMouseDown : function(e, t)
61996     {
61997         var view = this.grid.view ? this.grid.view : this.grid;
61998         var rowIndex;
61999         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
62000             return;
62001         };
62002         if(e.shiftKey && this.last !== false){
62003             var last = this.last;
62004             this.selectRange(last, rowIndex, e.ctrlKey);
62005             this.last = last; // reset the last
62006             view.focusRow(rowIndex);
62007         }else{
62008             var isSelected = this.isSelected(rowIndex);
62009             if(e.button !== 0 && isSelected){
62010                 view.focusRow(rowIndex);
62011             }else if(e.ctrlKey && isSelected){
62012                 this.deselectRow(rowIndex);
62013             }else if(!isSelected){
62014                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62015                 view.focusRow(rowIndex);
62016             }
62017         }
62018         this.fireEvent("afterselectionchange", this);
62019     },
62020     // private
62021     handleDragableRowClick :  function(grid, rowIndex, e) 
62022     {
62023         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62024             this.selectRow(rowIndex, false);
62025             var view = this.grid.view ? this.grid.view : this.grid;
62026             view.focusRow(rowIndex);
62027              this.fireEvent("afterselectionchange", this);
62028         }
62029     },
62030     
62031     /**
62032      * Selects multiple rows.
62033      * @param {Array} rows Array of the indexes of the row to select
62034      * @param {Boolean} keepExisting (optional) True to keep existing selections
62035      */
62036     selectRows : function(rows, keepExisting){
62037         if(!keepExisting){
62038             this.clearSelections();
62039         }
62040         for(var i = 0, len = rows.length; i < len; i++){
62041             this.selectRow(rows[i], true);
62042         }
62043     },
62044
62045     /**
62046      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62047      * @param {Number} startRow The index of the first row in the range
62048      * @param {Number} endRow The index of the last row in the range
62049      * @param {Boolean} keepExisting (optional) True to retain existing selections
62050      */
62051     selectRange : function(startRow, endRow, keepExisting){
62052         if(this.locked) {
62053             return;
62054         }
62055         if(!keepExisting){
62056             this.clearSelections();
62057         }
62058         if(startRow <= endRow){
62059             for(var i = startRow; i <= endRow; i++){
62060                 this.selectRow(i, true);
62061             }
62062         }else{
62063             for(var i = startRow; i >= endRow; i--){
62064                 this.selectRow(i, true);
62065             }
62066         }
62067     },
62068
62069     /**
62070      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62071      * @param {Number} startRow The index of the first row in the range
62072      * @param {Number} endRow The index of the last row in the range
62073      */
62074     deselectRange : function(startRow, endRow, preventViewNotify){
62075         if(this.locked) {
62076             return;
62077         }
62078         for(var i = startRow; i <= endRow; i++){
62079             this.deselectRow(i, preventViewNotify);
62080         }
62081     },
62082
62083     /**
62084      * Selects a row.
62085      * @param {Number} row The index of the row to select
62086      * @param {Boolean} keepExisting (optional) True to keep existing selections
62087      */
62088     selectRow : function(index, keepExisting, preventViewNotify){
62089         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62090             return;
62091         }
62092         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62093             if(!keepExisting || this.singleSelect){
62094                 this.clearSelections();
62095             }
62096             var r = this.grid.ds.getAt(index);
62097             this.selections.add(r);
62098             this.last = this.lastActive = index;
62099             if(!preventViewNotify){
62100                 var view = this.grid.view ? this.grid.view : this.grid;
62101                 view.onRowSelect(index);
62102             }
62103             this.fireEvent("rowselect", this, index, r);
62104             this.fireEvent("selectionchange", this);
62105         }
62106     },
62107
62108     /**
62109      * Deselects a row.
62110      * @param {Number} row The index of the row to deselect
62111      */
62112     deselectRow : function(index, preventViewNotify){
62113         if(this.locked) {
62114             return;
62115         }
62116         if(this.last == index){
62117             this.last = false;
62118         }
62119         if(this.lastActive == index){
62120             this.lastActive = false;
62121         }
62122         var r = this.grid.ds.getAt(index);
62123         this.selections.remove(r);
62124         if(!preventViewNotify){
62125             var view = this.grid.view ? this.grid.view : this.grid;
62126             view.onRowDeselect(index);
62127         }
62128         this.fireEvent("rowdeselect", this, index);
62129         this.fireEvent("selectionchange", this);
62130     },
62131
62132     // private
62133     restoreLast : function(){
62134         if(this._last){
62135             this.last = this._last;
62136         }
62137     },
62138
62139     // private
62140     acceptsNav : function(row, col, cm){
62141         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62142     },
62143
62144     // private
62145     onEditorKey : function(field, e){
62146         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62147         if(k == e.TAB){
62148             e.stopEvent();
62149             ed.completeEdit();
62150             if(e.shiftKey){
62151                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62152             }else{
62153                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62154             }
62155         }else if(k == e.ENTER && !e.ctrlKey){
62156             e.stopEvent();
62157             ed.completeEdit();
62158             if(e.shiftKey){
62159                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62160             }else{
62161                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62162             }
62163         }else if(k == e.ESC){
62164             ed.cancelEdit();
62165         }
62166         if(newCell){
62167             g.startEditing(newCell[0], newCell[1]);
62168         }
62169     }
62170 });/*
62171  * Based on:
62172  * Ext JS Library 1.1.1
62173  * Copyright(c) 2006-2007, Ext JS, LLC.
62174  *
62175  * Originally Released Under LGPL - original licence link has changed is not relivant.
62176  *
62177  * Fork - LGPL
62178  * <script type="text/javascript">
62179  */
62180 /**
62181  * @class Roo.grid.CellSelectionModel
62182  * @extends Roo.grid.AbstractSelectionModel
62183  * This class provides the basic implementation for cell selection in a grid.
62184  * @constructor
62185  * @param {Object} config The object containing the configuration of this model.
62186  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62187  */
62188 Roo.grid.CellSelectionModel = function(config){
62189     Roo.apply(this, config);
62190
62191     this.selection = null;
62192
62193     this.addEvents({
62194         /**
62195              * @event beforerowselect
62196              * Fires before a cell is selected.
62197              * @param {SelectionModel} this
62198              * @param {Number} rowIndex The selected row index
62199              * @param {Number} colIndex The selected cell index
62200              */
62201             "beforecellselect" : true,
62202         /**
62203              * @event cellselect
62204              * Fires when a cell is selected.
62205              * @param {SelectionModel} this
62206              * @param {Number} rowIndex The selected row index
62207              * @param {Number} colIndex The selected cell index
62208              */
62209             "cellselect" : true,
62210         /**
62211              * @event selectionchange
62212              * Fires when the active selection changes.
62213              * @param {SelectionModel} this
62214              * @param {Object} selection null for no selection or an object (o) with two properties
62215                 <ul>
62216                 <li>o.record: the record object for the row the selection is in</li>
62217                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62218                 </ul>
62219              */
62220             "selectionchange" : true,
62221         /**
62222              * @event tabend
62223              * Fires when the tab (or enter) was pressed on the last editable cell
62224              * You can use this to trigger add new row.
62225              * @param {SelectionModel} this
62226              */
62227             "tabend" : true,
62228          /**
62229              * @event beforeeditnext
62230              * Fires before the next editable sell is made active
62231              * You can use this to skip to another cell or fire the tabend
62232              *    if you set cell to false
62233              * @param {Object} eventdata object : { cell : [ row, col ] } 
62234              */
62235             "beforeeditnext" : true
62236     });
62237     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62238 };
62239
62240 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62241     
62242     enter_is_tab: false,
62243
62244     /** @ignore */
62245     initEvents : function(){
62246         this.grid.on("mousedown", this.handleMouseDown, this);
62247         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62248         var view = this.grid.view;
62249         view.on("refresh", this.onViewChange, this);
62250         view.on("rowupdated", this.onRowUpdated, this);
62251         view.on("beforerowremoved", this.clearSelections, this);
62252         view.on("beforerowsinserted", this.clearSelections, this);
62253         if(this.grid.isEditor){
62254             this.grid.on("beforeedit", this.beforeEdit,  this);
62255         }
62256     },
62257
62258         //private
62259     beforeEdit : function(e){
62260         this.select(e.row, e.column, false, true, e.record);
62261     },
62262
62263         //private
62264     onRowUpdated : function(v, index, r){
62265         if(this.selection && this.selection.record == r){
62266             v.onCellSelect(index, this.selection.cell[1]);
62267         }
62268     },
62269
62270         //private
62271     onViewChange : function(){
62272         this.clearSelections(true);
62273     },
62274
62275         /**
62276          * Returns the currently selected cell,.
62277          * @return {Array} The selected cell (row, column) or null if none selected.
62278          */
62279     getSelectedCell : function(){
62280         return this.selection ? this.selection.cell : null;
62281     },
62282
62283     /**
62284      * Clears all selections.
62285      * @param {Boolean} true to prevent the gridview from being notified about the change.
62286      */
62287     clearSelections : function(preventNotify){
62288         var s = this.selection;
62289         if(s){
62290             if(preventNotify !== true){
62291                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62292             }
62293             this.selection = null;
62294             this.fireEvent("selectionchange", this, null);
62295         }
62296     },
62297
62298     /**
62299      * Returns true if there is a selection.
62300      * @return {Boolean}
62301      */
62302     hasSelection : function(){
62303         return this.selection ? true : false;
62304     },
62305
62306     /** @ignore */
62307     handleMouseDown : function(e, t){
62308         var v = this.grid.getView();
62309         if(this.isLocked()){
62310             return;
62311         };
62312         var row = v.findRowIndex(t);
62313         var cell = v.findCellIndex(t);
62314         if(row !== false && cell !== false){
62315             this.select(row, cell);
62316         }
62317     },
62318
62319     /**
62320      * Selects a cell.
62321      * @param {Number} rowIndex
62322      * @param {Number} collIndex
62323      */
62324     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62325         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62326             this.clearSelections();
62327             r = r || this.grid.dataSource.getAt(rowIndex);
62328             this.selection = {
62329                 record : r,
62330                 cell : [rowIndex, colIndex]
62331             };
62332             if(!preventViewNotify){
62333                 var v = this.grid.getView();
62334                 v.onCellSelect(rowIndex, colIndex);
62335                 if(preventFocus !== true){
62336                     v.focusCell(rowIndex, colIndex);
62337                 }
62338             }
62339             this.fireEvent("cellselect", this, rowIndex, colIndex);
62340             this.fireEvent("selectionchange", this, this.selection);
62341         }
62342     },
62343
62344         //private
62345     isSelectable : function(rowIndex, colIndex, cm){
62346         return !cm.isHidden(colIndex);
62347     },
62348
62349     /** @ignore */
62350     handleKeyDown : function(e){
62351         //Roo.log('Cell Sel Model handleKeyDown');
62352         if(!e.isNavKeyPress()){
62353             return;
62354         }
62355         var g = this.grid, s = this.selection;
62356         if(!s){
62357             e.stopEvent();
62358             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62359             if(cell){
62360                 this.select(cell[0], cell[1]);
62361             }
62362             return;
62363         }
62364         var sm = this;
62365         var walk = function(row, col, step){
62366             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62367         };
62368         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62369         var newCell;
62370
62371       
62372
62373         switch(k){
62374             case e.TAB:
62375                 // handled by onEditorKey
62376                 if (g.isEditor && g.editing) {
62377                     return;
62378                 }
62379                 if(e.shiftKey) {
62380                     newCell = walk(r, c-1, -1);
62381                 } else {
62382                     newCell = walk(r, c+1, 1);
62383                 }
62384                 break;
62385             
62386             case e.DOWN:
62387                newCell = walk(r+1, c, 1);
62388                 break;
62389             
62390             case e.UP:
62391                 newCell = walk(r-1, c, -1);
62392                 break;
62393             
62394             case e.RIGHT:
62395                 newCell = walk(r, c+1, 1);
62396                 break;
62397             
62398             case e.LEFT:
62399                 newCell = walk(r, c-1, -1);
62400                 break;
62401             
62402             case e.ENTER:
62403                 
62404                 if(g.isEditor && !g.editing){
62405                    g.startEditing(r, c);
62406                    e.stopEvent();
62407                    return;
62408                 }
62409                 
62410                 
62411              break;
62412         };
62413         if(newCell){
62414             this.select(newCell[0], newCell[1]);
62415             e.stopEvent();
62416             
62417         }
62418     },
62419
62420     acceptsNav : function(row, col, cm){
62421         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62422     },
62423     /**
62424      * Selects a cell.
62425      * @param {Number} field (not used) - as it's normally used as a listener
62426      * @param {Number} e - event - fake it by using
62427      *
62428      * var e = Roo.EventObjectImpl.prototype;
62429      * e.keyCode = e.TAB
62430      *
62431      * 
62432      */
62433     onEditorKey : function(field, e){
62434         
62435         var k = e.getKey(),
62436             newCell,
62437             g = this.grid,
62438             ed = g.activeEditor,
62439             forward = false;
62440         ///Roo.log('onEditorKey' + k);
62441         
62442         
62443         if (this.enter_is_tab && k == e.ENTER) {
62444             k = e.TAB;
62445         }
62446         
62447         if(k == e.TAB){
62448             if(e.shiftKey){
62449                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62450             }else{
62451                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62452                 forward = true;
62453             }
62454             
62455             e.stopEvent();
62456             
62457         } else if(k == e.ENTER &&  !e.ctrlKey){
62458             ed.completeEdit();
62459             e.stopEvent();
62460             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62461         
62462                 } else if(k == e.ESC){
62463             ed.cancelEdit();
62464         }
62465                 
62466         if (newCell) {
62467             var ecall = { cell : newCell, forward : forward };
62468             this.fireEvent('beforeeditnext', ecall );
62469             newCell = ecall.cell;
62470                         forward = ecall.forward;
62471         }
62472                 
62473         if(newCell){
62474             //Roo.log('next cell after edit');
62475             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62476         } else if (forward) {
62477             // tabbed past last
62478             this.fireEvent.defer(100, this, ['tabend',this]);
62479         }
62480     }
62481 });/*
62482  * Based on:
62483  * Ext JS Library 1.1.1
62484  * Copyright(c) 2006-2007, Ext JS, LLC.
62485  *
62486  * Originally Released Under LGPL - original licence link has changed is not relivant.
62487  *
62488  * Fork - LGPL
62489  * <script type="text/javascript">
62490  */
62491  
62492 /**
62493  * @class Roo.grid.EditorGrid
62494  * @extends Roo.grid.Grid
62495  * Class for creating and editable grid.
62496  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62497  * The container MUST have some type of size defined for the grid to fill. The container will be 
62498  * automatically set to position relative if it isn't already.
62499  * @param {Object} dataSource The data model to bind to
62500  * @param {Object} colModel The column model with info about this grid's columns
62501  */
62502 Roo.grid.EditorGrid = function(container, config){
62503     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62504     this.getGridEl().addClass("xedit-grid");
62505
62506     if(!this.selModel){
62507         this.selModel = new Roo.grid.CellSelectionModel();
62508     }
62509
62510     this.activeEditor = null;
62511
62512         this.addEvents({
62513             /**
62514              * @event beforeedit
62515              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62516              * <ul style="padding:5px;padding-left:16px;">
62517              * <li>grid - This grid</li>
62518              * <li>record - The record being edited</li>
62519              * <li>field - The field name being edited</li>
62520              * <li>value - The value for the field being edited.</li>
62521              * <li>row - The grid row index</li>
62522              * <li>column - The grid column index</li>
62523              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62524              * </ul>
62525              * @param {Object} e An edit event (see above for description)
62526              */
62527             "beforeedit" : true,
62528             /**
62529              * @event afteredit
62530              * Fires after a cell is edited. <br />
62531              * <ul style="padding:5px;padding-left:16px;">
62532              * <li>grid - This grid</li>
62533              * <li>record - The record being edited</li>
62534              * <li>field - The field name being edited</li>
62535              * <li>value - The value being set</li>
62536              * <li>originalValue - The original value for the field, before the edit.</li>
62537              * <li>row - The grid row index</li>
62538              * <li>column - The grid column index</li>
62539              * </ul>
62540              * @param {Object} e An edit event (see above for description)
62541              */
62542             "afteredit" : true,
62543             /**
62544              * @event validateedit
62545              * Fires after a cell is edited, but before the value is set in the record. 
62546          * You can use this to modify the value being set in the field, Return false
62547              * to cancel the change. The edit event object has the following properties <br />
62548              * <ul style="padding:5px;padding-left:16px;">
62549          * <li>editor - This editor</li>
62550              * <li>grid - This grid</li>
62551              * <li>record - The record being edited</li>
62552              * <li>field - The field name being edited</li>
62553              * <li>value - The value being set</li>
62554              * <li>originalValue - The original value for the field, before the edit.</li>
62555              * <li>row - The grid row index</li>
62556              * <li>column - The grid column index</li>
62557              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62558              * </ul>
62559              * @param {Object} e An edit event (see above for description)
62560              */
62561             "validateedit" : true
62562         });
62563     this.on("bodyscroll", this.stopEditing,  this);
62564     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62565 };
62566
62567 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62568     /**
62569      * @cfg {Number} clicksToEdit
62570      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62571      */
62572     clicksToEdit: 2,
62573
62574     // private
62575     isEditor : true,
62576     // private
62577     trackMouseOver: false, // causes very odd FF errors
62578
62579     onCellDblClick : function(g, row, col){
62580         this.startEditing(row, col);
62581     },
62582
62583     onEditComplete : function(ed, value, startValue){
62584         this.editing = false;
62585         this.activeEditor = null;
62586         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62587         var r = ed.record;
62588         var field = this.colModel.getDataIndex(ed.col);
62589         var e = {
62590             grid: this,
62591             record: r,
62592             field: field,
62593             originalValue: startValue,
62594             value: value,
62595             row: ed.row,
62596             column: ed.col,
62597             cancel:false,
62598             editor: ed
62599         };
62600         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62601         cell.show();
62602           
62603         if(String(value) !== String(startValue)){
62604             
62605             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62606                 r.set(field, e.value);
62607                 // if we are dealing with a combo box..
62608                 // then we also set the 'name' colum to be the displayField
62609                 if (ed.field.displayField && ed.field.name) {
62610                     r.set(ed.field.name, ed.field.el.dom.value);
62611                 }
62612                 
62613                 delete e.cancel; //?? why!!!
62614                 this.fireEvent("afteredit", e);
62615             }
62616         } else {
62617             this.fireEvent("afteredit", e); // always fire it!
62618         }
62619         this.view.focusCell(ed.row, ed.col);
62620     },
62621
62622     /**
62623      * Starts editing the specified for the specified row/column
62624      * @param {Number} rowIndex
62625      * @param {Number} colIndex
62626      */
62627     startEditing : function(row, col){
62628         this.stopEditing();
62629         if(this.colModel.isCellEditable(col, row)){
62630             this.view.ensureVisible(row, col, true);
62631           
62632             var r = this.dataSource.getAt(row);
62633             var field = this.colModel.getDataIndex(col);
62634             var cell = Roo.get(this.view.getCell(row,col));
62635             var e = {
62636                 grid: this,
62637                 record: r,
62638                 field: field,
62639                 value: r.data[field],
62640                 row: row,
62641                 column: col,
62642                 cancel:false 
62643             };
62644             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62645                 this.editing = true;
62646                 var ed = this.colModel.getCellEditor(col, row);
62647                 
62648                 if (!ed) {
62649                     return;
62650                 }
62651                 if(!ed.rendered){
62652                     ed.render(ed.parentEl || document.body);
62653                 }
62654                 ed.field.reset();
62655                
62656                 cell.hide();
62657                 
62658                 (function(){ // complex but required for focus issues in safari, ie and opera
62659                     ed.row = row;
62660                     ed.col = col;
62661                     ed.record = r;
62662                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62663                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62664                     this.activeEditor = ed;
62665                     var v = r.data[field];
62666                     ed.startEdit(this.view.getCell(row, col), v);
62667                     // combo's with 'displayField and name set
62668                     if (ed.field.displayField && ed.field.name) {
62669                         ed.field.el.dom.value = r.data[ed.field.name];
62670                     }
62671                     
62672                     
62673                 }).defer(50, this);
62674             }
62675         }
62676     },
62677         
62678     /**
62679      * Stops any active editing
62680      */
62681     stopEditing : function(){
62682         if(this.activeEditor){
62683             this.activeEditor.completeEdit();
62684         }
62685         this.activeEditor = null;
62686     },
62687         
62688          /**
62689      * Called to get grid's drag proxy text, by default returns this.ddText.
62690      * @return {String}
62691      */
62692     getDragDropText : function(){
62693         var count = this.selModel.getSelectedCell() ? 1 : 0;
62694         return String.format(this.ddText, count, count == 1 ? '' : 's');
62695     }
62696         
62697 });/*
62698  * Based on:
62699  * Ext JS Library 1.1.1
62700  * Copyright(c) 2006-2007, Ext JS, LLC.
62701  *
62702  * Originally Released Under LGPL - original licence link has changed is not relivant.
62703  *
62704  * Fork - LGPL
62705  * <script type="text/javascript">
62706  */
62707
62708 // private - not really -- you end up using it !
62709 // This is a support class used internally by the Grid components
62710
62711 /**
62712  * @class Roo.grid.GridEditor
62713  * @extends Roo.Editor
62714  * Class for creating and editable grid elements.
62715  * @param {Object} config any settings (must include field)
62716  */
62717 Roo.grid.GridEditor = function(field, config){
62718     if (!config && field.field) {
62719         config = field;
62720         field = Roo.factory(config.field, Roo.form);
62721     }
62722     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62723     field.monitorTab = false;
62724 };
62725
62726 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62727     
62728     /**
62729      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62730      */
62731     
62732     alignment: "tl-tl",
62733     autoSize: "width",
62734     hideEl : false,
62735     cls: "x-small-editor x-grid-editor",
62736     shim:false,
62737     shadow:"frame"
62738 });/*
62739  * Based on:
62740  * Ext JS Library 1.1.1
62741  * Copyright(c) 2006-2007, Ext JS, LLC.
62742  *
62743  * Originally Released Under LGPL - original licence link has changed is not relivant.
62744  *
62745  * Fork - LGPL
62746  * <script type="text/javascript">
62747  */
62748   
62749
62750   
62751 Roo.grid.PropertyRecord = Roo.data.Record.create([
62752     {name:'name',type:'string'},  'value'
62753 ]);
62754
62755
62756 Roo.grid.PropertyStore = function(grid, source){
62757     this.grid = grid;
62758     this.store = new Roo.data.Store({
62759         recordType : Roo.grid.PropertyRecord
62760     });
62761     this.store.on('update', this.onUpdate,  this);
62762     if(source){
62763         this.setSource(source);
62764     }
62765     Roo.grid.PropertyStore.superclass.constructor.call(this);
62766 };
62767
62768
62769
62770 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62771     setSource : function(o){
62772         this.source = o;
62773         this.store.removeAll();
62774         var data = [];
62775         for(var k in o){
62776             if(this.isEditableValue(o[k])){
62777                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62778             }
62779         }
62780         this.store.loadRecords({records: data}, {}, true);
62781     },
62782
62783     onUpdate : function(ds, record, type){
62784         if(type == Roo.data.Record.EDIT){
62785             var v = record.data['value'];
62786             var oldValue = record.modified['value'];
62787             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62788                 this.source[record.id] = v;
62789                 record.commit();
62790                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62791             }else{
62792                 record.reject();
62793             }
62794         }
62795     },
62796
62797     getProperty : function(row){
62798        return this.store.getAt(row);
62799     },
62800
62801     isEditableValue: function(val){
62802         if(val && val instanceof Date){
62803             return true;
62804         }else if(typeof val == 'object' || typeof val == 'function'){
62805             return false;
62806         }
62807         return true;
62808     },
62809
62810     setValue : function(prop, value){
62811         this.source[prop] = value;
62812         this.store.getById(prop).set('value', value);
62813     },
62814
62815     getSource : function(){
62816         return this.source;
62817     }
62818 });
62819
62820 Roo.grid.PropertyColumnModel = function(grid, store){
62821     this.grid = grid;
62822     var g = Roo.grid;
62823     g.PropertyColumnModel.superclass.constructor.call(this, [
62824         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62825         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62826     ]);
62827     this.store = store;
62828     this.bselect = Roo.DomHelper.append(document.body, {
62829         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62830             {tag: 'option', value: 'true', html: 'true'},
62831             {tag: 'option', value: 'false', html: 'false'}
62832         ]
62833     });
62834     Roo.id(this.bselect);
62835     var f = Roo.form;
62836     this.editors = {
62837         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62838         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62839         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62840         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62841         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62842     };
62843     this.renderCellDelegate = this.renderCell.createDelegate(this);
62844     this.renderPropDelegate = this.renderProp.createDelegate(this);
62845 };
62846
62847 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62848     
62849     
62850     nameText : 'Name',
62851     valueText : 'Value',
62852     
62853     dateFormat : 'm/j/Y',
62854     
62855     
62856     renderDate : function(dateVal){
62857         return dateVal.dateFormat(this.dateFormat);
62858     },
62859
62860     renderBool : function(bVal){
62861         return bVal ? 'true' : 'false';
62862     },
62863
62864     isCellEditable : function(colIndex, rowIndex){
62865         return colIndex == 1;
62866     },
62867
62868     getRenderer : function(col){
62869         return col == 1 ?
62870             this.renderCellDelegate : this.renderPropDelegate;
62871     },
62872
62873     renderProp : function(v){
62874         return this.getPropertyName(v);
62875     },
62876
62877     renderCell : function(val){
62878         var rv = val;
62879         if(val instanceof Date){
62880             rv = this.renderDate(val);
62881         }else if(typeof val == 'boolean'){
62882             rv = this.renderBool(val);
62883         }
62884         return Roo.util.Format.htmlEncode(rv);
62885     },
62886
62887     getPropertyName : function(name){
62888         var pn = this.grid.propertyNames;
62889         return pn && pn[name] ? pn[name] : name;
62890     },
62891
62892     getCellEditor : function(colIndex, rowIndex){
62893         var p = this.store.getProperty(rowIndex);
62894         var n = p.data['name'], val = p.data['value'];
62895         
62896         if(typeof(this.grid.customEditors[n]) == 'string'){
62897             return this.editors[this.grid.customEditors[n]];
62898         }
62899         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62900             return this.grid.customEditors[n];
62901         }
62902         if(val instanceof Date){
62903             return this.editors['date'];
62904         }else if(typeof val == 'number'){
62905             return this.editors['number'];
62906         }else if(typeof val == 'boolean'){
62907             return this.editors['boolean'];
62908         }else{
62909             return this.editors['string'];
62910         }
62911     }
62912 });
62913
62914 /**
62915  * @class Roo.grid.PropertyGrid
62916  * @extends Roo.grid.EditorGrid
62917  * This class represents the  interface of a component based property grid control.
62918  * <br><br>Usage:<pre><code>
62919  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62920       
62921  });
62922  // set any options
62923  grid.render();
62924  * </code></pre>
62925   
62926  * @constructor
62927  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62928  * The container MUST have some type of size defined for the grid to fill. The container will be
62929  * automatically set to position relative if it isn't already.
62930  * @param {Object} config A config object that sets properties on this grid.
62931  */
62932 Roo.grid.PropertyGrid = function(container, config){
62933     config = config || {};
62934     var store = new Roo.grid.PropertyStore(this);
62935     this.store = store;
62936     var cm = new Roo.grid.PropertyColumnModel(this, store);
62937     store.store.sort('name', 'ASC');
62938     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62939         ds: store.store,
62940         cm: cm,
62941         enableColLock:false,
62942         enableColumnMove:false,
62943         stripeRows:false,
62944         trackMouseOver: false,
62945         clicksToEdit:1
62946     }, config));
62947     this.getGridEl().addClass('x-props-grid');
62948     this.lastEditRow = null;
62949     this.on('columnresize', this.onColumnResize, this);
62950     this.addEvents({
62951          /**
62952              * @event beforepropertychange
62953              * Fires before a property changes (return false to stop?)
62954              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62955              * @param {String} id Record Id
62956              * @param {String} newval New Value
62957          * @param {String} oldval Old Value
62958              */
62959         "beforepropertychange": true,
62960         /**
62961              * @event propertychange
62962              * Fires after a property changes
62963              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62964              * @param {String} id Record Id
62965              * @param {String} newval New Value
62966          * @param {String} oldval Old Value
62967              */
62968         "propertychange": true
62969     });
62970     this.customEditors = this.customEditors || {};
62971 };
62972 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62973     
62974      /**
62975      * @cfg {Object} customEditors map of colnames=> custom editors.
62976      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62977      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62978      * false disables editing of the field.
62979          */
62980     
62981       /**
62982      * @cfg {Object} propertyNames map of property Names to their displayed value
62983          */
62984     
62985     render : function(){
62986         Roo.grid.PropertyGrid.superclass.render.call(this);
62987         this.autoSize.defer(100, this);
62988     },
62989
62990     autoSize : function(){
62991         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62992         if(this.view){
62993             this.view.fitColumns();
62994         }
62995     },
62996
62997     onColumnResize : function(){
62998         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62999         this.autoSize();
63000     },
63001     /**
63002      * Sets the data for the Grid
63003      * accepts a Key => Value object of all the elements avaiable.
63004      * @param {Object} data  to appear in grid.
63005      */
63006     setSource : function(source){
63007         this.store.setSource(source);
63008         //this.autoSize();
63009     },
63010     /**
63011      * Gets all the data from the grid.
63012      * @return {Object} data  data stored in grid
63013      */
63014     getSource : function(){
63015         return this.store.getSource();
63016     }
63017 });/*
63018   
63019  * Licence LGPL
63020  
63021  */
63022  
63023 /**
63024  * @class Roo.grid.Calendar
63025  * @extends Roo.grid.Grid
63026  * This class extends the Grid to provide a calendar widget
63027  * <br><br>Usage:<pre><code>
63028  var grid = new Roo.grid.Calendar("my-container-id", {
63029      ds: myDataStore,
63030      cm: myColModel,
63031      selModel: mySelectionModel,
63032      autoSizeColumns: true,
63033      monitorWindowResize: false,
63034      trackMouseOver: true
63035      eventstore : real data store..
63036  });
63037  // set any options
63038  grid.render();
63039   
63040   * @constructor
63041  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63042  * The container MUST have some type of size defined for the grid to fill. The container will be
63043  * automatically set to position relative if it isn't already.
63044  * @param {Object} config A config object that sets properties on this grid.
63045  */
63046 Roo.grid.Calendar = function(container, config){
63047         // initialize the container
63048         this.container = Roo.get(container);
63049         this.container.update("");
63050         this.container.setStyle("overflow", "hidden");
63051     this.container.addClass('x-grid-container');
63052
63053     this.id = this.container.id;
63054
63055     Roo.apply(this, config);
63056     // check and correct shorthanded configs
63057     
63058     var rows = [];
63059     var d =1;
63060     for (var r = 0;r < 6;r++) {
63061         
63062         rows[r]=[];
63063         for (var c =0;c < 7;c++) {
63064             rows[r][c]= '';
63065         }
63066     }
63067     if (this.eventStore) {
63068         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63069         this.eventStore.on('load',this.onLoad, this);
63070         this.eventStore.on('beforeload',this.clearEvents, this);
63071          
63072     }
63073     
63074     this.dataSource = new Roo.data.Store({
63075             proxy: new Roo.data.MemoryProxy(rows),
63076             reader: new Roo.data.ArrayReader({}, [
63077                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63078     });
63079
63080     this.dataSource.load();
63081     this.ds = this.dataSource;
63082     this.ds.xmodule = this.xmodule || false;
63083     
63084     
63085     var cellRender = function(v,x,r)
63086     {
63087         return String.format(
63088             '<div class="fc-day  fc-widget-content"><div>' +
63089                 '<div class="fc-event-container"></div>' +
63090                 '<div class="fc-day-number">{0}</div>'+
63091                 
63092                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63093             '</div></div>', v);
63094     
63095     }
63096     
63097     
63098     this.colModel = new Roo.grid.ColumnModel( [
63099         {
63100             xtype: 'ColumnModel',
63101             xns: Roo.grid,
63102             dataIndex : 'weekday0',
63103             header : 'Sunday',
63104             renderer : cellRender
63105         },
63106         {
63107             xtype: 'ColumnModel',
63108             xns: Roo.grid,
63109             dataIndex : 'weekday1',
63110             header : 'Monday',
63111             renderer : cellRender
63112         },
63113         {
63114             xtype: 'ColumnModel',
63115             xns: Roo.grid,
63116             dataIndex : 'weekday2',
63117             header : 'Tuesday',
63118             renderer : cellRender
63119         },
63120         {
63121             xtype: 'ColumnModel',
63122             xns: Roo.grid,
63123             dataIndex : 'weekday3',
63124             header : 'Wednesday',
63125             renderer : cellRender
63126         },
63127         {
63128             xtype: 'ColumnModel',
63129             xns: Roo.grid,
63130             dataIndex : 'weekday4',
63131             header : 'Thursday',
63132             renderer : cellRender
63133         },
63134         {
63135             xtype: 'ColumnModel',
63136             xns: Roo.grid,
63137             dataIndex : 'weekday5',
63138             header : 'Friday',
63139             renderer : cellRender
63140         },
63141         {
63142             xtype: 'ColumnModel',
63143             xns: Roo.grid,
63144             dataIndex : 'weekday6',
63145             header : 'Saturday',
63146             renderer : cellRender
63147         }
63148     ]);
63149     this.cm = this.colModel;
63150     this.cm.xmodule = this.xmodule || false;
63151  
63152         
63153           
63154     //this.selModel = new Roo.grid.CellSelectionModel();
63155     //this.sm = this.selModel;
63156     //this.selModel.init(this);
63157     
63158     
63159     if(this.width){
63160         this.container.setWidth(this.width);
63161     }
63162
63163     if(this.height){
63164         this.container.setHeight(this.height);
63165     }
63166     /** @private */
63167         this.addEvents({
63168         // raw events
63169         /**
63170          * @event click
63171          * The raw click event for the entire grid.
63172          * @param {Roo.EventObject} e
63173          */
63174         "click" : true,
63175         /**
63176          * @event dblclick
63177          * The raw dblclick event for the entire grid.
63178          * @param {Roo.EventObject} e
63179          */
63180         "dblclick" : true,
63181         /**
63182          * @event contextmenu
63183          * The raw contextmenu event for the entire grid.
63184          * @param {Roo.EventObject} e
63185          */
63186         "contextmenu" : true,
63187         /**
63188          * @event mousedown
63189          * The raw mousedown event for the entire grid.
63190          * @param {Roo.EventObject} e
63191          */
63192         "mousedown" : true,
63193         /**
63194          * @event mouseup
63195          * The raw mouseup event for the entire grid.
63196          * @param {Roo.EventObject} e
63197          */
63198         "mouseup" : true,
63199         /**
63200          * @event mouseover
63201          * The raw mouseover event for the entire grid.
63202          * @param {Roo.EventObject} e
63203          */
63204         "mouseover" : true,
63205         /**
63206          * @event mouseout
63207          * The raw mouseout event for the entire grid.
63208          * @param {Roo.EventObject} e
63209          */
63210         "mouseout" : true,
63211         /**
63212          * @event keypress
63213          * The raw keypress event for the entire grid.
63214          * @param {Roo.EventObject} e
63215          */
63216         "keypress" : true,
63217         /**
63218          * @event keydown
63219          * The raw keydown event for the entire grid.
63220          * @param {Roo.EventObject} e
63221          */
63222         "keydown" : true,
63223
63224         // custom events
63225
63226         /**
63227          * @event cellclick
63228          * Fires when a cell is clicked
63229          * @param {Grid} this
63230          * @param {Number} rowIndex
63231          * @param {Number} columnIndex
63232          * @param {Roo.EventObject} e
63233          */
63234         "cellclick" : true,
63235         /**
63236          * @event celldblclick
63237          * Fires when a cell is double clicked
63238          * @param {Grid} this
63239          * @param {Number} rowIndex
63240          * @param {Number} columnIndex
63241          * @param {Roo.EventObject} e
63242          */
63243         "celldblclick" : true,
63244         /**
63245          * @event rowclick
63246          * Fires when a row is clicked
63247          * @param {Grid} this
63248          * @param {Number} rowIndex
63249          * @param {Roo.EventObject} e
63250          */
63251         "rowclick" : true,
63252         /**
63253          * @event rowdblclick
63254          * Fires when a row is double clicked
63255          * @param {Grid} this
63256          * @param {Number} rowIndex
63257          * @param {Roo.EventObject} e
63258          */
63259         "rowdblclick" : true,
63260         /**
63261          * @event headerclick
63262          * Fires when a header is clicked
63263          * @param {Grid} this
63264          * @param {Number} columnIndex
63265          * @param {Roo.EventObject} e
63266          */
63267         "headerclick" : true,
63268         /**
63269          * @event headerdblclick
63270          * Fires when a header cell is double clicked
63271          * @param {Grid} this
63272          * @param {Number} columnIndex
63273          * @param {Roo.EventObject} e
63274          */
63275         "headerdblclick" : true,
63276         /**
63277          * @event rowcontextmenu
63278          * Fires when a row is right clicked
63279          * @param {Grid} this
63280          * @param {Number} rowIndex
63281          * @param {Roo.EventObject} e
63282          */
63283         "rowcontextmenu" : true,
63284         /**
63285          * @event cellcontextmenu
63286          * Fires when a cell is right clicked
63287          * @param {Grid} this
63288          * @param {Number} rowIndex
63289          * @param {Number} cellIndex
63290          * @param {Roo.EventObject} e
63291          */
63292          "cellcontextmenu" : true,
63293         /**
63294          * @event headercontextmenu
63295          * Fires when a header is right clicked
63296          * @param {Grid} this
63297          * @param {Number} columnIndex
63298          * @param {Roo.EventObject} e
63299          */
63300         "headercontextmenu" : true,
63301         /**
63302          * @event bodyscroll
63303          * Fires when the body element is scrolled
63304          * @param {Number} scrollLeft
63305          * @param {Number} scrollTop
63306          */
63307         "bodyscroll" : true,
63308         /**
63309          * @event columnresize
63310          * Fires when the user resizes a column
63311          * @param {Number} columnIndex
63312          * @param {Number} newSize
63313          */
63314         "columnresize" : true,
63315         /**
63316          * @event columnmove
63317          * Fires when the user moves a column
63318          * @param {Number} oldIndex
63319          * @param {Number} newIndex
63320          */
63321         "columnmove" : true,
63322         /**
63323          * @event startdrag
63324          * Fires when row(s) start being dragged
63325          * @param {Grid} this
63326          * @param {Roo.GridDD} dd The drag drop object
63327          * @param {event} e The raw browser event
63328          */
63329         "startdrag" : true,
63330         /**
63331          * @event enddrag
63332          * Fires when a drag operation is complete
63333          * @param {Grid} this
63334          * @param {Roo.GridDD} dd The drag drop object
63335          * @param {event} e The raw browser event
63336          */
63337         "enddrag" : true,
63338         /**
63339          * @event dragdrop
63340          * Fires when dragged row(s) are dropped on a valid DD target
63341          * @param {Grid} this
63342          * @param {Roo.GridDD} dd The drag drop object
63343          * @param {String} targetId The target drag drop object
63344          * @param {event} e The raw browser event
63345          */
63346         "dragdrop" : true,
63347         /**
63348          * @event dragover
63349          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63350          * @param {Grid} this
63351          * @param {Roo.GridDD} dd The drag drop object
63352          * @param {String} targetId The target drag drop object
63353          * @param {event} e The raw browser event
63354          */
63355         "dragover" : true,
63356         /**
63357          * @event dragenter
63358          *  Fires when the dragged row(s) first cross another DD target while being dragged
63359          * @param {Grid} this
63360          * @param {Roo.GridDD} dd The drag drop object
63361          * @param {String} targetId The target drag drop object
63362          * @param {event} e The raw browser event
63363          */
63364         "dragenter" : true,
63365         /**
63366          * @event dragout
63367          * Fires when the dragged row(s) leave another DD target while being dragged
63368          * @param {Grid} this
63369          * @param {Roo.GridDD} dd The drag drop object
63370          * @param {String} targetId The target drag drop object
63371          * @param {event} e The raw browser event
63372          */
63373         "dragout" : true,
63374         /**
63375          * @event rowclass
63376          * Fires when a row is rendered, so you can change add a style to it.
63377          * @param {GridView} gridview   The grid view
63378          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63379          */
63380         'rowclass' : true,
63381
63382         /**
63383          * @event render
63384          * Fires when the grid is rendered
63385          * @param {Grid} grid
63386          */
63387         'render' : true,
63388             /**
63389              * @event select
63390              * Fires when a date is selected
63391              * @param {DatePicker} this
63392              * @param {Date} date The selected date
63393              */
63394         'select': true,
63395         /**
63396              * @event monthchange
63397              * Fires when the displayed month changes 
63398              * @param {DatePicker} this
63399              * @param {Date} date The selected month
63400              */
63401         'monthchange': true,
63402         /**
63403              * @event evententer
63404              * Fires when mouse over an event
63405              * @param {Calendar} this
63406              * @param {event} Event
63407              */
63408         'evententer': true,
63409         /**
63410              * @event eventleave
63411              * Fires when the mouse leaves an
63412              * @param {Calendar} this
63413              * @param {event}
63414              */
63415         'eventleave': true,
63416         /**
63417              * @event eventclick
63418              * Fires when the mouse click an
63419              * @param {Calendar} this
63420              * @param {event}
63421              */
63422         'eventclick': true,
63423         /**
63424              * @event eventrender
63425              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63426              * @param {Calendar} this
63427              * @param {data} data to be modified
63428              */
63429         'eventrender': true
63430         
63431     });
63432
63433     Roo.grid.Grid.superclass.constructor.call(this);
63434     this.on('render', function() {
63435         this.view.el.addClass('x-grid-cal'); 
63436         
63437         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63438
63439     },this);
63440     
63441     if (!Roo.grid.Calendar.style) {
63442         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63443             
63444             
63445             '.x-grid-cal .x-grid-col' :  {
63446                 height: 'auto !important',
63447                 'vertical-align': 'top'
63448             },
63449             '.x-grid-cal  .fc-event-hori' : {
63450                 height: '14px'
63451             }
63452              
63453             
63454         }, Roo.id());
63455     }
63456
63457     
63458     
63459 };
63460 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63461     /**
63462      * @cfg {Store} eventStore The store that loads events.
63463      */
63464     eventStore : 25,
63465
63466      
63467     activeDate : false,
63468     startDay : 0,
63469     autoWidth : true,
63470     monitorWindowResize : false,
63471
63472     
63473     resizeColumns : function() {
63474         var col = (this.view.el.getWidth() / 7) - 3;
63475         // loop through cols, and setWidth
63476         for(var i =0 ; i < 7 ; i++){
63477             this.cm.setColumnWidth(i, col);
63478         }
63479     },
63480      setDate :function(date) {
63481         
63482         Roo.log('setDate?');
63483         
63484         this.resizeColumns();
63485         var vd = this.activeDate;
63486         this.activeDate = date;
63487 //        if(vd && this.el){
63488 //            var t = date.getTime();
63489 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63490 //                Roo.log('using add remove');
63491 //                
63492 //                this.fireEvent('monthchange', this, date);
63493 //                
63494 //                this.cells.removeClass("fc-state-highlight");
63495 //                this.cells.each(function(c){
63496 //                   if(c.dateValue == t){
63497 //                       c.addClass("fc-state-highlight");
63498 //                       setTimeout(function(){
63499 //                            try{c.dom.firstChild.focus();}catch(e){}
63500 //                       }, 50);
63501 //                       return false;
63502 //                   }
63503 //                   return true;
63504 //                });
63505 //                return;
63506 //            }
63507 //        }
63508         
63509         var days = date.getDaysInMonth();
63510         
63511         var firstOfMonth = date.getFirstDateOfMonth();
63512         var startingPos = firstOfMonth.getDay()-this.startDay;
63513         
63514         if(startingPos < this.startDay){
63515             startingPos += 7;
63516         }
63517         
63518         var pm = date.add(Date.MONTH, -1);
63519         var prevStart = pm.getDaysInMonth()-startingPos;
63520 //        
63521         
63522         
63523         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63524         
63525         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63526         //this.cells.addClassOnOver('fc-state-hover');
63527         
63528         var cells = this.cells.elements;
63529         var textEls = this.textNodes;
63530         
63531         //Roo.each(cells, function(cell){
63532         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63533         //});
63534         
63535         days += startingPos;
63536
63537         // convert everything to numbers so it's fast
63538         var day = 86400000;
63539         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63540         //Roo.log(d);
63541         //Roo.log(pm);
63542         //Roo.log(prevStart);
63543         
63544         var today = new Date().clearTime().getTime();
63545         var sel = date.clearTime().getTime();
63546         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63547         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63548         var ddMatch = this.disabledDatesRE;
63549         var ddText = this.disabledDatesText;
63550         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63551         var ddaysText = this.disabledDaysText;
63552         var format = this.format;
63553         
63554         var setCellClass = function(cal, cell){
63555             
63556             //Roo.log('set Cell Class');
63557             cell.title = "";
63558             var t = d.getTime();
63559             
63560             //Roo.log(d);
63561             
63562             
63563             cell.dateValue = t;
63564             if(t == today){
63565                 cell.className += " fc-today";
63566                 cell.className += " fc-state-highlight";
63567                 cell.title = cal.todayText;
63568             }
63569             if(t == sel){
63570                 // disable highlight in other month..
63571                 cell.className += " fc-state-highlight";
63572                 
63573             }
63574             // disabling
63575             if(t < min) {
63576                 //cell.className = " fc-state-disabled";
63577                 cell.title = cal.minText;
63578                 return;
63579             }
63580             if(t > max) {
63581                 //cell.className = " fc-state-disabled";
63582                 cell.title = cal.maxText;
63583                 return;
63584             }
63585             if(ddays){
63586                 if(ddays.indexOf(d.getDay()) != -1){
63587                     // cell.title = ddaysText;
63588                    // cell.className = " fc-state-disabled";
63589                 }
63590             }
63591             if(ddMatch && format){
63592                 var fvalue = d.dateFormat(format);
63593                 if(ddMatch.test(fvalue)){
63594                     cell.title = ddText.replace("%0", fvalue);
63595                    cell.className = " fc-state-disabled";
63596                 }
63597             }
63598             
63599             if (!cell.initialClassName) {
63600                 cell.initialClassName = cell.dom.className;
63601             }
63602             
63603             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63604         };
63605
63606         var i = 0;
63607         
63608         for(; i < startingPos; i++) {
63609             cells[i].dayName =  (++prevStart);
63610             Roo.log(textEls[i]);
63611             d.setDate(d.getDate()+1);
63612             
63613             //cells[i].className = "fc-past fc-other-month";
63614             setCellClass(this, cells[i]);
63615         }
63616         
63617         var intDay = 0;
63618         
63619         for(; i < days; i++){
63620             intDay = i - startingPos + 1;
63621             cells[i].dayName =  (intDay);
63622             d.setDate(d.getDate()+1);
63623             
63624             cells[i].className = ''; // "x-date-active";
63625             setCellClass(this, cells[i]);
63626         }
63627         var extraDays = 0;
63628         
63629         for(; i < 42; i++) {
63630             //textEls[i].innerHTML = (++extraDays);
63631             
63632             d.setDate(d.getDate()+1);
63633             cells[i].dayName = (++extraDays);
63634             cells[i].className = "fc-future fc-other-month";
63635             setCellClass(this, cells[i]);
63636         }
63637         
63638         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63639         
63640         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63641         
63642         // this will cause all the cells to mis
63643         var rows= [];
63644         var i =0;
63645         for (var r = 0;r < 6;r++) {
63646             for (var c =0;c < 7;c++) {
63647                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63648             }    
63649         }
63650         
63651         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63652         for(i=0;i<cells.length;i++) {
63653             
63654             this.cells.elements[i].dayName = cells[i].dayName ;
63655             this.cells.elements[i].className = cells[i].className;
63656             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63657             this.cells.elements[i].title = cells[i].title ;
63658             this.cells.elements[i].dateValue = cells[i].dateValue ;
63659         }
63660         
63661         
63662         
63663         
63664         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63665         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63666         
63667         ////if(totalRows != 6){
63668             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63669            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63670        // }
63671         
63672         this.fireEvent('monthchange', this, date);
63673         
63674         
63675     },
63676  /**
63677      * Returns the grid's SelectionModel.
63678      * @return {SelectionModel}
63679      */
63680     getSelectionModel : function(){
63681         if(!this.selModel){
63682             this.selModel = new Roo.grid.CellSelectionModel();
63683         }
63684         return this.selModel;
63685     },
63686
63687     load: function() {
63688         this.eventStore.load()
63689         
63690         
63691         
63692     },
63693     
63694     findCell : function(dt) {
63695         dt = dt.clearTime().getTime();
63696         var ret = false;
63697         this.cells.each(function(c){
63698             //Roo.log("check " +c.dateValue + '?=' + dt);
63699             if(c.dateValue == dt){
63700                 ret = c;
63701                 return false;
63702             }
63703             return true;
63704         });
63705         
63706         return ret;
63707     },
63708     
63709     findCells : function(rec) {
63710         var s = rec.data.start_dt.clone().clearTime().getTime();
63711        // Roo.log(s);
63712         var e= rec.data.end_dt.clone().clearTime().getTime();
63713        // Roo.log(e);
63714         var ret = [];
63715         this.cells.each(function(c){
63716              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63717             
63718             if(c.dateValue > e){
63719                 return ;
63720             }
63721             if(c.dateValue < s){
63722                 return ;
63723             }
63724             ret.push(c);
63725         });
63726         
63727         return ret;    
63728     },
63729     
63730     findBestRow: function(cells)
63731     {
63732         var ret = 0;
63733         
63734         for (var i =0 ; i < cells.length;i++) {
63735             ret  = Math.max(cells[i].rows || 0,ret);
63736         }
63737         return ret;
63738         
63739     },
63740     
63741     
63742     addItem : function(rec)
63743     {
63744         // look for vertical location slot in
63745         var cells = this.findCells(rec);
63746         
63747         rec.row = this.findBestRow(cells);
63748         
63749         // work out the location.
63750         
63751         var crow = false;
63752         var rows = [];
63753         for(var i =0; i < cells.length; i++) {
63754             if (!crow) {
63755                 crow = {
63756                     start : cells[i],
63757                     end :  cells[i]
63758                 };
63759                 continue;
63760             }
63761             if (crow.start.getY() == cells[i].getY()) {
63762                 // on same row.
63763                 crow.end = cells[i];
63764                 continue;
63765             }
63766             // different row.
63767             rows.push(crow);
63768             crow = {
63769                 start: cells[i],
63770                 end : cells[i]
63771             };
63772             
63773         }
63774         
63775         rows.push(crow);
63776         rec.els = [];
63777         rec.rows = rows;
63778         rec.cells = cells;
63779         for (var i = 0; i < cells.length;i++) {
63780             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63781             
63782         }
63783         
63784         
63785     },
63786     
63787     clearEvents: function() {
63788         
63789         if (!this.eventStore.getCount()) {
63790             return;
63791         }
63792         // reset number of rows in cells.
63793         Roo.each(this.cells.elements, function(c){
63794             c.rows = 0;
63795         });
63796         
63797         this.eventStore.each(function(e) {
63798             this.clearEvent(e);
63799         },this);
63800         
63801     },
63802     
63803     clearEvent : function(ev)
63804     {
63805         if (ev.els) {
63806             Roo.each(ev.els, function(el) {
63807                 el.un('mouseenter' ,this.onEventEnter, this);
63808                 el.un('mouseleave' ,this.onEventLeave, this);
63809                 el.remove();
63810             },this);
63811             ev.els = [];
63812         }
63813     },
63814     
63815     
63816     renderEvent : function(ev,ctr) {
63817         if (!ctr) {
63818              ctr = this.view.el.select('.fc-event-container',true).first();
63819         }
63820         
63821          
63822         this.clearEvent(ev);
63823             //code
63824        
63825         
63826         
63827         ev.els = [];
63828         var cells = ev.cells;
63829         var rows = ev.rows;
63830         this.fireEvent('eventrender', this, ev);
63831         
63832         for(var i =0; i < rows.length; i++) {
63833             
63834             cls = '';
63835             if (i == 0) {
63836                 cls += ' fc-event-start';
63837             }
63838             if ((i+1) == rows.length) {
63839                 cls += ' fc-event-end';
63840             }
63841             
63842             //Roo.log(ev.data);
63843             // how many rows should it span..
63844             var cg = this.eventTmpl.append(ctr,Roo.apply({
63845                 fccls : cls
63846                 
63847             }, ev.data) , true);
63848             
63849             
63850             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63851             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63852             cg.on('click', this.onEventClick, this, ev);
63853             
63854             ev.els.push(cg);
63855             
63856             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63857             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63858             //Roo.log(cg);
63859              
63860             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63861             cg.setWidth(ebox.right - sbox.x -2);
63862         }
63863     },
63864     
63865     renderEvents: function()
63866     {   
63867         // first make sure there is enough space..
63868         
63869         if (!this.eventTmpl) {
63870             this.eventTmpl = new Roo.Template(
63871                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63872                     '<div class="fc-event-inner">' +
63873                         '<span class="fc-event-time">{time}</span>' +
63874                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63875                     '</div>' +
63876                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63877                 '</div>'
63878             );
63879                 
63880         }
63881                
63882         
63883         
63884         this.cells.each(function(c) {
63885             //Roo.log(c.select('.fc-day-content div',true).first());
63886             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63887         });
63888         
63889         var ctr = this.view.el.select('.fc-event-container',true).first();
63890         
63891         var cls;
63892         this.eventStore.each(function(ev){
63893             
63894             this.renderEvent(ev);
63895              
63896              
63897         }, this);
63898         this.view.layout();
63899         
63900     },
63901     
63902     onEventEnter: function (e, el,event,d) {
63903         this.fireEvent('evententer', this, el, event);
63904     },
63905     
63906     onEventLeave: function (e, el,event,d) {
63907         this.fireEvent('eventleave', this, el, event);
63908     },
63909     
63910     onEventClick: function (e, el,event,d) {
63911         this.fireEvent('eventclick', this, el, event);
63912     },
63913     
63914     onMonthChange: function () {
63915         this.store.load();
63916     },
63917     
63918     onLoad: function () {
63919         
63920         //Roo.log('calendar onload');
63921 //         
63922         if(this.eventStore.getCount() > 0){
63923             
63924            
63925             
63926             this.eventStore.each(function(d){
63927                 
63928                 
63929                 // FIXME..
63930                 var add =   d.data;
63931                 if (typeof(add.end_dt) == 'undefined')  {
63932                     Roo.log("Missing End time in calendar data: ");
63933                     Roo.log(d);
63934                     return;
63935                 }
63936                 if (typeof(add.start_dt) == 'undefined')  {
63937                     Roo.log("Missing Start time in calendar data: ");
63938                     Roo.log(d);
63939                     return;
63940                 }
63941                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63942                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63943                 add.id = add.id || d.id;
63944                 add.title = add.title || '??';
63945                 
63946                 this.addItem(d);
63947                 
63948              
63949             },this);
63950         }
63951         
63952         this.renderEvents();
63953     }
63954     
63955
63956 });
63957 /*
63958  grid : {
63959                 xtype: 'Grid',
63960                 xns: Roo.grid,
63961                 listeners : {
63962                     render : function ()
63963                     {
63964                         _this.grid = this;
63965                         
63966                         if (!this.view.el.hasClass('course-timesheet')) {
63967                             this.view.el.addClass('course-timesheet');
63968                         }
63969                         if (this.tsStyle) {
63970                             this.ds.load({});
63971                             return; 
63972                         }
63973                         Roo.log('width');
63974                         Roo.log(_this.grid.view.el.getWidth());
63975                         
63976                         
63977                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63978                             '.course-timesheet .x-grid-row' : {
63979                                 height: '80px'
63980                             },
63981                             '.x-grid-row td' : {
63982                                 'vertical-align' : 0
63983                             },
63984                             '.course-edit-link' : {
63985                                 'color' : 'blue',
63986                                 'text-overflow' : 'ellipsis',
63987                                 'overflow' : 'hidden',
63988                                 'white-space' : 'nowrap',
63989                                 'cursor' : 'pointer'
63990                             },
63991                             '.sub-link' : {
63992                                 'color' : 'green'
63993                             },
63994                             '.de-act-sup-link' : {
63995                                 'color' : 'purple',
63996                                 'text-decoration' : 'line-through'
63997                             },
63998                             '.de-act-link' : {
63999                                 'color' : 'red',
64000                                 'text-decoration' : 'line-through'
64001                             },
64002                             '.course-timesheet .course-highlight' : {
64003                                 'border-top-style': 'dashed !important',
64004                                 'border-bottom-bottom': 'dashed !important'
64005                             },
64006                             '.course-timesheet .course-item' : {
64007                                 'font-family'   : 'tahoma, arial, helvetica',
64008                                 'font-size'     : '11px',
64009                                 'overflow'      : 'hidden',
64010                                 'padding-left'  : '10px',
64011                                 'padding-right' : '10px',
64012                                 'padding-top' : '10px' 
64013                             }
64014                             
64015                         }, Roo.id());
64016                                 this.ds.load({});
64017                     }
64018                 },
64019                 autoWidth : true,
64020                 monitorWindowResize : false,
64021                 cellrenderer : function(v,x,r)
64022                 {
64023                     return v;
64024                 },
64025                 sm : {
64026                     xtype: 'CellSelectionModel',
64027                     xns: Roo.grid
64028                 },
64029                 dataSource : {
64030                     xtype: 'Store',
64031                     xns: Roo.data,
64032                     listeners : {
64033                         beforeload : function (_self, options)
64034                         {
64035                             options.params = options.params || {};
64036                             options.params._month = _this.monthField.getValue();
64037                             options.params.limit = 9999;
64038                             options.params['sort'] = 'when_dt';    
64039                             options.params['dir'] = 'ASC';    
64040                             this.proxy.loadResponse = this.loadResponse;
64041                             Roo.log("load?");
64042                             //this.addColumns();
64043                         },
64044                         load : function (_self, records, options)
64045                         {
64046                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64047                                 // if you click on the translation.. you can edit it...
64048                                 var el = Roo.get(this);
64049                                 var id = el.dom.getAttribute('data-id');
64050                                 var d = el.dom.getAttribute('data-date');
64051                                 var t = el.dom.getAttribute('data-time');
64052                                 //var id = this.child('span').dom.textContent;
64053                                 
64054                                 //Roo.log(this);
64055                                 Pman.Dialog.CourseCalendar.show({
64056                                     id : id,
64057                                     when_d : d,
64058                                     when_t : t,
64059                                     productitem_active : id ? 1 : 0
64060                                 }, function() {
64061                                     _this.grid.ds.load({});
64062                                 });
64063                            
64064                            });
64065                            
64066                            _this.panel.fireEvent('resize', [ '', '' ]);
64067                         }
64068                     },
64069                     loadResponse : function(o, success, response){
64070                             // this is overridden on before load..
64071                             
64072                             Roo.log("our code?");       
64073                             //Roo.log(success);
64074                             //Roo.log(response)
64075                             delete this.activeRequest;
64076                             if(!success){
64077                                 this.fireEvent("loadexception", this, o, response);
64078                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64079                                 return;
64080                             }
64081                             var result;
64082                             try {
64083                                 result = o.reader.read(response);
64084                             }catch(e){
64085                                 Roo.log("load exception?");
64086                                 this.fireEvent("loadexception", this, o, response, e);
64087                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64088                                 return;
64089                             }
64090                             Roo.log("ready...");        
64091                             // loop through result.records;
64092                             // and set this.tdate[date] = [] << array of records..
64093                             _this.tdata  = {};
64094                             Roo.each(result.records, function(r){
64095                                 //Roo.log(r.data);
64096                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64097                                     _this.tdata[r.data.when_dt.format('j')] = [];
64098                                 }
64099                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64100                             });
64101                             
64102                             //Roo.log(_this.tdata);
64103                             
64104                             result.records = [];
64105                             result.totalRecords = 6;
64106                     
64107                             // let's generate some duumy records for the rows.
64108                             //var st = _this.dateField.getValue();
64109                             
64110                             // work out monday..
64111                             //st = st.add(Date.DAY, -1 * st.format('w'));
64112                             
64113                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64114                             
64115                             var firstOfMonth = date.getFirstDayOfMonth();
64116                             var days = date.getDaysInMonth();
64117                             var d = 1;
64118                             var firstAdded = false;
64119                             for (var i = 0; i < result.totalRecords ; i++) {
64120                                 //var d= st.add(Date.DAY, i);
64121                                 var row = {};
64122                                 var added = 0;
64123                                 for(var w = 0 ; w < 7 ; w++){
64124                                     if(!firstAdded && firstOfMonth != w){
64125                                         continue;
64126                                     }
64127                                     if(d > days){
64128                                         continue;
64129                                     }
64130                                     firstAdded = true;
64131                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64132                                     row['weekday'+w] = String.format(
64133                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64134                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64135                                                     d,
64136                                                     date.format('Y-m-')+dd
64137                                                 );
64138                                     added++;
64139                                     if(typeof(_this.tdata[d]) != 'undefined'){
64140                                         Roo.each(_this.tdata[d], function(r){
64141                                             var is_sub = '';
64142                                             var deactive = '';
64143                                             var id = r.id;
64144                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64145                                             if(r.parent_id*1>0){
64146                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64147                                                 id = r.parent_id;
64148                                             }
64149                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64150                                                 deactive = 'de-act-link';
64151                                             }
64152                                             
64153                                             row['weekday'+w] += String.format(
64154                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64155                                                     id, //0
64156                                                     r.product_id_name, //1
64157                                                     r.when_dt.format('h:ia'), //2
64158                                                     is_sub, //3
64159                                                     deactive, //4
64160                                                     desc // 5
64161                                             );
64162                                         });
64163                                     }
64164                                     d++;
64165                                 }
64166                                 
64167                                 // only do this if something added..
64168                                 if(added > 0){ 
64169                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64170                                 }
64171                                 
64172                                 
64173                                 // push it twice. (second one with an hour..
64174                                 
64175                             }
64176                             //Roo.log(result);
64177                             this.fireEvent("load", this, o, o.request.arg);
64178                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64179                         },
64180                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64181                     proxy : {
64182                         xtype: 'HttpProxy',
64183                         xns: Roo.data,
64184                         method : 'GET',
64185                         url : baseURL + '/Roo/Shop_course.php'
64186                     },
64187                     reader : {
64188                         xtype: 'JsonReader',
64189                         xns: Roo.data,
64190                         id : 'id',
64191                         fields : [
64192                             {
64193                                 'name': 'id',
64194                                 'type': 'int'
64195                             },
64196                             {
64197                                 'name': 'when_dt',
64198                                 'type': 'string'
64199                             },
64200                             {
64201                                 'name': 'end_dt',
64202                                 'type': 'string'
64203                             },
64204                             {
64205                                 'name': 'parent_id',
64206                                 'type': 'int'
64207                             },
64208                             {
64209                                 'name': 'product_id',
64210                                 'type': 'int'
64211                             },
64212                             {
64213                                 'name': 'productitem_id',
64214                                 'type': 'int'
64215                             },
64216                             {
64217                                 'name': 'guid',
64218                                 'type': 'int'
64219                             }
64220                         ]
64221                     }
64222                 },
64223                 toolbar : {
64224                     xtype: 'Toolbar',
64225                     xns: Roo,
64226                     items : [
64227                         {
64228                             xtype: 'Button',
64229                             xns: Roo.Toolbar,
64230                             listeners : {
64231                                 click : function (_self, e)
64232                                 {
64233                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64234                                     sd.setMonth(sd.getMonth()-1);
64235                                     _this.monthField.setValue(sd.format('Y-m-d'));
64236                                     _this.grid.ds.load({});
64237                                 }
64238                             },
64239                             text : "Back"
64240                         },
64241                         {
64242                             xtype: 'Separator',
64243                             xns: Roo.Toolbar
64244                         },
64245                         {
64246                             xtype: 'MonthField',
64247                             xns: Roo.form,
64248                             listeners : {
64249                                 render : function (_self)
64250                                 {
64251                                     _this.monthField = _self;
64252                                    // _this.monthField.set  today
64253                                 },
64254                                 select : function (combo, date)
64255                                 {
64256                                     _this.grid.ds.load({});
64257                                 }
64258                             },
64259                             value : (function() { return new Date(); })()
64260                         },
64261                         {
64262                             xtype: 'Separator',
64263                             xns: Roo.Toolbar
64264                         },
64265                         {
64266                             xtype: 'TextItem',
64267                             xns: Roo.Toolbar,
64268                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64269                         },
64270                         {
64271                             xtype: 'Fill',
64272                             xns: Roo.Toolbar
64273                         },
64274                         {
64275                             xtype: 'Button',
64276                             xns: Roo.Toolbar,
64277                             listeners : {
64278                                 click : function (_self, e)
64279                                 {
64280                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64281                                     sd.setMonth(sd.getMonth()+1);
64282                                     _this.monthField.setValue(sd.format('Y-m-d'));
64283                                     _this.grid.ds.load({});
64284                                 }
64285                             },
64286                             text : "Next"
64287                         }
64288                     ]
64289                 },
64290                  
64291             }
64292         };
64293         
64294         *//*
64295  * Based on:
64296  * Ext JS Library 1.1.1
64297  * Copyright(c) 2006-2007, Ext JS, LLC.
64298  *
64299  * Originally Released Under LGPL - original licence link has changed is not relivant.
64300  *
64301  * Fork - LGPL
64302  * <script type="text/javascript">
64303  */
64304  
64305 /**
64306  * @class Roo.LoadMask
64307  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64308  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64309  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64310  * element's UpdateManager load indicator and will be destroyed after the initial load.
64311  * @constructor
64312  * Create a new LoadMask
64313  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64314  * @param {Object} config The config object
64315  */
64316 Roo.LoadMask = function(el, config){
64317     this.el = Roo.get(el);
64318     Roo.apply(this, config);
64319     if(this.store){
64320         this.store.on('beforeload', this.onBeforeLoad, this);
64321         this.store.on('load', this.onLoad, this);
64322         this.store.on('loadexception', this.onLoadException, this);
64323         this.removeMask = false;
64324     }else{
64325         var um = this.el.getUpdateManager();
64326         um.showLoadIndicator = false; // disable the default indicator
64327         um.on('beforeupdate', this.onBeforeLoad, this);
64328         um.on('update', this.onLoad, this);
64329         um.on('failure', this.onLoad, this);
64330         this.removeMask = true;
64331     }
64332 };
64333
64334 Roo.LoadMask.prototype = {
64335     /**
64336      * @cfg {Boolean} removeMask
64337      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64338      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64339      */
64340     removeMask : false,
64341     /**
64342      * @cfg {String} msg
64343      * The text to display in a centered loading message box (defaults to 'Loading...')
64344      */
64345     msg : 'Loading...',
64346     /**
64347      * @cfg {String} msgCls
64348      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64349      */
64350     msgCls : 'x-mask-loading',
64351
64352     /**
64353      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64354      * @type Boolean
64355      */
64356     disabled: false,
64357
64358     /**
64359      * Disables the mask to prevent it from being displayed
64360      */
64361     disable : function(){
64362        this.disabled = true;
64363     },
64364
64365     /**
64366      * Enables the mask so that it can be displayed
64367      */
64368     enable : function(){
64369         this.disabled = false;
64370     },
64371     
64372     onLoadException : function()
64373     {
64374         Roo.log(arguments);
64375         
64376         if (typeof(arguments[3]) != 'undefined') {
64377             Roo.MessageBox.alert("Error loading",arguments[3]);
64378         } 
64379         /*
64380         try {
64381             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64382                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64383             }   
64384         } catch(e) {
64385             
64386         }
64387         */
64388     
64389         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64390     },
64391     // private
64392     onLoad : function()
64393     {
64394         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64395     },
64396
64397     // private
64398     onBeforeLoad : function(){
64399         if(!this.disabled){
64400             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64401         }
64402     },
64403
64404     // private
64405     destroy : function(){
64406         if(this.store){
64407             this.store.un('beforeload', this.onBeforeLoad, this);
64408             this.store.un('load', this.onLoad, this);
64409             this.store.un('loadexception', this.onLoadException, this);
64410         }else{
64411             var um = this.el.getUpdateManager();
64412             um.un('beforeupdate', this.onBeforeLoad, this);
64413             um.un('update', this.onLoad, this);
64414             um.un('failure', this.onLoad, this);
64415         }
64416     }
64417 };/*
64418  * Based on:
64419  * Ext JS Library 1.1.1
64420  * Copyright(c) 2006-2007, Ext JS, LLC.
64421  *
64422  * Originally Released Under LGPL - original licence link has changed is not relivant.
64423  *
64424  * Fork - LGPL
64425  * <script type="text/javascript">
64426  */
64427
64428
64429 /**
64430  * @class Roo.XTemplate
64431  * @extends Roo.Template
64432  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64433 <pre><code>
64434 var t = new Roo.XTemplate(
64435         '&lt;select name="{name}"&gt;',
64436                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64437         '&lt;/select&gt;'
64438 );
64439  
64440 // then append, applying the master template values
64441  </code></pre>
64442  *
64443  * Supported features:
64444  *
64445  *  Tags:
64446
64447 <pre><code>
64448       {a_variable} - output encoded.
64449       {a_variable.format:("Y-m-d")} - call a method on the variable
64450       {a_variable:raw} - unencoded output
64451       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64452       {a_variable:this.method_on_template(...)} - call a method on the template object.
64453  
64454 </code></pre>
64455  *  The tpl tag:
64456 <pre><code>
64457         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64458         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64459         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64460         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64461   
64462         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64463         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64464 </code></pre>
64465  *      
64466  */
64467 Roo.XTemplate = function()
64468 {
64469     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64470     if (this.html) {
64471         this.compile();
64472     }
64473 };
64474
64475
64476 Roo.extend(Roo.XTemplate, Roo.Template, {
64477
64478     /**
64479      * The various sub templates
64480      */
64481     tpls : false,
64482     /**
64483      *
64484      * basic tag replacing syntax
64485      * WORD:WORD()
64486      *
64487      * // you can fake an object call by doing this
64488      *  x.t:(test,tesT) 
64489      * 
64490      */
64491     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64492
64493     /**
64494      * compile the template
64495      *
64496      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64497      *
64498      */
64499     compile: function()
64500     {
64501         var s = this.html;
64502      
64503         s = ['<tpl>', s, '</tpl>'].join('');
64504     
64505         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64506             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64507             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64508             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64509             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64510             m,
64511             id     = 0,
64512             tpls   = [];
64513     
64514         while(true == !!(m = s.match(re))){
64515             var forMatch   = m[0].match(nameRe),
64516                 ifMatch   = m[0].match(ifRe),
64517                 execMatch   = m[0].match(execRe),
64518                 namedMatch   = m[0].match(namedRe),
64519                 
64520                 exp  = null, 
64521                 fn   = null,
64522                 exec = null,
64523                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64524                 
64525             if (ifMatch) {
64526                 // if - puts fn into test..
64527                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64528                 if(exp){
64529                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64530                 }
64531             }
64532             
64533             if (execMatch) {
64534                 // exec - calls a function... returns empty if true is  returned.
64535                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64536                 if(exp){
64537                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64538                 }
64539             }
64540             
64541             
64542             if (name) {
64543                 // for = 
64544                 switch(name){
64545                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64546                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64547                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64548                 }
64549             }
64550             var uid = namedMatch ? namedMatch[1] : id;
64551             
64552             
64553             tpls.push({
64554                 id:     namedMatch ? namedMatch[1] : id,
64555                 target: name,
64556                 exec:   exec,
64557                 test:   fn,
64558                 body:   m[1] || ''
64559             });
64560             if (namedMatch) {
64561                 s = s.replace(m[0], '');
64562             } else { 
64563                 s = s.replace(m[0], '{xtpl'+ id + '}');
64564             }
64565             ++id;
64566         }
64567         this.tpls = [];
64568         for(var i = tpls.length-1; i >= 0; --i){
64569             this.compileTpl(tpls[i]);
64570             this.tpls[tpls[i].id] = tpls[i];
64571         }
64572         this.master = tpls[tpls.length-1];
64573         return this;
64574     },
64575     /**
64576      * same as applyTemplate, except it's done to one of the subTemplates
64577      * when using named templates, you can do:
64578      *
64579      * var str = pl.applySubTemplate('your-name', values);
64580      *
64581      * 
64582      * @param {Number} id of the template
64583      * @param {Object} values to apply to template
64584      * @param {Object} parent (normaly the instance of this object)
64585      */
64586     applySubTemplate : function(id, values, parent)
64587     {
64588         
64589         
64590         var t = this.tpls[id];
64591         
64592         
64593         try { 
64594             if(t.test && !t.test.call(this, values, parent)){
64595                 return '';
64596             }
64597         } catch(e) {
64598             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64599             Roo.log(e.toString());
64600             Roo.log(t.test);
64601             return ''
64602         }
64603         try { 
64604             
64605             if(t.exec && t.exec.call(this, values, parent)){
64606                 return '';
64607             }
64608         } catch(e) {
64609             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64610             Roo.log(e.toString());
64611             Roo.log(t.exec);
64612             return ''
64613         }
64614         try {
64615             var vs = t.target ? t.target.call(this, values, parent) : values;
64616             parent = t.target ? values : parent;
64617             if(t.target && vs instanceof Array){
64618                 var buf = [];
64619                 for(var i = 0, len = vs.length; i < len; i++){
64620                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64621                 }
64622                 return buf.join('');
64623             }
64624             return t.compiled.call(this, vs, parent);
64625         } catch (e) {
64626             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64627             Roo.log(e.toString());
64628             Roo.log(t.compiled);
64629             return '';
64630         }
64631     },
64632
64633     compileTpl : function(tpl)
64634     {
64635         var fm = Roo.util.Format;
64636         var useF = this.disableFormats !== true;
64637         var sep = Roo.isGecko ? "+" : ",";
64638         var undef = function(str) {
64639             Roo.log("Property not found :"  + str);
64640             return '';
64641         };
64642         
64643         var fn = function(m, name, format, args)
64644         {
64645             //Roo.log(arguments);
64646             args = args ? args.replace(/\\'/g,"'") : args;
64647             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64648             if (typeof(format) == 'undefined') {
64649                 format= 'htmlEncode';
64650             }
64651             if (format == 'raw' ) {
64652                 format = false;
64653             }
64654             
64655             if(name.substr(0, 4) == 'xtpl'){
64656                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64657             }
64658             
64659             // build an array of options to determine if value is undefined..
64660             
64661             // basically get 'xxxx.yyyy' then do
64662             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64663             //    (function () { Roo.log("Property not found"); return ''; })() :
64664             //    ......
64665             
64666             var udef_ar = [];
64667             var lookfor = '';
64668             Roo.each(name.split('.'), function(st) {
64669                 lookfor += (lookfor.length ? '.': '') + st;
64670                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64671             });
64672             
64673             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64674             
64675             
64676             if(format && useF){
64677                 
64678                 args = args ? ',' + args : "";
64679                  
64680                 if(format.substr(0, 5) != "this."){
64681                     format = "fm." + format + '(';
64682                 }else{
64683                     format = 'this.call("'+ format.substr(5) + '", ';
64684                     args = ", values";
64685                 }
64686                 
64687                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64688             }
64689              
64690             if (args.length) {
64691                 // called with xxyx.yuu:(test,test)
64692                 // change to ()
64693                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64694             }
64695             // raw.. - :raw modifier..
64696             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64697             
64698         };
64699         var body;
64700         // branched to use + in gecko and [].join() in others
64701         if(Roo.isGecko){
64702             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64703                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64704                     "';};};";
64705         }else{
64706             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64707             body.push(tpl.body.replace(/(\r\n|\n)/g,
64708                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64709             body.push("'].join('');};};");
64710             body = body.join('');
64711         }
64712         
64713         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64714        
64715         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64716         eval(body);
64717         
64718         return this;
64719     },
64720
64721     applyTemplate : function(values){
64722         return this.master.compiled.call(this, values, {});
64723         //var s = this.subs;
64724     },
64725
64726     apply : function(){
64727         return this.applyTemplate.apply(this, arguments);
64728     }
64729
64730  });
64731
64732 Roo.XTemplate.from = function(el){
64733     el = Roo.getDom(el);
64734     return new Roo.XTemplate(el.value || el.innerHTML);
64735 };